(heimdal.info) Walkthru a sample Kerberos 5 client
Info Catalog
(heimdal.info) Kerberos 5 API Overview
(heimdal.info) Programming with Kerberos
(heimdal.info) Validating a password in a server application
Walkthru a sample Kerberos 5 client
===================================
This example contains parts of a sample TCP Kerberos 5 clients, if you
want a real working client, please look in `appl/test' directory in the
Heimdal distribution.
All Kerberos error-codes that are returned from kerberos functions in
this program are passed to `krb5_err', that will print a descriptive
text of the error code and exit. Graphical programs can convert
error-code to a humal readable error-string with the
`krb5_get_err_text(3)' function.
Note that you should not use any Kerberos function before
`krb5_init_context()' have completed successfully. That is the reson
`err()' is used when `krb5_init_context()' fails.
First the client needs to call `krb5_init_context' to initialize the
Kerberos 5 library. This is only needed once per thread in the program.
If the function returns a non-zero value it indicates that either the
Kerberos implemtation is failing or its disabled on this host.
#include <krb5.h>
int
main(int argc, char **argv)
{
krb5_context context;
if (krb5_context(&context))
errx (1, "krb5_context");
Now the client wants to connect to the host at the other end. The
preferred way of doing this is using `getaddrinfo(3)' (for operating
system that have this function implemented), since getaddrinfo is
neutral to the address type and can use any protocol that is available.
struct addrinfo *ai, *a;
struct addrinfo hints;
int error;
memset (&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
error = getaddrinfo (hostname, "pop3", &hints, &ai);
if (error)
errx (1, "%s: %s", hostname, gai_strerror(error));
for (a = ai; a != NULL; a = a->ai_next) {
int s;
s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
if (s < 0)
continue;
if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
warn ("connect(%s)", hostname);
close (s);
continue;
}
freeaddrinfo (ai);
ai = NULL;
}
if (ai) {
freeaddrinfo (ai);
errx ("failed to contact %s", hostname);
}
Before authenticating, an authentication context needs to be created.
This context keeps all information for one (to be) authenticated
connection (see `krb5_auth_context(3)').
status = krb5_auth_con_init (context, &auth_context);
if (status)
krb5_err (context, 1, status, "krb5_auth_con_init");
For setting the address in the authentication there is a help function
`krb5_auth_con_setaddrs_from_fd' that does everthing that is needed
when given a connected file descriptor to the socket.
status = krb5_auth_con_setaddrs_from_fd (context,
auth_context,
&sock);
if (status)
krb5_err (context, 1, status,
"krb5_auth_con_setaddrs_from_fd");
The next step is to build a server principal for the service we want to
connect to. (See also `krb5_sname_to_principal(3)'.)
status = krb5_sname_to_principal (context,
hostname,
service,
KRB5_NT_SRV_HST,
&server);
if (status)
krb5_err (context, 1, status, "krb5_sname_to_principal");
The client principal is not passed to `krb5_sendauth(3)' function, this
causes the `krb5_sendauth' function to try to figure it out itself.
The server program is using the function `krb5_recvauth(3)' to receive
the Kerberos 5 authenticator.
In this case, mutual authenication will be tried. That means that the
server will authenticate to the client. Using mutual authenication is
good since it enables the user to verify that they are talking to the
right server (a server that knows the key).
If you are using a non-blocking socket you will need to do all work of
`krb5_sendauth' yourself. Basically you need to send over the
authenticator from `krb5_mk_req(3)' and, in case of mutual
authentication, verifying the result from the server with
`krb5_rd_rep(3)'.
status = krb5_sendauth (context,
&auth_context,
&sock,
VERSION,
NULL,
server,
AP_OPTS_MUTUAL_REQUIRED,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL);
if (status)
krb5_err (context, 1, status, "krb5_sendauth");
Once authentication has been performed, it is time to send some data.
First we create a krb5_data structure, then we sign it with
`krb5_mk_safe(3)' using the `auth_context' that contains the
session-key that was exchanged in the
`krb5_sendauth(3)'/`krb5_recvauth(3)' authentication sequence.
data.data = "hej";
data.length = 3;
krb5_data_zero (&packet);
status = krb5_mk_safe (context,
auth_context,
&data,
&packet,
NULL);
if (status)
krb5_err (context, 1, status, "krb5_mk_safe");
And send it over the network.
len = packet.length;
net_len = htonl(len);
if (krb5_net_write (context, &sock, &net_len, 4) != 4)
err (1, "krb5_net_write");
if (krb5_net_write (context, &sock, packet.data, len) != len)
err (1, "krb5_net_write");
To send encrypted (and signed) data `krb5_mk_priv(3)' should be used
instead. `krb5_mk_priv(3)' works the same way as `krb5_mk_safe(3)',
with the exception that it encrypts the data in addition to signing it.
data.data = "hemligt";
data.length = 7;
krb5_data_free (&packet);
status = krb5_mk_priv (context,
auth_context,
&data,
&packet,
NULL);
if (status)
krb5_err (context, 1, status, "krb5_mk_priv");
And send it over the network.
len = packet.length;
net_len = htonl(len);
if (krb5_net_write (context, &sock, &net_len, 4) != 4)
err (1, "krb5_net_write");
if (krb5_net_write (context, &sock, packet.data, len) != len)
err (1, "krb5_net_write");
The server is using `krb5_rd_safe(3)' and `krb5_rd_priv(3)' to verify
the signature and decrypt the packet.
Info Catalog
(heimdal.info) Kerberos 5 API Overview
(heimdal.info) Programming with Kerberos
(heimdal.info) Validating a password in a server application
automatically generated byinfo2html