/* $Id: auth_krb5.c 7462 2005-12-12 01:06:54Z eagle $
**
** Check an username and password against Kerberos v5.
**
** Based on nnrpkrb5auth by Christopher P. Lindsey <lindsey@mallorn.com>
** See <http://www.mallorn.com/tools/nnrpkrb5auth>
**
** This program takes a username and password pair from nnrpd and checks
** checks their validity against a Kerberos v5 KDC by attempting to obtain a
** TGT. With the -i <instance> command line option, appends /<instance> to
** the username prior to authentication.
**
** Special thanks to Von Welch <vwelch@vwelch.com> for giving me the initial
** code on which the Kerberos V authentication is based many years ago, and
** for introducing me to Kerberos back in '96.
**
** Also, thanks to Graeme Mathieson <graeme@mathie.cx> for his inspiration
** through the pamckpasswd program.
*/
#include "config.h"
#include "clibrary.h"
#include "libauth.h"
#ifdef HAVE_ET_COM_ERR_H
# include <et/com_err.h>
#else
# include <com_err.h>
#endif
/* krb5_get_in_tkt_with_password is deprecated. */
#define KRB5_DEPRECATED 1
#include <krb5.h>
#include "inn/messages.h"
#include "libinn.h"
/*
* Default life of the ticket we are getting. Since we are just checking
* to see if the user can get one, it doesn't need a long lifetime.
*/
#define KRB5_DEFAULT_LIFE 60 * 5 /* 5 minutes */
/*
** Check the username and password by attempting to get a TGT. Returns 1 on
** success and 0 on failure. Errors are reported via com_err.
*/
static int
krb5_check_password (char *principal_name, char *password)
{
krb5_context kcontext;
krb5_creds creds;
krb5_principal user_principal;
krb5_data *user_realm;
krb5_principal service_principal;
krb5_timestamp now;
krb5_address **addrs = (krb5_address **) NULL; /* Use default */
long lifetime = KRB5_DEFAULT_LIFE;
int options = 0;
/* TGT service name for convenience */
krb5_data tgtname = { 0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME };
krb5_preauthtype *preauth = NULL;
krb5_error_code code;
/* Our return code - 1 is success */
int result = 0;
/* Initialize our Kerberos state */
code = krb5_init_context (&kcontext);
if (code) {
com_err (message_program_name, code, "initializing krb5 context");
return 0;
}
#ifdef HAVE_KRB5_INIT_ETS
/* Initialize krb5 error tables */
krb5_init_ets (kcontext);
#endif
/* Get current time */
code = krb5_timeofday (kcontext, &now);
if (code) {
com_err (message_program_name, code, "getting time of day");
return 0;
}
/* Set up credentials to be filled in */
memset (&creds, 0, sizeof(creds));
/* From here on, goto cleanup to exit */
/* Parse the username into a krb5 principal */
if (!principal_name) {
com_err (message_program_name, 0, "passed NULL principal name");
goto cleanup;
}
code = krb5_parse_name (kcontext, principal_name, &user_principal);
if (code) {
com_err (message_program_name, code,
"parsing user principal name %.100s", principal_name);
goto cleanup;
}
creds.client = user_principal;
/* Get the user's realm for building service principal */
user_realm = krb5_princ_realm (kcontext, user_principal);
/*
* Build the service name into a principal. Right now this is
* a TGT for the user's realm.
*/
code = krb5_build_principal_ext (kcontext,
&service_principal,
user_realm->length,
user_realm->data,
tgtname.length,
tgtname.data,
user_realm->length,
user_realm->data,
0 /* terminator */);
if (code) {
com_err(message_program_name, code, "building service principal name");
goto cleanup;
}
creds.server = service_principal;
creds.times.starttime = 0; /* Now */
creds.times.endtime = now + lifetime;
creds.times.renew_till = 0; /* Unrenewable */
/* DO IT */
code = krb5_get_in_tkt_with_password (kcontext,
options,
addrs,
NULL,
preauth,
password,
0,
&creds,
0);
/* We are done with password at this point... */
if (code) {
/* FAILURE - Parse a few common errors here */
switch (code) {
case KRB5KRB_AP_ERR_BAD_INTEGRITY:
com_err (message_program_name, 0, "bad password for %.100s",
principal_name);
break;
case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
com_err (message_program_name, 0, "unknown user \"%.100s\"",
principal_name);
break;
default:
com_err (message_program_name, code,
"checking Kerberos password for %.100s", principal_name);
}
result = 0;
} else {
/* SUCCESS */
result = 1;
}
/* Cleanup */
cleanup:
krb5_free_cred_contents (kcontext, &creds);
return result;
}
int
main (int argc, char *argv[])
{
struct auth_info *authinfo;
char *new_user;
message_program_name = "auth_krb5";
/* Retrieve the username and passwd from nnrpd. */
authinfo = get_auth_info(stdin);
/* Must have a username/password, and no '@' in the address. @ checking
is there to prevent authentication against another Kerberos realm; there
should be a -r <realm> commandline option to make this check unnecessary
in the future. */
if (authinfo == NULL)
die("no authentication information from nnrpd");
if (authinfo->username[0] == '\0')
die("null username");
if (strchr(authinfo->username, '@') != NULL)
die("username contains @, not allowed");
/* May need to prepend instance name if -i option was given. */
if (argc > 1) {
if (argc == 3 && strcmp(argv[1], "-i") == 0) {
new_user = concat(authinfo->username, "/", argv[2], (char *) 0);
free(authinfo->username);
authinfo->username = new_user;
} else {
die("error parsing command-line options");
}
}
if (krb5_check_password(authinfo->username, authinfo->password)) {
printf("User:%s\r\n", authinfo->username);
exit(0);
} else {
die("failure validating password");
}
}
syntax highlighted by Code2HTML, v. 0.9.1