2008-02-06 18:01:22 -06:00
/* Authors: Simo Sorce <ssorce@redhat.com>
2008-02-04 14:15:52 -06:00
*
* Copyright ( C ) 2007 Red Hat
* see file ' COPYING ' for use and warranty information
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; version 2 only
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2008-02-06 18:01:22 -06:00
*/
2008-02-04 14:15:52 -06:00
2007-12-21 10:37:19 -06:00
# define _GNU_SOURCE
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <sys/time.h>
# include <unistd.h>
# include <stdio.h>
# include <stdarg.h>
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include <time.h>
2008-06-26 05:52:25 -05:00
# define KRB5_PRIVATE 1
2007-12-21 10:37:19 -06:00
# include <krb5.h>
2008-05-01 08:57:32 -05:00
# ifdef WITH_MOZLDAP
# include <mozldap/ldap.h>
# else
2007-12-21 10:37:19 -06:00
# include <ldap.h>
2008-05-01 08:57:32 -05:00
# endif
2007-12-21 10:37:19 -06:00
# include <sasl/sasl.h>
# include <popt.h>
2008-08-13 08:46:03 -05:00
/* Salt types */
# define NO_SALT -1
# define KRB5_KDB_SALTTYPE_NORMAL 0
# define KRB5_KDB_SALTTYPE_V4 1
# define KRB5_KDB_SALTTYPE_NOREALM 2
# define KRB5_KDB_SALTTYPE_ONLYREALM 3
# define KRB5_KDB_SALTTYPE_SPECIAL 4
# define KRB5_KDB_SALTTYPE_AFS3 5
# define KEYTAB_SET_OID "2.16.840.1.113730.3.8.3.1"
# define KEYTAB_RET_OID "2.16.840.1.113730.3.8.3.2"
struct krb_key_salt {
krb5_enctype enctype ;
krb5_int32 salttype ;
krb5_keyblock key ;
krb5_data salt ;
} ;
struct keys_container {
krb5_int32 nkeys ;
struct krb_key_salt * ksdata ;
} ;
2007-12-21 10:37:19 -06:00
static int ldap_sasl_interact ( LDAP * ld , unsigned flags , void * priv_data , void * sit )
{
sasl_interact_t * in = NULL ;
int ret = LDAP_OTHER ;
krb5_principal princ = ( krb5_principal ) priv_data ;
if ( ! ld ) return LDAP_PARAM_ERROR ;
for ( in = sit ; in & & in - > id ! = SASL_CB_LIST_END ; in + + ) {
switch ( in - > id ) {
case SASL_CB_USER :
in - > result = princ - > data [ 0 ] . data ;
in - > len = princ - > data [ 0 ] . length ;
ret = LDAP_SUCCESS ;
break ;
case SASL_CB_GETREALM :
in - > result = princ - > realm . data ;
in - > len = princ - > realm . length ;
ret = LDAP_SUCCESS ;
break ;
default :
in - > result = NULL ;
in - > len = 0 ;
ret = LDAP_OTHER ;
}
}
return ret ;
}
2008-08-13 08:46:03 -05:00
static void free_keys_contents ( krb5_context krbctx , struct keys_container * keys )
{
struct krb_key_salt * ksdata ;
int i ;
ksdata = keys - > ksdata ;
for ( i = 0 ; i < keys - > nkeys ; i + + ) {
krb5_free_keyblock_contents ( krbctx , & ksdata [ i ] . key ) ;
krb5_free_data_contents ( krbctx , & ksdata [ i ] . salt ) ;
}
free ( ksdata ) ;
keys - > ksdata = NULL ;
keys - > nkeys = 0 ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
/* Determines Encryption and Salt types,
* allocates key_salt data storage ,
* filters out equivalent encodings ,
* returns 0 if no enctypes available , > 0 if enctypes are available */
static int prep_ksdata ( krb5_context krbctx , const char * str ,
struct keys_container * keys )
2008-02-06 18:01:22 -06:00
{
2008-08-13 08:46:03 -05:00
struct krb_key_salt * ksdata ;
krb5_error_code krberr ;
int n , i , j , nkeys ;
2008-02-06 18:01:22 -06:00
2008-08-13 08:46:03 -05:00
if ( str = = NULL ) {
krb5_enctype * ktypes ;
2008-02-06 18:01:22 -06:00
2008-08-13 08:46:03 -05:00
krberr = krb5_get_permitted_enctypes ( krbctx , & ktypes ) ;
if ( krberr ) {
fprintf ( stderr , " No system preferred enctypes ?! \n " ) ;
return 0 ;
}
2008-02-06 18:01:22 -06:00
2008-08-13 08:46:03 -05:00
for ( n = 0 ; ktypes [ n ] ; n + + ) /* count */ ;
2008-02-06 18:01:22 -06:00
2008-08-13 08:46:03 -05:00
ksdata = calloc ( n + 1 , sizeof ( struct krb_key_salt ) ) ;
if ( NULL = = ksdata ) {
fprintf ( stderr , " Out of memory!? \n " ) ;
return 0 ;
}
2008-02-06 18:01:22 -06:00
2008-08-13 08:46:03 -05:00
for ( i = 0 ; i < n ; i + + ) {
ksdata [ i ] . enctype = ktypes [ i ] ;
ksdata [ i ] . salttype = KRB5_KDB_SALTTYPE_NORMAL ;
}
2008-02-06 18:01:22 -06:00
2008-08-13 08:46:03 -05:00
krb5_free_ktypes ( krbctx , ktypes ) ;
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
nkeys = i ;
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
} else {
char * tmp , * t , * p , * q ;
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
t = tmp = strdup ( str ) ;
if ( ! tmp ) {
fprintf ( stderr , " Out of memory \n " ) ;
return 0 ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
/* count */
n = 0 ;
while ( ( p = strchr ( t , ' , ' ) ) ) {
t = p + 1 ;
n + + ;
}
n + + ; /* count the last one that is 0 terminated instead */
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
/* at the end we will have at most n entries + 1 terminating */
ksdata = calloc ( n + 1 , sizeof ( struct krb_key_salt ) ) ;
if ( ! ksdata ) {
fprintf ( stderr , " Out of memory \n " ) ;
return 0 ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
for ( i = 0 , j = 0 , t = tmp ; i < n ; i + + ) {
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
p = strchr ( t , ' , ' ) ;
if ( p ) * p = ' \0 ' ;
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
q = strchr ( t , ' : ' ) ;
if ( q ) * q + + = ' \0 ' ;
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
krberr = krb5_string_to_enctype ( t , & ksdata [ j ] . enctype ) ;
if ( krberr ! = 0 ) {
fprintf ( stderr ,
" Warning unrecognized encryption type: [%s] \n " , t ) ;
t = p + 1 ;
continue ;
}
t = p + 1 ;
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
if ( ! q ) {
ksdata [ j ] . salttype = KRB5_KDB_SALTTYPE_NORMAL ;
j + + ;
continue ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
krberr = krb5_string_to_salttype ( q , & ksdata [ j ] . salttype ) ;
if ( krberr ! = 0 ) {
fprintf ( stderr , " Warning unrecognized salt type: [%s] \n " , q ) ;
continue ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
j + + ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
nkeys = j ;
free ( tmp ) ;
}
/* Check we don't already have a key with a similar encoding,
* it would just produce redundant data and this is what the
* MIT code do anyway */
for ( i = 0 , n = 0 ; i < nkeys ; i + + ) {
int similar = 0 ;
for ( j = 0 ; j < i ; j + + ) {
krberr = krb5_c_enctype_compare ( krbctx ,
ksdata [ j ] . enctype ,
ksdata [ i ] . enctype ,
& similar ) ;
if ( krberr ) {
free_keys_contents ( krbctx , keys ) ;
fprintf ( stderr , " Enctype comparison failed! \n " ) ;
return 0 ;
}
if ( similar & &
( ksdata [ j ] . salttype = = ksdata [ i ] . salttype ) ) {
break ;
}
}
if ( j < i ) {
/* redundant encoding, remove it, and shift others */
int x ;
for ( x = i ; x < nkeys - 1 ; x + + ) {
ksdata [ x ] . enctype = ksdata [ x + 1 ] . enctype ;
ksdata [ x ] . salttype = ksdata [ x + 1 ] . salttype ;
}
continue ;
}
/* count only confirmed enc/salt tuples */
n + + ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
keys - > nkeys = n ;
keys - > ksdata = ksdata ;
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
return n ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
static int create_keys ( krb5_context krbctx ,
krb5_principal princ ,
char * password ,
const char * enctypes_string ,
struct keys_container * keys )
{
struct krb_key_salt * ksdata ;
krb5_error_code krberr ;
krb5_data key_password ;
krb5_data * realm ;
int i , j , nkeys ;
int ret ;
ret = prep_ksdata ( krbctx , enctypes_string , keys ) ;
if ( ret = = 0 ) return 0 ;
ksdata = keys - > ksdata ;
nkeys = keys - > nkeys ;
if ( password ) {
key_password . data = password ;
key_password . length = strlen ( password ) ;
realm = krb5_princ_realm ( krbctx , princ ) ;
}
for ( i = 0 ; i < nkeys ; i + + ) {
krb5_data * salt ;
if ( ! password ) {
/* cool, random keys */
krberr = krb5_c_make_random_key ( krbctx ,
ksdata [ i ] . enctype ,
& ksdata [ i ] . key ) ;
if ( krberr ) {
fprintf ( stderr , " Failed to create random key! \n " ) ;
return 0 ;
}
/* set the salt to NO_SALT as the key was random */
ksdata [ i ] . salttype = NO_SALT ;
continue ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
/* Make keys using password and required salt */
switch ( ksdata [ i ] . salttype ) {
case KRB5_KDB_SALTTYPE_ONLYREALM :
krberr = krb5_copy_data ( krbctx , realm , & salt ) ;
if ( krberr ) {
fprintf ( stderr , " Failed to create key! \n " ) ;
return 0 ;
}
ksdata [ i ] . salt . length = salt - > length ;
ksdata [ i ] . salt . data = malloc ( salt - > length ) ;
if ( ! ksdata [ i ] . salt . data ) {
fprintf ( stderr , " Out of memory! \n " ) ;
return 0 ;
}
memcpy ( ksdata [ i ] . salt . data , salt - > data , salt - > length ) ;
krb5_free_data ( krbctx , salt ) ;
break ;
case KRB5_KDB_SALTTYPE_NOREALM :
krberr = krb5_principal2salt_norealm ( krbctx , princ , & ksdata [ i ] . salt ) ;
if ( krberr ) {
fprintf ( stderr , " Failed to create key! \n " ) ;
return 0 ;
}
break ;
case KRB5_KDB_SALTTYPE_NORMAL :
krberr = krb5_principal2salt ( krbctx , princ , & ksdata [ i ] . salt ) ;
if ( krberr ) {
fprintf ( stderr , " Failed to create key! \n " ) ;
return 0 ;
}
break ;
/* no KRB5_KDB_SALTTYPE_V4, we do not support krb v4 */
case KRB5_KDB_SALTTYPE_AFS3 :
/* Comment from MIT sources:
* * Why do we do this ? Well , the afs_mit_string_to_key
* * needs to use strlen , and the realm is not NULL
* * terminated . . . .
*/
ksdata [ i ] . salt . data = ( char * ) malloc ( realm - > length + 1 ) ;
if ( NULL = = ksdata [ i ] . salt . data ) {
fprintf ( stderr , " Out of memory! \n " ) ;
return 0 ;
}
memcpy ( ( char * ) ksdata [ i ] . salt . data ,
( char * ) realm - > data , realm - > length ) ;
ksdata [ i ] . salt . data [ realm - > length ] = ' \0 ' ;
/* AFS uses a special length (UGLY) */
ksdata [ i ] . salt . length = SALT_TYPE_AFS_LENGTH ;
break ;
default :
fprintf ( stderr , " Bad or unsupported salt type (%d)! \n " ,
ksdata [ i ] . salttype ) ;
return 0 ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
krberr = krb5_c_string_to_key ( krbctx ,
ksdata [ i ] . enctype ,
& key_password ,
& ksdata [ i ] . salt ,
& ksdata [ i ] . key ) ;
if ( krberr ) {
fprintf ( stderr , " Failed to create key! \n " ) ;
return 0 ;
}
/* set back salt length to real value if AFS3 */
if ( ksdata [ i ] . salttype = = KRB5_KDB_SALTTYPE_AFS3 ) {
ksdata [ i ] . salt . length = realm - > length ;
}
}
return nkeys ;
2007-12-21 10:37:19 -06:00
}
2008-08-13 08:46:03 -05:00
static struct berval * create_key_control ( struct keys_container * keys ,
const char * principalName )
2007-12-21 10:37:19 -06:00
{
2008-08-13 08:46:03 -05:00
struct krb_key_salt * ksdata ;
struct berval * bval ;
BerElement * be ;
int ret , i ;
be = ber_alloc_t ( LBER_USE_DER ) ;
if ( ! be ) {
return NULL ;
}
ret = ber_printf ( be , " {s{ " , principalName ) ;
if ( ret = = - 1 ) {
ber_free ( be , 1 ) ;
return NULL ;
}
ksdata = keys - > ksdata ;
for ( i = 0 ; i < keys - > nkeys ; i + + ) {
/* we set only the EncryptionKey and salt, no s2kparams */
ret = ber_printf ( be , " {t[{t[i]t[o]}] " ,
( ber_tag_t ) ( LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0 ) ,
( ber_tag_t ) ( LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0 ) ,
( ber_int_t ) ksdata [ i ] . enctype ,
( ber_tag_t ) ( LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1 ) ,
( char * ) ksdata [ i ] . key . contents , ( ber_len_t ) ksdata [ i ] . key . length ) ;
if ( ret = = - 1 ) {
ber_free ( be , 1 ) ;
return NULL ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
if ( ksdata [ i ] . salttype = = NO_SALT ) {
ret = ber_printf ( be , " } " ) ;
continue ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
/* we have to pass a salt structure */
ret = ber_printf ( be , " t[{t[i]t[o]}]} " ,
( ber_tag_t ) ( LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1 ) ,
( ber_tag_t ) ( LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0 ) ,
( ber_int_t ) ksdata [ i ] . salttype ,
( ber_tag_t ) ( LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1 ) ,
( char * ) ksdata [ i ] . salt . data , ( ber_len_t ) ksdata [ i ] . salt . length ) ;
if ( ret = = - 1 ) {
ber_free ( be , 1 ) ;
return NULL ;
}
}
ret = ber_printf ( be , " }} " ) ;
if ( ret = = - 1 ) {
ber_free ( be , 1 ) ;
return NULL ;
}
ret = ber_flatten ( be , & bval ) ;
if ( ret = = - 1 ) {
ber_free ( be , 1 ) ;
return NULL ;
}
ber_free ( be , 1 ) ;
return bval ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
int filter_keys ( krb5_context krbctx , struct keys_container * keys ,
ber_int_t * enctypes )
{
struct krb_key_salt * ksdata ;
int i , j , n ;
n = keys - > nkeys ;
ksdata = keys - > ksdata ;
for ( i = 0 ; i < n ; i + + ) {
if ( ksdata [ i ] . enctype = = enctypes [ i ] ) continue ;
if ( enctypes [ i ] = = 0 ) {
/* remove unsupported one */
krb5_free_keyblock_contents ( krbctx , & ksdata [ i ] . key ) ;
krb5_free_data_contents ( krbctx , & ksdata [ i ] . salt ) ;
for ( j = i ; j < n - 1 ; j + + ) {
keys [ j ] = keys [ j + 1 ] ;
}
n - - ;
/* new key has been moved to this position, make sure
* we do not skip it , by neutralizing next i increment */
i - - ;
}
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
if ( n = = 0 ) {
fprintf ( stderr , " No keys accepted by KDC \n " ) ;
return 0 ;
}
keys - > nkeys = n ;
return n ;
2007-12-21 10:37:19 -06:00
}
2008-08-13 08:46:03 -05:00
static int ldap_set_keytab ( krb5_context krbctx ,
const char * servername ,
2007-12-21 10:37:19 -06:00
const char * principal_name ,
krb5_principal princ ,
2008-08-13 08:46:03 -05:00
struct keys_container * keys )
2007-12-21 10:37:19 -06:00
{
int version ;
LDAP * ld = NULL ;
BerElement * sctrl = NULL ;
struct berval * control = NULL ;
char * retoid = NULL ;
struct berval * retdata = NULL ;
struct timeval tv ;
2008-06-26 05:52:25 -05:00
LDAPMessage * res = NULL ;
2007-12-21 10:37:19 -06:00
LDAPControl * * srvctrl = NULL ;
LDAPControl * pprc = NULL ;
char * err = NULL ;
int msgid ;
int ret , rc ;
int kvno , i ;
ber_tag_t rtag ;
ber_int_t * encs = NULL ;
2008-08-13 08:46:03 -05:00
/* cant' return more than nkeys, sometimes less */
encs = calloc ( keys - > nkeys + 1 , sizeof ( ber_int_t ) ) ;
2007-12-21 10:37:19 -06:00
if ( ! encs ) {
fprintf ( stderr , " Out of Memory! \n " ) ;
return 0 ;
}
/* build password change control */
2008-08-13 08:46:03 -05:00
control = create_key_control ( keys , principal_name ) ;
2007-12-21 10:37:19 -06:00
if ( ! control ) {
fprintf ( stderr , " Failed to create control! \n " ) ;
goto error_out ;
}
/* TODO: support referrals ? */
2008-05-01 08:57:32 -05:00
ld = ldap_init ( servername , 389 ) ;
if ( ld = = NULL ) {
2007-12-21 10:37:19 -06:00
fprintf ( stderr , " Unable to initialize ldap library! \n " ) ;
goto error_out ;
}
version = LDAP_VERSION3 ;
ret = ldap_set_option ( ld , LDAP_OPT_PROTOCOL_VERSION , & version ) ;
2008-05-01 08:57:32 -05:00
if ( ret ! = LDAP_SUCCESS ) {
2007-12-21 10:37:19 -06:00
fprintf ( stderr , " Unable to set ldap options! \n " ) ;
goto error_out ;
}
ret = ldap_sasl_interactive_bind_s ( ld ,
NULL , " GSSAPI " ,
NULL , NULL ,
2008-03-05 13:54:13 -06:00
LDAP_SASL_QUIET ,
2007-12-21 10:37:19 -06:00
ldap_sasl_interact , princ ) ;
if ( ret ! = LDAP_SUCCESS ) {
fprintf ( stderr , " SASL Bind failed! \n " ) ;
goto error_out ;
}
/* find base dn */
/* TODO: address the case where we have multiple naming contexts */
tv . tv_sec = 10 ;
2008-02-06 18:01:22 -06:00
tv . tv_usec = 0 ;
2007-12-21 10:37:19 -06:00
/* perform password change */
ret = ldap_extended_operation ( ld ,
KEYTAB_SET_OID ,
control , NULL , NULL ,
& msgid ) ;
if ( ret ! = LDAP_SUCCESS ) {
fprintf ( stderr , " Operation failed! %s \n " , ldap_err2string ( ret ) ) ;
goto error_out ;
}
ber_bvfree ( control ) ;
2008-02-06 18:01:22 -06:00
control = NULL ;
2007-12-21 10:37:19 -06:00
tv . tv_sec = 10 ;
2008-02-06 18:01:22 -06:00
tv . tv_usec = 0 ;
2007-12-21 10:37:19 -06:00
ret = ldap_result ( ld , msgid , 1 , & tv , & res ) ;
if ( ret = = - 1 ) {
fprintf ( stderr , " Operation failed! %s \n " , ldap_err2string ( ret ) ) ;
goto error_out ;
}
ret = ldap_parse_extended_result ( ld , res , & retoid , & retdata , 0 ) ;
if ( ret ! = LDAP_SUCCESS ) {
fprintf ( stderr , " Operation failed! %s \n " , ldap_err2string ( ret ) ) ;
goto error_out ;
}
2008-02-06 18:01:22 -06:00
2007-12-21 10:37:19 -06:00
ret = ldap_parse_result ( ld , res , & rc , NULL , & err , NULL , & srvctrl , 0 ) ;
if ( ret ! = LDAP_SUCCESS | | rc ! = LDAP_SUCCESS ) {
fprintf ( stderr , " Operation failed! %s \n " , err ? err : ldap_err2string ( ret ) ) ;
goto error_out ;
}
if ( ! srvctrl ) {
fprintf ( stderr , " Missing reply control! \n " ) ;
goto error_out ;
}
for ( i = 0 ; srvctrl [ i ] ; i + + ) {
if ( 0 = = strcmp ( srvctrl [ i ] - > ldctl_oid , KEYTAB_RET_OID ) ) {
pprc = srvctrl [ i ] ;
}
}
if ( ! pprc ) {
fprintf ( stderr , " Missing reply control! \n " ) ;
goto error_out ;
}
sctrl = ber_init ( & pprc - > ldctl_value ) ;
if ( ! sctrl ) {
fprintf ( stderr , " ber_init() failed, Invalid control ?! \n " ) ;
goto error_out ;
}
/* Format of response
*
* KeytabGetRequest : : = SEQUENCE {
* new_kvno Int32
* SEQUENCE OF KeyTypes
* }
*
* * List of accepted enctypes *
* KeyTypes : : = SEQUENCE {
* enctype Int32
* }
*/
rtag = ber_scanf ( sctrl , " {i{ " , & kvno ) ;
if ( rtag = = LBER_ERROR ) {
fprintf ( stderr , " ber_scanf() failed, Invalid control ?! \n " ) ;
goto error_out ;
}
2008-08-13 08:46:03 -05:00
for ( i = 0 ; i < keys - > nkeys ; i + + ) {
2007-12-21 10:37:19 -06:00
ret = ber_scanf ( sctrl , " {i} " , & encs [ i ] ) ;
if ( ret = = LBER_ERROR ) break ;
2008-02-06 18:01:22 -06:00
}
2008-08-13 08:46:03 -05:00
ret = filter_keys ( krbctx , keys , encs ) ;
if ( ret = = 0 ) goto error_out ;
2007-12-21 10:37:19 -06:00
if ( err ) ldap_memfree ( err ) ;
ber_free ( sctrl , 1 ) ;
ldap_controls_free ( srvctrl ) ;
ldap_msgfree ( res ) ;
2008-05-01 08:57:32 -05:00
ldap_unbind_ext ( ld , NULL , NULL ) ;
2007-12-21 10:37:19 -06:00
return kvno ;
error_out :
if ( sctrl ) ber_free ( sctrl , 1 ) ;
if ( srvctrl ) ldap_controls_free ( srvctrl ) ;
if ( err ) ldap_memfree ( err ) ;
if ( res ) ldap_msgfree ( res ) ;
2008-05-01 08:57:32 -05:00
if ( ld ) ldap_unbind_ext ( ld , NULL , NULL ) ;
2007-12-21 10:37:19 -06:00
if ( control ) ber_bvfree ( control ) ;
2008-05-14 14:48:34 -05:00
free ( encs ) ;
2007-12-21 10:37:19 -06:00
return 0 ;
}
2008-08-13 08:46:03 -05:00
static char * ask_password ( krb5_context krbctx )
{
krb5_prompt ap_prompts [ 2 ] ;
krb5_data k5d_pw0 ;
krb5_data k5d_pw1 ;
char pw0 [ 256 ] ;
char pw1 [ 256 ] ;
char * password ;
k5d_pw0 . length = sizeof ( pw0 ) ;
k5d_pw0 . data = pw0 ;
ap_prompts [ 0 ] . prompt = " New Principal Password " ;
ap_prompts [ 0 ] . hidden = 1 ;
ap_prompts [ 0 ] . reply = & k5d_pw0 ;
k5d_pw1 . length = sizeof ( pw1 ) ;
k5d_pw1 . data = pw1 ;
ap_prompts [ 1 ] . prompt = " Verify Principal Password " ;
ap_prompts [ 1 ] . hidden = 1 ;
ap_prompts [ 1 ] . reply = & k5d_pw1 ;
krb5_prompter_posix ( krbctx , NULL ,
NULL , NULL ,
2 , ap_prompts ) ;
if ( strcmp ( pw0 , pw1 ) ) {
fprintf ( stderr , " Passwords do not match! " ) ;
return NULL ;
}
password = malloc ( k5d_pw0 . length + 1 ) ;
if ( ! password ) return NULL ;
memcpy ( password , pw0 , k5d_pw0 . length ) ;
password [ k5d_pw0 . length ] = ' \0 ' ;
return password ;
}
2007-12-21 10:37:19 -06:00
int main ( int argc , char * argv [ ] )
{
static const char * server = NULL ;
static const char * principal = NULL ;
static const char * keytab = NULL ;
2008-02-06 18:01:22 -06:00
static const char * enctypes_string = NULL ;
2008-03-05 13:54:13 -06:00
int quiet = 0 ;
2008-08-13 08:46:03 -05:00
int askpass = 0 ;
2008-04-08 17:02:42 -05:00
int permitted_enctypes = 0 ;
2007-12-21 10:37:19 -06:00
struct poptOption options [ ] = {
2008-06-04 10:03:36 -05:00
{ " quiet " , ' q ' , POPT_ARG_NONE , & quiet , 0 , " Print as little as possible " , " Output only on errors " } ,
2007-12-21 10:37:19 -06:00
{ " server " , ' s ' , POPT_ARG_STRING , & server , 0 , " Contact this specific KDC Server " , " Server Name " } ,
{ " principal " , ' p ' , POPT_ARG_STRING , & principal , 0 , " The principal to get a keytab for (ex: ftp/ftp.example.com@EXAMPLE.COM) " , " Kerberos Service Principal Name " } ,
{ " keytab " , ' k ' , POPT_ARG_STRING , & keytab , 0 , " File were to store the keytab information " , " Keytab File Name " } ,
2008-06-04 10:03:36 -05:00
{ " enctypes " , ' e ' , POPT_ARG_STRING , & enctypes_string , 0 , " Encryption types to request " , " Comma separated encryption types list " } ,
2008-04-08 17:02:42 -05:00
{ " permitted-enctypes " , 0 , POPT_ARG_NONE , & permitted_enctypes , 0 , " Show the list of permitted encryption types and exit " , " Permitted Encryption Types " } ,
2008-08-13 08:46:03 -05:00
{ " password " , ' P ' , POPT_ARG_NONE , & askpass , 0 , " Asks for a non-random password to use for the principal " } ,
2007-12-21 10:37:19 -06:00
{ NULL , 0 , POPT_ARG_NONE , NULL , 0 , NULL , NULL }
} ;
poptContext pc ;
char * ktname ;
2008-08-13 08:46:03 -05:00
char * password = NULL ;
2007-12-21 10:37:19 -06:00
krb5_context krbctx ;
krb5_ccache ccache ;
krb5_principal uprinc ;
krb5_principal sprinc ;
krb5_error_code krberr ;
ber_int_t * enctypes ;
2008-08-13 08:46:03 -05:00
struct keys_container keys ;
2007-12-21 10:37:19 -06:00
krb5_keytab kt ;
int kvno ;
int i , ret ;
2008-04-08 17:02:42 -05:00
krberr = krb5_init_context ( & krbctx ) ;
if ( krberr ) {
fprintf ( stderr , " Kerberos context initialization failed \n " ) ;
exit ( 1 ) ;
}
2007-12-21 10:37:19 -06:00
pc = poptGetContext ( " ipa-getkeytab " , argc , ( const char * * ) argv , options , 0 ) ;
ret = poptGetNextOpt ( pc ) ;
2008-04-08 17:02:42 -05:00
if ( ret = = - 1 & & permitted_enctypes & &
! ( server | | principal | | keytab | | quiet ) ) {
2008-08-13 08:46:03 -05:00
krb5_enctype * ktypes ;
2008-04-08 17:02:42 -05:00
char enc [ 79 ] ; /* fit std terminal or truncate */
krberr = krb5_get_permitted_enctypes ( krbctx , & ktypes ) ;
if ( krberr ) {
fprintf ( stderr , " No system preferred enctypes ?! \n " ) ;
exit ( 1 ) ;
}
fprintf ( stdout , " Supported encryption types: \n " ) ;
for ( i = 0 ; ktypes [ i ] ; i + + ) {
krberr = krb5_enctype_to_string ( ktypes [ i ] , enc , 79 ) ;
if ( krberr ) {
fprintf ( stderr , " Warning: failed to convert type (#%d) \n " , i ) ;
continue ;
}
fprintf ( stdout , " %s \n " , enc ) ;
}
2008-08-13 08:46:03 -05:00
krb5_free_ktypes ( krbctx , ktypes ) ;
2008-04-08 17:02:42 -05:00
exit ( 0 ) ;
}
if ( ret ! = - 1 | | ! server | | ! principal | | ! keytab | | permitted_enctypes ) {
2008-03-05 13:54:13 -06:00
if ( ! quiet ) {
poptPrintUsage ( pc , stderr , 0 ) ;
}
2008-04-08 17:02:42 -05:00
exit ( 2 ) ;
2007-12-21 10:37:19 -06:00
}
2008-08-13 08:46:03 -05:00
if ( askpass ) {
password = ask_password ( krbctx ) ;
if ( ! password ) {
exit ( 2 ) ;
}
2008-09-24 16:50:56 -05:00
} else if ( enctypes_string & & strchr ( enctypes_string , ' : ' ) ) {
2008-08-13 08:46:03 -05:00
if ( ! quiet ) {
fprintf ( stderr , " Warning: salt types are not honored with randomized passwords (see opt. -P) \n " ) ;
}
}
2007-12-21 10:37:19 -06:00
ret = asprintf ( & ktname , " WRFILE:%s " , keytab ) ;
if ( ret = = - 1 ) {
exit ( 3 ) ;
}
krberr = krb5_parse_name ( krbctx , principal , & sprinc ) ;
if ( krberr ) {
fprintf ( stderr , " Invalid Service Principal Name \n " ) ;
exit ( 4 ) ;
}
krberr = krb5_cc_default ( krbctx , & ccache ) ;
if ( krberr ) {
2008-02-06 18:01:22 -06:00
fprintf ( stderr , " Kerberos Credential Cache not found \n "
" Do you have a Kerberos Ticket? \n " ) ;
2007-12-21 10:37:19 -06:00
exit ( 5 ) ;
}
2008-02-06 18:01:22 -06:00
2007-12-21 10:37:19 -06:00
krberr = krb5_cc_get_principal ( krbctx , ccache , & uprinc ) ;
if ( krberr ) {
2008-02-06 18:01:22 -06:00
fprintf ( stderr , " Kerberos User Principal not found \n "
" Do you have a valid Credential Cache? \n " ) ;
2007-12-21 10:37:19 -06:00
exit ( 6 ) ;
}
krberr = krb5_kt_resolve ( krbctx , ktname , & kt ) ;
if ( krberr ) {
fprintf ( stderr , " Failed to open Keytab \n " ) ;
exit ( 7 ) ;
}
/* create key material */
2008-08-13 08:46:03 -05:00
ret = create_keys ( krbctx , sprinc , password , enctypes_string , & keys ) ;
if ( ! ret ) {
fprintf ( stderr , " Failed to create key material \n " ) ;
2008-02-06 18:01:22 -06:00
exit ( 8 ) ;
}
2007-12-21 10:37:19 -06:00
2008-08-13 08:46:03 -05:00
kvno = ldap_set_keytab ( krbctx , server , principal , uprinc , & keys ) ;
2007-12-21 10:37:19 -06:00
if ( ! kvno ) {
exit ( 9 ) ;
}
2008-08-13 08:46:03 -05:00
for ( i = 0 ; i < keys . nkeys ; i + + ) {
2007-12-21 10:37:19 -06:00
krb5_keytab_entry kt_entry ;
memset ( ( char * ) & kt_entry , 0 , sizeof ( kt_entry ) ) ;
kt_entry . principal = sprinc ;
2008-08-13 08:46:03 -05:00
kt_entry . key = keys . ksdata [ i ] . key ;
2007-12-21 10:37:19 -06:00
kt_entry . vno = kvno ;
krberr = krb5_kt_add_entry ( krbctx , kt , & kt_entry ) ;
if ( krberr ) {
fprintf ( stderr , " Failed to add key to the keytab \n " ) ;
exit ( 11 ) ;
}
}
2008-08-13 08:46:03 -05:00
free_keys_contents ( krbctx , & keys ) ;
2007-12-21 10:37:19 -06:00
krberr = krb5_kt_close ( krbctx , kt ) ;
if ( krberr ) {
fprintf ( stderr , " Failed to close the keytab \n " ) ;
exit ( 12 ) ;
}
2008-03-05 13:54:13 -06:00
if ( ! quiet ) {
fprintf ( stderr ,
" Keytab successfully retrieved and stored in: %s \n " ,
keytab ) ;
}
2007-12-21 10:37:19 -06:00
exit ( 0 ) ;
}