1@c $Id: programming.texi,v 1.2.8.1 2003/04/24 11:55:45 lha Exp $ 2 3@node Programming with Kerberos 4@chapter Programming with Kerberos 5 6First you need to know how the Kerberos model works, go read the 7introduction text (@pxref{What is Kerberos?}). 8 9@macro manpage{man, section} 10@cite{\man\(\section\)} 11@end macro 12 13@menu 14* Kerberos 5 API Overview:: 15* Walkthru a sample Kerberos 5 client:: 16* Validating a password in a server application:: 17@end menu 18 19@node Kerberos 5 API Overview, Walkthru a sample Kerberos 5 client, Programming with Kerberos, Programming with Kerberos 20@section Kerberos 5 API Overview 21 22Most functions are documenteded in manual pages. This overview only 23tries to point to where to look for a specific function. 24 25@subsection Kerberos context 26 27A kerberos context (@code{krb5_context}) holds all per thread state. All global variables that 28are context specific are stored in this struture, including default 29encryption types, credential-cache (ticket file), and default realms. 30 31See the manual pages for @manpage{krb5_context,3} and 32@manpage{krb5_init_context,3}. 33 34@subsection Kerberos authenication context 35 36Kerberos authentication context (@code{krb5_auth_context}) holds all 37context related to an authenticated connection, in a similar way to the 38kerberos context that holds the context for the thread or process. 39 40The @code{krb5_auth_context} is used by various functions that are 41directly related to authentication between the server/client. Example of 42data that this structure contains are various flags, addresses of client 43and server, port numbers, keyblocks (and subkeys), sequence numbers, 44replay cache, and checksum types. 45 46See the manual page for @manpage{krb5_auth_context,3}. 47 48@subsection Keytab management 49 50A keytab is a storage for locally stored keys. Heimdal includes keytab 51support for Kerberos 5 keytabs, Kerberos 4 srvtab, AFS-KeyFile's, 52and for storing keys in memory. 53 54See also manual page for @manpage{krb5_keytab,3} 55 56@node Walkthru a sample Kerberos 5 client, Validating a password in a server application, Kerberos 5 API Overview, Programming with Kerberos 57@section Walkthru a sample Kerberos 5 client 58 59This example contains parts of a sample TCP Kerberos 5 clients, if you 60want a real working client, please look in @file{appl/test} directory in 61the Heimdal distribution. 62 63All Kerberos error-codes that are returned from kerberos functions in 64this program are passed to @code{krb5_err}, that will print a 65descriptive text of the error code and exit. Graphical programs can 66convert error-code to a humal readable error-string with the 67@manpage{krb5_get_err_text,3} function. 68 69Note that you should not use any Kerberos function before 70@code{krb5_init_context()} have completed successfully. That is the 71reson @code{err()} is used when @code{krb5_init_context()} fails. 72 73First the client needs to call @code{krb5_init_context} to initialize 74the Kerberos 5 library. This is only needed once per thread 75in the program. If the function returns a non-zero value it indicates 76that either the Kerberos implemtation is failing or its disabled on 77this host. 78 79@example 80#include <krb5.h> 81 82int 83main(int argc, char **argv) 84@{ 85 krb5_context context; 86 87 if (krb5_context(&context)) 88 errx (1, "krb5_context"); 89@end example 90 91Now the client wants to connect to the host at the other end. The 92preferred way of doing this is using @manpage{getaddrinfo,3} (for 93operating system that have this function implemented), since getaddrinfo 94is neutral to the address type and can use any protocol that is available. 95 96@example 97 struct addrinfo *ai, *a; 98 struct addrinfo hints; 99 int error; 100 101 memset (&hints, 0, sizeof(hints)); 102 hints.ai_socktype = SOCK_STREAM; 103 hints.ai_protocol = IPPROTO_TCP; 104 105 error = getaddrinfo (hostname, "pop3", &hints, &ai); 106 if (error) 107 errx (1, "%s: %s", hostname, gai_strerror(error)); 108 109 for (a = ai; a != NULL; a = a->ai_next) @{ 110 int s; 111 112 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 113 if (s < 0) 114 continue; 115 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) @{ 116 warn ("connect(%s)", hostname); 117 close (s); 118 continue; 119 @} 120 freeaddrinfo (ai); 121 ai = NULL; 122 @} 123 if (ai) @{ 124 freeaddrinfo (ai); 125 errx ("failed to contact %s", hostname); 126 @} 127@end example 128 129Before authenticating, an authentication context needs to be 130created. This context keeps all information for one (to be) authenticated 131connection (see @manpage{krb5_auth_context,3}). 132 133@example 134 status = krb5_auth_con_init (context, &auth_context); 135 if (status) 136 krb5_err (context, 1, status, "krb5_auth_con_init"); 137@end example 138 139For setting the address in the authentication there is a help function 140@code{krb5_auth_con_setaddrs_from_fd} that does everthing that is needed 141when given a connected file descriptor to the socket. 142 143@example 144 status = krb5_auth_con_setaddrs_from_fd (context, 145 auth_context, 146 &sock); 147 if (status) 148 krb5_err (context, 1, status, 149 "krb5_auth_con_setaddrs_from_fd"); 150@end example 151 152The next step is to build a server principal for the service we want 153to connect to. (See also @manpage{krb5_sname_to_principal,3}.) 154 155@example 156 status = krb5_sname_to_principal (context, 157 hostname, 158 service, 159 KRB5_NT_SRV_HST, 160 &server); 161 if (status) 162 krb5_err (context, 1, status, "krb5_sname_to_principal"); 163@end example 164 165The client principal is not passed to @manpage{krb5_sendauth,3} 166function, this causes the @code{krb5_sendauth} function to try to figure it 167out itself. 168 169The server program is using the function @manpage{krb5_recvauth,3} to 170receive the Kerberos 5 authenticator. 171 172In this case, mutual authenication will be tried. That means that the server 173will authenticate to the client. Using mutual authenication 174is good since it enables the user to verify that they are talking to the 175right server (a server that knows the key). 176 177If you are using a non-blocking socket you will need to do all work of 178@code{krb5_sendauth} yourself. Basically you need to send over the 179authenticator from @manpage{krb5_mk_req,3} and, in case of mutual 180authentication, verifying the result from the server with 181@manpage{krb5_rd_rep,3}. 182 183@example 184 status = krb5_sendauth (context, 185 &auth_context, 186 &sock, 187 VERSION, 188 NULL, 189 server, 190 AP_OPTS_MUTUAL_REQUIRED, 191 NULL, 192 NULL, 193 NULL, 194 NULL, 195 NULL, 196 NULL); 197 if (status) 198 krb5_err (context, 1, status, "krb5_sendauth"); 199@end example 200 201Once authentication has been performed, it is time to send some 202data. First we create a krb5_data structure, then we sign it with 203@manpage{krb5_mk_safe,3} using the @code{auth_context} that contains the 204session-key that was exchanged in the 205@manpage{krb5_sendauth,3}/@manpage{krb5_recvauth,3} authentication 206sequence. 207 208@example 209 data.data = "hej"; 210 data.length = 3; 211 212 krb5_data_zero (&packet); 213 214 status = krb5_mk_safe (context, 215 auth_context, 216 &data, 217 &packet, 218 NULL); 219 if (status) 220 krb5_err (context, 1, status, "krb5_mk_safe"); 221@end example 222 223And send it over the network. 224 225@example 226 len = packet.length; 227 net_len = htonl(len); 228 229 if (krb5_net_write (context, &sock, &net_len, 4) != 4) 230 err (1, "krb5_net_write"); 231 if (krb5_net_write (context, &sock, packet.data, len) != len) 232 err (1, "krb5_net_write"); 233@end example 234 235To send encrypted (and signed) data @manpage{krb5_mk_priv,3} should be 236used instead. @manpage{krb5_mk_priv,3} works the same way as 237@manpage{krb5_mk_safe,3}, with the exception that it encrypts the data 238in addition to signing it. 239 240@example 241 data.data = "hemligt"; 242 data.length = 7; 243 244 krb5_data_free (&packet); 245 246 status = krb5_mk_priv (context, 247 auth_context, 248 &data, 249 &packet, 250 NULL); 251 if (status) 252 krb5_err (context, 1, status, "krb5_mk_priv"); 253@end example 254 255And send it over the network. 256 257@example 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@end example 267 268The server is using @manpage{krb5_rd_safe,3} and 269@manpage{krb5_rd_priv,3} to verify the signature and decrypt the packet. 270 271@node Validating a password in a server application, , Walkthru a sample Kerberos 5 client, Programming with Kerberos 272@section Validating a password in an application 273 274See the manual page for @manpage{krb5_verify_user,3}. 275 276@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 277@c @section Why you should use GSS-API for new applications 278@c 279@c SSPI, bah, bah, microsoft, bah, bah, almost GSS-API. 280@c 281@c It would also be possible for other mechanisms then Kerberos, but that 282@c doesn't exist any other GSS-API implementations today. 283@c 284@c @node Walkthru a sample GSS-API client, , Why you should use GSS-API for new applications, Programming with Kerberos 285@c @section Walkthru a sample GSS-API client 286@c 287@c Write about how gssapi_clent.c works. 288