1krb5_introduction(3) Heimdal Kerberos 5 library krb5_introduction(3)
2
3
4
6 krb5_introduction - Introduction to the Kerberos 5 API
7
8
10 All functions are documented in manual pages. This section tries to
11 give an overview of the major components used in Kerberos library, and
12 point to where to look for a specific function.
13
14 Kerberos context
15 A kerberos context (krb5_context) holds all per thread state. All
16 global variables that are context specific are stored in this
17 structure, including default encryption types, credential cache (for
18 example, a ticket file), and default realms.
19
20 The internals of the structure should never be accessed directly,
21 functions exist for extracting information.
22
23 See the manual page for krb5_init_context() how to create a context and
24 module Heimdal Kerberos 5 library for more information about the
25 functions.
26
27 Kerberos authentication context
28 Kerberos authentication context (krb5_auth_context) holds all context
29 related to an authenticated connection, in a similar way to the
30 kerberos context that holds the context for the thread or process.
31
32 The krb5_auth_context is used by various functions that are directly
33 related to authentication between the server/client. Example of data
34 that this structure contains are various flags, addresses of client and
35 server, port numbers, keyblocks (and subkeys), sequence numbers, replay
36 cache, and checksum types.
37
38 Kerberos principal
39 The Kerberos principal is the structure that identifies a user or
40 service in Kerberos. The structure that holds the principal is the
41 krb5_principal. There are function to extract the realm and elements of
42 the principal, but most applications have no reason to inspect the
43 content of the structure.
44
45 The are several ways to create a principal (with different degree of
46 portability), and one way to free it.
47
48 See also the page The principal handing functions. for more information
49 and also module Heimdal Kerberos 5 principal functions.
50
51 Credential cache
52 A credential cache holds the tickets for a user. A given user can have
53 several credential caches, one for each realm where the user have the
54 initial tickets (the first krbtgt).
55
56 The credential cache data can be stored internally in different way,
57 each of them for different proposes. File credential (FILE) caches and
58 processes based (KCM) caches are for permanent storage. While memory
59 caches (MEMORY) are local caches to the local process.
60
61 Caches are opened with krb5_cc_resolve() or created with
62 krb5_cc_new_unique().
63
64 If the cache needs to be opened again (using krb5_cc_resolve())
65 krb5_cc_close() will close the handle, but not the remove the cache.
66 krb5_cc_destroy() will zero out the cache, remove the cache so it can
67 no longer be referenced.
68
69 See also The credential cache functions and Heimdal Kerberos 5
70 credential cache functions .
71
72 Kerberos errors
73 Kerberos errors are based on the com_err library. All error codes are
74 32-bit signed numbers, the first 24 bits define what subsystem the
75 error originates from, and last 8 bits are 255 error codes within the
76 library. Each error code have fixed string associated with it. For
77 example, the error-code -1765328383 have the symbolic name
78 KRB5KDC_ERR_NAME_EXP, and associated error string ``Client's entry in
79 database has expired''.
80
81 This is a great improvement compared to just getting one of the unix
82 error-codes back. However, Heimdal have an extention to pass back
83 customised errors messages. Instead of getting Key table entry not
84 found'', the user might backfailed to find
85 host/host.example.com@EXAMLE.COM(kvno 3) in keytab /etc/krb5.keytab
86 (des-cbc-crc)''. This improves the chance that the user find the cause
87 of the error so you should use the customised error message whenever
88 it's available.
89
90 See also module Heimdal Kerberos 5 error reporting functions .
91
92 Keytab management
93 A keytab is a storage for locally stored keys. Heimdal includes keytab
94 support for Kerberos 5 keytabs, Kerberos 4 srvtab, AFS-KeyFile's, and
95 for storing keys in memory.
96
97 Keytabs are used for servers and long-running services.
98
99 See also The keytab handing functions and Heimdal Kerberos 5 keytab
100 handling functions .
101
102 Kerberos crypto
103 Heimdal includes a implementation of the Kerberos crypto framework, all
104 crypto operations. To create a crypto context call krb5_crypto_init().
105
106 See also module Heimdal Kerberos 5 cryptography functions .
107
109 This example contains parts of a sample TCP Kerberos 5 clients, if you
110 want a real working client, please look in appl/test directory in the
111 Heimdal distribution.
112
113 All Kerberos error-codes that are returned from kerberos functions in
114 this program are passed to krb5_err, that will print a descriptive text
115 of the error code and exit. Graphical programs can convert error-code
116 to a human readable error-string with the krb5_get_error_message()
117 function.
118
119 Note that you should not use any Kerberos function before
120 krb5_init_context() have completed successfully. That is the reason
121 err() is used when krb5_init_context() fails.
122
123 First the client needs to call krb5_init_context to initialise the
124 Kerberos 5 library. This is only needed once per thread in the program.
125 If the function returns a non-zero value it indicates that either the
126 Kerberos implementation is failing or it's disabled on this host.
127
128 #include <krb5.h>
129
130 int
131 main(int argc, char **argv)
132 {
133 krb5_context context;
134
135 if (krb5_init_context(&context))
136 errx (1, "krb5_context");
137
138 Now the client wants to connect to the host at the other end. The
139 preferred way of doing this is using getaddrinfo (for operating system
140 that have this function implemented), since getaddrinfo is neutral to
141 the address type and can use any protocol that is available.
142
143 struct addrinfo *ai, *a;
144 struct addrinfo hints;
145 int error;
146
147 memset (&hints, 0, sizeof(hints));
148 hints.ai_socktype = SOCK_STREAM;
149 hints.ai_protocol = IPPROTO_TCP;
150
151 error = getaddrinfo (hostname, "pop3", &hints, &ai);
152 if (error)
153 errx (1, "%s: %s", hostname, gai_strerror(error));
154
155 for (a = ai; a != NULL; a = a->ai_next) {
156 int s;
157
158 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
159 if (s < 0)
160 continue;
161 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
162 warn ("connect(%s)", hostname);
163 close (s);
164 continue;
165 }
166 freeaddrinfo (ai);
167 ai = NULL;
168 }
169 if (ai) {
170 freeaddrinfo (ai);
171 errx ("failed to contact %s", hostname);
172 }
173
174 Before authenticating, an authentication context needs to be created.
175 This context keeps all information for one (to be) authenticated
176 connection (see krb5_auth_context).
177
178 status = krb5_auth_con_init (context, &auth_context);
179 if (status)
180 krb5_err (context, 1, status, "krb5_auth_con_init");
181
182 For setting the address in the authentication there is a help function
183 krb5_auth_con_setaddrs_from_fd() that does everything that is needed
184 when given a connected file descriptor to the socket.
185
186 status = krb5_auth_con_setaddrs_from_fd (context,
187 auth_context,
188 &sock);
189 if (status)
190 krb5_err (context, 1, status,
191 "krb5_auth_con_setaddrs_from_fd");
192
193 The next step is to build a server principal for the service we want to
194 connect to. (See also krb5_sname_to_principal().)
195
196 status = krb5_sname_to_principal (context,
197 hostname,
198 service,
199 KRB5_NT_SRV_HST,
200 &server);
201 if (status)
202 krb5_err (context, 1, status, "krb5_sname_to_principal");
203
204 The client principal is not passed to krb5_sendauth() function, this
205 causes the krb5_sendauth() function to try to figure it out itself.
206
207 The server program is using the function krb5_recvauth() to receive the
208 Kerberos 5 authenticator.
209
210 In this case, mutual authentication will be tried. That means that the
211 server will authenticate to the client. Using mutual authentication is
212 required to avoid man-in-the-middle attacks, since it enables the user
213 to verify that they are talking to the right server (a server that
214 knows the key).
215
216 If you are using a non-blocking socket you will need to do all work of
217 krb5_sendauth() yourself. Basically you need to send over the
218 authenticator from krb5_mk_req() and, in case of mutual authentication,
219 verifying the result from the server with krb5_rd_rep().
220
221 status = krb5_sendauth (context,
222 &auth_context,
223 &sock,
224 VERSION,
225 NULL,
226 server,
227 AP_OPTS_MUTUAL_REQUIRED,
228 NULL,
229 NULL,
230 NULL,
231 NULL,
232 NULL,
233 NULL);
234 if (status)
235 krb5_err (context, 1, status, "krb5_sendauth");
236
237 Once authentication has been performed, it is time to send some data.
238 First we create a krb5_data structure, then we sign it with
239 krb5_mk_safe() using the auth_context that contains the session-key
240 that was exchanged in the krb5_sendauth()/krb5_recvauth()
241 authentication sequence.
242
243 data.data = "hej";
244 data.length = 3;
245
246 krb5_data_zero (&packet);
247
248 status = krb5_mk_safe (context,
249 auth_context,
250 &data,
251 &packet,
252 NULL);
253 if (status)
254 krb5_err (context, 1, status, "krb5_mk_safe");
255
256 And send it over the network.
257
258 len = packet.length;
259 net_len = htonl(len);
260
261 if (krb5_net_write (context, &sock, &net_len, 4) != 4)
262 err (1, "krb5_net_write");
263 if (krb5_net_write (context, &sock, packet.data, len) != len)
264 err (1, "krb5_net_write");
265
266 To send encrypted (and signed) data krb5_mk_priv() should be used
267 instead. krb5_mk_priv() works the same way as krb5_mk_safe(), with the
268 exception that it encrypts the data in addition to signing it.
269
270 data.data = "hemligt";
271 data.length = 7;
272
273 krb5_data_free (&packet);
274
275 status = krb5_mk_priv (context,
276 auth_context,
277 &data,
278 &packet,
279 NULL);
280 if (status)
281 krb5_err (context, 1, status, "krb5_mk_priv");
282
283 And send it over the network.
284
285 len = packet.length;
286 net_len = htonl(len);
287
288 if (krb5_net_write (context, &sock, &net_len, 4) != 4)
289 err (1, "krb5_net_write");
290 if (krb5_net_write (context, &sock, packet.data, len) != len)
291 err (1, "krb5_net_write");
292
293 The server is using krb5_rd_safe() and krb5_rd_priv() to verify the
294 signature and decrypt the packet.
295
297 See the manual page for krb5_verify_user().
298
300 This section is somewhat disorganised, but so far there is no overall
301 structure to the differences, though some of the have their root in
302 that Heimdal uses an ASN.1 compiler and MIT doesn't.
303
304 Principal and realms
305 Heimdal stores the realm as a krb5_realm, that is a char *. MIT
306 Kerberos uses a krb5_data to store a realm.
307
308 In Heimdal krb5_principal doesn't contain the component name_type; it's
309 instead stored in component name.name_type. To get and set the nametype
310 in Heimdal, use krb5_principal_get_type() and
311 krb5_principal_set_type().
312
313 For more information about principal and realms, see krb5_principal.
314
315 Error messages
316 To get the error string, Heimdal uses krb5_get_error_message(). This is
317 to return custom error messages (like Can't find
318 host/datan.example.com\@CODE.COM in /etc/krb5.conf.'' instead of aKey
319 table entry not found'' that error_message returns.
320
321 Heimdal uses a threadsafe(r) version of the com_err interface; the
322 global com_err table isn't initialised. Then error_message returns
323 quite a boring error string (just the error code itself).
324
325
326
327Version 7.7.1 Tue Nov 15 2022 krb5_introduction(3)