[6450] in Kerberos

home help back first fref pref prev next nref lref last post

Code for moving to kerberos

daemon@ATHENA.MIT.EDU (Emil Sit)
Sat Jan 6 21:42:22 1996

To: kerberos@MIT.EDU
Date: 7 Jan 1996 02:35:51 GMT
From: sit@ (Emil Sit)

In article <199512240320.WAA13253@toxicwaste.media.mit.edu>,
Derek Atkins <warlord@MIT.EDU> wrote:
>thing -- ask a user for a password, compare it to the passwd entry in
>YP, and then add the password to kerberos.  This, too, would be fairly
>simple to write but I don't have code for you...
>
>Perhaps someone else has code to share?

I am converting an AIX /etc/security/passwd single machine system to
a CNS (ie krb4 based) multiple machine system. One of the (many) things
I have to do is get their crypt()'ed passwords into the kdc. I wrote up
this code to do it. It does basically what derek suggested. There are
very likely some non-secure or non-standard things done in the code, since
I have never used the kerberos API before and never done any security
related coding.

The basic outline of the code is pretty simple. It checks to see if a
kerberos instance of the person exists, and exits if it does. If not,
it prompts for a password (using getpass() ... this may not port well)
and checks against /etc/security/passwd entry (AIXism). If it matches,
it does a password check on kdc side to see if it is a blatantly bad
pw. If that's ok, it will add the principal and exit.

You'll have to create a register.<hostname> principal, stick its key in
your srvtab or (krb-srvtab), and also put "register.*" in your
admin_acl.add. 

I compile with:
gcc -Wall -g  -c -I/usr/kerberos/include reg.c
gcc -Wall -g  -o reg reg.o -L/usr/kerberos/lib -ls -lkadm -lkrb -ldes -lcom_err

It seems to be working ok for me so far on my limited testing, but I would
appreciate if a more knowledgable person would comment on it. 

Thanks,

Emil

-=-=-Cut Here-=-=-=-
/*
 * Verifies existing user on local machine and if they
 * exist, add them with their password
 *
 * Copyright (C) 1996 by the Bronx High School of Science
 *
 * Bronx Science makes no representations about the suitability of
 * this software for any purpose. It is provided "as is" without express
 * or implied warranty.
 *
 * $Id: reg.c,v 1.6 1996/01/05 18:01:43 esit Exp esit $
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/nameser.h>	/* For MAXDNAME */

/*
 * This program must have appropriate permissions to obtain the
 * encrypted password. On AIX, this is suid root.
 */
#include <pwd.h>		/* /etc/passwd stuff */
#include <userpw.h>		/* /etc/security/passwd stuff */
#include <unistd.h>

/*
 * We use a register.hostname key in KEYFILE (usu /etc/krb-srvtab)
 * so we need to be suid root for that too. Alternately,
 * you can store it in some other file and compile that in.
 * Maybe one day this will be a command line param.
 */
#include <kadm.h>
#include <kadm_err.h>
#include <krb_err.h>
#include <com_err.h>

/* My error codes */
#define ERR_USER	1
#define ERR_KRB		2
#define ERR_SYS		3

#define PE_NO		0
#define PE_YES		1
#define PE_UNSURE	2

/*
 * function declarations
 */
int chk_good_pw( des_cblock *key );
int add_principal(char *name, char *instance, char *realm, des_cblock *key );
int princ_exists( char *name, char *instance, char *realm);
int init_principal( char *name, char *instance, char *realm);
void leave( char *str, int x );
char progname[MAXPATHLEN];

/* sigh. Make this global so that we can wipe it in an exit procedure */
char *pword = NULL;

int main( int argc, char *argv[] )
{
    char myname[ANAME_SZ];
    char myinst[INST_SZ];
    char myrealm[REALM_SZ];
    char tktstring[MAXPATHLEN];

    char host[MAXDNAME+1];
    char hostinst[INST_SZ];

    char *cpword = NULL;
    struct userpw *upw_chk;
    des_cblock newkey;

    int status;

    /* Initializations */
    memset( myname, 0, sizeof(myname) );	/* new princ */
    memset( myinst, 0, sizeof(myinst) );
    memset( myrealm, 0, sizeof(myrealm) );
    (void) sprintf( tktstring, "/tmp/tkt_ank_%d", getpid() );
    krb_set_tkt_string( tktstring );

    (void) strcpy( progname, argv[0] );		/* com_err */

    if ( gethostname( host, MAXDNAME ) < 0 ) {	/* svc tickets */
    	leave( "Error: Unable to get hostname. Try from a different machine.\n", ERR_SYS ); 
    }
    (void) strncpy( hostinst, krb_get_phost(host), INST_SZ );

    /* Do we need to to continue? */
    (void) init_principal( myname, myinst, myrealm );
#ifndef DEBUG
    printf( "Checking for %s.%s@%s...", myname, myinst, myrealm );
    if ( princ_exists( myname, myinst, myrealm ) == PE_NO ) {
	printf( "Not found. Continuing...\n" );
    } else {
	printf( "Found. Exitting...\n" );
	exit( 0 );
    }
#endif /* NOT DEBUG */

    /* Begin setup */
    status = kadm_init_link( PWSERV_NAME, KRB_MASTER, myrealm );
    if ( status != KSUCCESS ) {
	com_err( progname, status, "while initializing." );
	exit( ERR_KRB );
    }
    /* Get tickets to check password. */
    status = krb_get_svc_in_tkt("register", hostinst, myrealm, PWSERV_NAME,
				       KRB_MASTER, 1, KEYFILE ); 
    if ( status != KSUCCESS ) {
	/* According to src, get_svc_in_tkt errs need to be offset
	 * by krb_err_base if using com_err. This does the same.
	 */
	fprintf( stderr, "Kerberos error: %s\n", krb_get_err_text(status) );
	leave( NULL, ERR_KRB );
    }

    printf( "Please enter your password. We require this to finish setting up a new\n" );
    printf( "security system. Users who do not complete this process will *NOT* be\n" );
    printf( "allowed to use ANY of the Internet machines at BSC starting March 1.\n");
    pword = getpass( "Your current password: " );
    if ( pword == NULL ) {
	leave( "Error reading password.", ERR_USER );
    }
    if ( strlen(pword) == 0 ) {
	leave( "You have an empty password! I don't trust you...", ERR_USER );
    }

    /* Convert stuff so we don't need cleartext pw anymore */
    (void) des_string_to_key( pword, newkey );
    upw_chk = getuserpw( myname );
    if ( upw_chk == NULL ) {
	leave( "Unable to access password file.", ERR_SYS );
    }
    cpword = crypt( pword, upw_chk->upw_passwd );
    memset( pword, 0, sizeof(pword) );

    /* strcmp returns non zero when strings are different */
    /* We should probably prompt for password again. :) */
    if ( strcmp( cpword, upw_chk->upw_passwd ) ) {
	leave( "Incorrect password.", ERR_USER );
    }

    /* Check for good password; loop until we get one. */
    status = chk_good_pw( &newkey );
    while ( status == KADM_INSECURE_PW ) {
	printf("Please choose another password.\n\n");
	des_read_password( &newkey, "Password: ", 1 );
	status = chk_good_pw( &newkey );
    }

    status = add_principal( myname, myinst, myrealm, &newkey );
    if ( status != KADM_SUCCESS ) {
	com_err(progname, status,
	    "while adding principal.");
    }

    (void) dest_tkt();
    return 0;
}

/* Talk to kdc and see if it is a good key */
int chk_good_pw( des_cblock *key )
{
    int status;
    char *ret_st;

    status = kadm_check_pw( *key, NULL, (u_char **)&ret_st );
    if (ret_st) {
	printf("\n%s\n", ret_st);
	free(ret_st);
    }
    if (status != KADM_SUCCESS) {
	com_err(progname, status,
	    "while checking password.");
	leave( NULL, ERR_KRB );
    }
    if (status == KADM_DB_INUSE) {
	com_err(progname, 0, "Please try again later.");
	leave( NULL,  ERR_KRB );
    }
    return( status );
}

int add_principal(char *name, char *instance, char *realm, des_cblock *key )
{
    Kadm_vals vals;
    int status = KADM_SUCCESS; 

    bzero( (char *)vals.fields, sizeof(vals.fields) );
    SET_FIELD( KADM_NAME, vals.fields );
    SET_FIELD( KADM_INST, vals.fields );
    SET_FIELD( KADM_DESKEY, vals.fields );

    strcpy( vals.name, name );
    strcpy( vals.instance, instance );

    memcpy( (char *) &vals.key_low, (char *) key, sizeof(KRB_INT32) );
    memcpy( (char *) &vals.key_high, (char *)(((KRB_INT32 *) key) + 1), sizeof(KRB_INT32) );

    vals.key_low = htonl(vals.key_low);
    vals.key_high = htonl(vals.key_high);

    status = kadm_add(&vals);
    return( status );
}

/* init_principal
 * Takes blank name, inst, realm; sets them to current user.
 * Checks for validity of user; returns if user is valid 
 */
int init_principal(char *name, char *instance, char *realm)
{
    int status;
    uid_t userno;
    struct passwd *upw;

    userno = getuid();
    if ( userno < 200 ) {
	fprintf( stderr, "Error: uid < 200. Please consult the system administrators.\n" );
	exit( ERR_USER );
    }

    upw = getpwuid( getuid() );

    if ( upw != NULL ) {
	(void) strcpy( name, upw->pw_name );
    } else {
	fprintf( stderr, "Error: you don't appear to be in the passwd file.\n" );
	exit( ERR_USER );
    }

    if ( ( status = krb_get_lrealm( realm, 1 ) ) != KSUCCESS ) {
	com_err( progname, status, "while getting local realm." );
	exit( ERR_KRB );
    }

    return 0;
}

/* Snarfed from src/kadmin/get_srvtab.c from CNS krb4 95q1 */
int princ_exists(char *name, char *instance, char *realm)
{
    int status;

    status = krb_get_pw_in_tkt(name, instance, realm, "krbtgt", realm, 1, "");

    if ((status == KSUCCESS) || (status == INTK_BADPW))
        return(PE_YES);
    else if (status == KDC_PR_UNKNOWN)
        return(PE_NO);
    else
        return(PE_UNSURE);
}

void leave( char *str, int x )
{
    if (str)
	(void) fprintf( stderr, "%s\n", str );
    if (pword)
	memset( pword, 0, sizeof(pword) ); 
    (void) dest_tkt();
    exit(x);
}
-=-=-Cut Here-=-=-=-
-- 
Emil Sit                        | E-mail: sit@mit.edu or esit@bxscience.edu
http://www.bxscience.edu/~esit/ | PGP Public key available on request

home help back first fref pref prev next nref lref last post