If ticket is expired, reacquire credentials instead of renewing
[heimdal.git] / doc / programming.texi
blob9572f00e40bbe06c6f13a9d7a97349809ecee56c
1 @c $Id$
3 @node Programming with Kerberos
4 @chapter Programming with Kerberos
6 First you need to know how the Kerberos model works, go read the
7 introduction text (@pxref{What is Kerberos?}).
9 @menu
10 * Kerberos 5 API Overview::     
11 * Walkthru a sample Kerberos 5 client::  
12 * Validating a password in a server application::  
13 * API diffrences to MIT Kerberos::
14 @end menu
16 @node Kerberos 5 API Overview, Walkthru a sample Kerberos 5 client, Programming with Kerberos, Programming with Kerberos
17 @section Kerberos 5 API Overview
19 Most functions are documenteded in manual pages.  This overview only
20 tries to point to where to look for a specific function.
22 @subsection Kerberos context
24 A kerberos context (@code{krb5_context}) holds all per thread state. All global variables that
25 are context specific are stored in this struture, including default
26 encryption types, credential-cache (ticket file), and default realms.
28 See the manual pages for @manpage{krb5_context,3} and
29 @manpage{krb5_init_context,3}.
31 @subsection Kerberos authenication context
33 Kerberos authentication context (@code{krb5_auth_context}) holds all
34 context related to an authenticated connection, in a similar way to the
35 kerberos context that holds the context for the thread or process.
37 The @code{krb5_auth_context} is used by various functions that are
38 directly related to authentication between the server/client. Example of
39 data that this structure contains are various flags, addresses of client
40 and server, port numbers, keyblocks (and subkeys), sequence numbers,
41 replay cache, and checksum types.
43 See the manual page for @manpage{krb5_auth_context,3}.
45 @subsection Keytab management
47 A keytab is a storage for locally stored keys. Heimdal includes keytab
48 support for Kerberos 5 keytabs, Kerberos 4 srvtab, AFS-KeyFile's,
49 and for storing keys in memory.
51 See also manual page for @manpage{krb5_keytab,3}
53 @subsection Kerberos principal
55 See also manual page for @manpage{krb5_principal,3}
57 @subsection Kerberos crypto
59 See also manual page for @manpage{krb5_crypto_init,3},
60 @manpage{krb5_keyblock,3}, @manpage{krb5_create_checksum,3}, 
61 and @manpage{krb5_encrypt,3}.
63 @node Walkthru a sample Kerberos 5 client, Validating a password in a server application, Kerberos 5 API Overview, Programming with Kerberos
64 @section Walkthru a sample Kerberos 5 client
66 This example contains parts of a sample TCP Kerberos 5 clients, if you
67 want a real working client, please look in @file{appl/test} directory in
68 the Heimdal distribution.
70 All Kerberos error-codes that are returned from kerberos functions in
71 this program are passed to @code{krb5_err}, that will print a
72 descriptive text of the error code and exit. Graphical programs can
73 convert error-code to a humal readable error-string with the
74 @manpage{krb5_get_err_text,3} function.
76 Note that you should not use any Kerberos function before
77 @code{krb5_init_context()} have completed successfully. That is the
78 reson @code{err()} is used when @code{krb5_init_context()} fails.
80 First the client needs to call @code{krb5_init_context} to initialise
81 the Kerberos 5 library. This is only needed once per thread
82 in the program. If the function returns a non-zero value it indicates
83 that either the Kerberos implemtation is failing or its disabled on
84 this host.
86 @example
87 #include <krb5.h>
89 int
90 main(int argc, char **argv)
92         krb5_context context;
94         if (krb5_context(&context))
95                 errx (1, "krb5_context");
96 @end example
98 Now the client wants to connect to the host at the other end. The
99 preferred way of doing this is using @manpage{getaddrinfo,3} (for
100 operating system that have this function implemented), since getaddrinfo
101 is neutral to the address type and can use any protocol that is available.
103 @example
104         struct addrinfo *ai, *a;
105         struct addrinfo hints;
106         int error;
108         memset (&hints, 0, sizeof(hints));
109         hints.ai_socktype = SOCK_STREAM;
110         hints.ai_protocol = IPPROTO_TCP;
112         error = getaddrinfo (hostname, "pop3", &hints, &ai);
113         if (error)
114                 errx (1, "%s: %s", hostname, gai_strerror(error));
116         for (a = ai; a != NULL; a = a->ai_next) @{
117                 int s;
119                 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
120                 if (s < 0)
121                         continue;
122                 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) @{
123                         warn ("connect(%s)", hostname);
124                             close (s);
125                             continue;
126                 @}
127                 freeaddrinfo (ai);
128                 ai = NULL;
129         @}
130         if (ai) @{
131                     freeaddrinfo (ai);
132                     errx ("failed to contact %s", hostname);
133         @}
134 @end example
136 Before authenticating, an authentication context needs to be
137 created. This context keeps all information for one (to be) authenticated
138 connection (see @manpage{krb5_auth_context,3}).
140 @example
141         status = krb5_auth_con_init (context, &auth_context);
142         if (status)
143                 krb5_err (context, 1, status, "krb5_auth_con_init");
144 @end example
146 For setting the address in the authentication there is a help function
147 @code{krb5_auth_con_setaddrs_from_fd} that does everthing that is needed
148 when given a connected file descriptor to the socket.
150 @example
151         status = krb5_auth_con_setaddrs_from_fd (context,
152                                                  auth_context,
153                                                  &sock);
154         if (status)
155                 krb5_err (context, 1, status, 
156                           "krb5_auth_con_setaddrs_from_fd");
157 @end example
159 The next step is to build a server principal for the service we want
160 to connect to. (See also @manpage{krb5_sname_to_principal,3}.)
162 @example
163         status = krb5_sname_to_principal (context,
164                                           hostname,
165                                           service,
166                                           KRB5_NT_SRV_HST,
167                                           &server);
168         if (status)
169                 krb5_err (context, 1, status, "krb5_sname_to_principal");
170 @end example
172 The client principal is not passed to @manpage{krb5_sendauth,3}
173 function, this causes the @code{krb5_sendauth} function to try to figure it
174 out itself.
176 The server program is using the function @manpage{krb5_recvauth,3} to
177 receive the Kerberos 5 authenticator.
179 In this case, mutual authenication will be tried. That means that the server
180 will authenticate to the client. Using mutual authenication
181 is good since it enables the user to verify that they are talking to the
182 right server (a server that knows the key).
184 If you are using a non-blocking socket you will need to do all work of
185 @code{krb5_sendauth} yourself. Basically you need to send over the
186 authenticator from @manpage{krb5_mk_req,3} and, in case of mutual
187 authentication, verifying the result from the server with
188 @manpage{krb5_rd_rep,3}.
190 @example
191         status = krb5_sendauth (context,
192                                 &auth_context,
193                                 &sock,
194                                 VERSION,
195                                 NULL,
196                                 server,
197                                 AP_OPTS_MUTUAL_REQUIRED,
198                                 NULL,
199                                 NULL,
200                                 NULL,
201                                 NULL,
202                                 NULL,
203                                 NULL);
204         if (status)
205                 krb5_err (context, 1, status, "krb5_sendauth");
206 @end example
208 Once authentication has been performed, it is time to send some
209 data. First we create a krb5_data structure, then we sign it with
210 @manpage{krb5_mk_safe,3} using the @code{auth_context} that contains the
211 session-key that was exchanged in the
212 @manpage{krb5_sendauth,3}/@manpage{krb5_recvauth,3} authentication
213 sequence.
215 @example
216         data.data   = "hej";
217         data.length = 3;
219         krb5_data_zero (&packet);
221         status = krb5_mk_safe (context,
222                                auth_context,
223                                &data,
224                                &packet,
225                                NULL);
226         if (status)
227                 krb5_err (context, 1, status, "krb5_mk_safe");
228 @end example
230 And send it over the network.
232 @example
233         len = packet.length;
234         net_len = htonl(len);
236         if (krb5_net_write (context, &sock, &net_len, 4) != 4)
237                 err (1, "krb5_net_write");
238         if (krb5_net_write (context, &sock, packet.data, len) != len)
239                 err (1, "krb5_net_write");
240 @end example
242 To send encrypted (and signed) data @manpage{krb5_mk_priv,3} should be
243 used instead. @manpage{krb5_mk_priv,3} works the same way as
244 @manpage{krb5_mk_safe,3}, with the exception that it encrypts the data
245 in addition to signing it.
247 @example
248         data.data   = "hemligt";
249         data.length = 7;
251         krb5_data_free (&packet);
253         status = krb5_mk_priv (context,
254                                auth_context,
255                                &data,
256                                &packet,
257                                NULL);
258         if (status)
259                 krb5_err (context, 1, status, "krb5_mk_priv");
260 @end example
262 And send it over the network.
264 @example
265         len = packet.length;
266         net_len = htonl(len);
268         if (krb5_net_write (context, &sock, &net_len, 4) != 4)
269                 err (1, "krb5_net_write");
270         if (krb5_net_write (context, &sock, packet.data, len) != len)
271                 err (1, "krb5_net_write");
273 @end example
275 The server is using @manpage{krb5_rd_safe,3} and
276 @manpage{krb5_rd_priv,3} to verify the signature and decrypt the packet.
278 @node Validating a password in a server application, API diffrences to MIT Kerberos, Walkthru a sample Kerberos 5 client, Programming with Kerberos
279 @section Validating a password in an application
281 See the manual page for @manpage{krb5_verify_user,3}.
283 @node API diffrences to MIT Kerberos, , Validating a password in a server application, Programming with Kerberos
284 @section API diffrences to MIT Kerberos
286 This section is somewhat unorganised, but so far there is no overall
287 structure to the diffrecies, though some of the have their root in
288 that heimdal uses a ASN.1 compiler and MIT doesn't.
290 @subsection Principal and realms
292 Heimdal store the realm as a @code{krb5_realm} that is a @code{char *}.
293 MIT Kerberos uses a @code{krb5_data} to store a realm. See
295 In Heimdal @code{krb5_principal} doesn't contain the component
296 @code{name_type}, its instead stored in component
297 @code{name.name_type}. To get and set the nametype in Heimdal, use
298 @manpage{krb5_principal_get_type,3} and
299 @manpage{krb5_principal_set_type,3}.
301 For more information about principal and realms, see
302 @manpage{krb5_principal,3}.
304 @subsection Error messages
306 To get the error string, Heimdal users uses
307 @manpage{krb5_get_error_string,3} or if @code{NULL} is returned,
308 @manpage{krb5_get_err_text,3}. This is to return custom error messages
309 (like ''Can't find host/datan.example.com@@EXAMPLE.COM in
310 /etc/krb5.conf.'' instead of a ``Key table entry not found'' that
311 @manpage{error_message,3} returns.
313 Heimdal uses a threadsafe(er) version of the com_err interface, the
314 global com_err table isn't initialised, then @manpage{error_message,3}
315 returns quite boring error string (just the error code itself).
318 @c @node Why you should use GSS-API for new applications, Walkthru a sample GSS-API client, Validating a password in a server application, Programming with Kerberos
319 @c @section Why you should use GSS-API for new applications
320 @c 
321 @c SSPI, bah, bah, microsoft, bah, bah, almost GSS-API.
322 @c 
323 @c It would also be possible for other mechanisms then Kerberos, but that
324 @c doesn't exist any other GSS-API implementations today.
325 @c 
326 @c @node Walkthru a sample GSS-API client, , Why you should use GSS-API for new applications, Programming with Kerberos
327 @c @section Walkthru a sample GSS-API client
328 @c 
329 @c Write about how gssapi_clent.c works.