X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;ds=inline;f=contrib%2Fslapd-modules%2Fsmbk5pwd%2Fsmbk5pwd.c;h=301bdc89456ab39310e2629338561a7196976b46;hb=c216e1809971ac6c5b2b973501e0e8a766b77287;hp=38b8b47102d00e19aab9a32bd380602307c47f43;hpb=584e0fd0ce2d43432ec00e0713689e804183cb0b;p=openldap diff --git a/contrib/slapd-modules/smbk5pwd/smbk5pwd.c b/contrib/slapd-modules/smbk5pwd/smbk5pwd.c index 38b8b47102..301bdc8945 100644 --- a/contrib/slapd-modules/smbk5pwd/smbk5pwd.c +++ b/contrib/slapd-modules/smbk5pwd/smbk5pwd.c @@ -1,7 +1,9 @@ /* smbk5pwd.c - Overlay for managing Samba and Heimdal passwords */ /* $OpenLDAP$ */ -/* - * Copyright 2004-2005 by Howard Chu, Symas Corp. +/* This work is part of OpenLDAP Software . + * + * Copyright 2004-2013 The OpenLDAP Foundation. + * Portions Copyright 2004-2005 by Howard Chu, Symas Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -12,11 +14,10 @@ * top-level directory of the distribution or, alternatively, at * . */ -/* +/* ACKNOWLEDGEMENTS: * Support for table-driven configuration added by Pierangelo Masarati. * Support for sambaPwdMustChange and sambaPwdCanChange added by Marco D'Ettorre. - * - * The conditions of the OpenLDAP Public License apply. + * Support for shadowLastChange added by SATOH Fumiyasu @ OSS Technology, Inc. */ #include @@ -59,12 +60,20 @@ static HDB *db; static AttributeDescription *ad_krb5Key; static AttributeDescription *ad_krb5KeyVersionNumber; static AttributeDescription *ad_krb5PrincipalName; +static AttributeDescription *ad_krb5ValidEnd; static ObjectClass *oc_krb5KDCEntry; #endif #ifdef DO_SAMBA +#ifdef HAVE_GNUTLS +#include +typedef unsigned char DES_cblock[8]; +#elif HAVE_OPENSSL #include #include +#else +#error Unsupported crypto backend. +#endif #include "ldap_utf8.h" static AttributeDescription *ad_sambaLMPassword; @@ -75,14 +84,21 @@ static AttributeDescription *ad_sambaPwdCanChange; static ObjectClass *oc_sambaSamAccount; #endif +#ifdef DO_SHADOW +static AttributeDescription *ad_shadowLastChange; +static ObjectClass *oc_shadowAccount; +#endif + /* Per-instance configuration information */ typedef struct smbk5pwd_t { unsigned mode; #define SMBK5PWD_F_KRB5 (0x1U) #define SMBK5PWD_F_SAMBA (0x2U) +#define SMBK5PWD_F_SHADOW (0x4U) #define SMBK5PWD_DO_KRB5(pi) ((pi)->mode & SMBK5PWD_F_KRB5) #define SMBK5PWD_DO_SAMBA(pi) ((pi)->mode & SMBK5PWD_F_SAMBA) +#define SMBK5PWD_DO_SHADOW(pi) ((pi)->mode & SMBK5PWD_F_SHADOW) #ifdef DO_KRB5 /* nothing yet */ @@ -94,6 +110,10 @@ typedef struct smbk5pwd_t { /* How many seconds after allowing a password change? */ time_t smb_can_change; #endif + +#ifdef DO_SHADOW + /* nothing yet */ +#endif } smbk5pwd_t; static const unsigned SMBK5PWD_F_ALL = @@ -104,6 +124,9 @@ static const unsigned SMBK5PWD_F_ALL = #ifdef DO_SAMBA | SMBK5PWD_F_SAMBA #endif +#ifdef DO_SHADOW + | SMBK5PWD_F_SHADOW +#endif ; static int smbk5pwd_modules_init( smbk5pwd_t *pi ); @@ -114,7 +137,7 @@ static const char hex[] = "0123456789abcdef"; /* From liblutil/passwd.c... */ static void lmPasswd_to_key( const char *lmPasswd, - des_cblock *key) + DES_cblock *key) { const unsigned char *lpw = (const unsigned char *)lmPasswd; unsigned char *k = (unsigned char *)key; @@ -129,7 +152,9 @@ static void lmPasswd_to_key( k[6] = ((lpw[5]&0x3F)<<2) | (lpw[6]>>6); k[7] = ((lpw[6]&0x7F)<<1); +#ifdef HAVE_OPENSSL des_set_odd_parity( key ); +#endif } #define MAX_PWLEN 256 @@ -162,22 +187,46 @@ static void lmhash( ) { char UcasePassword[15]; - des_cblock key; - des_key_schedule schedule; - des_cblock StdText = "KGS!@#$%"; - des_cblock hbuf[2]; + DES_cblock key; + DES_cblock StdText = "KGS!@#$%"; + DES_cblock hbuf[2]; +#ifdef HAVE_OPENSSL + DES_key_schedule schedule; +#elif defined(HAVE_GNUTLS) + gcry_cipher_hd_t h = NULL; + gcry_error_t err; + + err = gcry_cipher_open( &h, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC, 0 ); + if ( err ) return; +#endif strncpy( UcasePassword, passwd->bv_val, 14 ); UcasePassword[14] = '\0'; ldap_pvt_str2upper( UcasePassword ); lmPasswd_to_key( UcasePassword, &key ); +#ifdef HAVE_GNUTLS + err = gcry_cipher_setkey( h, &key, sizeof(key) ); + if ( err == 0 ) { + err = gcry_cipher_encrypt( h, &hbuf[0], sizeof(key), &StdText, sizeof(key) ); + if ( err == 0 ) { + gcry_cipher_reset( h ); + lmPasswd_to_key( &UcasePassword[7], &key ); + err = gcry_cipher_setkey( h, &key, sizeof(key) ); + if ( err == 0 ) { + err = gcry_cipher_encrypt( h, &hbuf[1], sizeof(key), &StdText, sizeof(key) ); + } + } + gcry_cipher_close( h ); + } +#elif defined(HAVE_OPENSSL) des_set_key_unchecked( &key, schedule ); des_ecb_encrypt( &StdText, &hbuf[0], schedule , DES_ENCRYPT ); lmPasswd_to_key( &UcasePassword[7], &key ); des_set_key_unchecked( &key, schedule ); des_ecb_encrypt( &StdText, &hbuf[1], schedule , DES_ENCRYPT ); +#endif hexify( (char *)hbuf, hash ); } @@ -192,14 +241,20 @@ static void nthash( * 256 UCS2 characters, not 256 bytes... */ char hbuf[HASHLEN]; +#ifdef HAVE_OPENSSL MD4_CTX ctx; +#endif if (passwd->bv_len > MAX_PWLEN*2) passwd->bv_len = MAX_PWLEN*2; - + +#ifdef HAVE_OPENSSL MD4_Init( &ctx ); MD4_Update( &ctx, passwd->bv_val, passwd->bv_len ); MD4_Final( (unsigned char *)hbuf, &ctx ); +#elif defined(HAVE_GNUTLS) + gcry_md_hash_buffer(GCRY_MD_MD4, hbuf, passwd->bv_val, passwd->bv_len ); +#endif hexify( hbuf, hash ); } @@ -273,9 +328,9 @@ static int k5key_chk( int rc; Entry *e; Attribute *a; - krb5_error_code ret; - krb5_keyblock key; - krb5_salt salt; + krb5_error_code ret; + krb5_keyblock key; + krb5_salt salt; hdb_entry ent; /* Find our thread context, find our Operation */ @@ -300,6 +355,19 @@ static int k5key_chk( memset( &ent, 0, sizeof(ent) ); ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal); if ( ret ) break; + + a = attr_find( e->e_attrs, ad_krb5ValidEnd ); + if (a) { + struct lutil_tm tm; + struct lutil_timet tt; + if ( lutil_parsetime( a->a_vals[0].bv_val, &tm ) == 0 && + lutil_tm2time( &tm, &tt ) == 0 && tt.tt_usec < op->o_time ) { + /* Account is expired */ + rc = LUTIL_PASSWD_ERR; + break; + } + } + krb5_get_pw_salt( context, ent.principal, &salt ); krb5_free_principal( context, ent.principal ); @@ -350,6 +418,7 @@ static int smbk5pwd_exop_passwd( Modifications *ml; slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; smbk5pwd_t *pi = on->on_bi.bi_private; + char term; /* Not the operation we expected, pass it on... */ if ( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) ) { @@ -360,12 +429,16 @@ static int smbk5pwd_exop_passwd( rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); if ( rc != LDAP_SUCCESS ) return rc; + term = qpw->rs_new.bv_val[qpw->rs_new.bv_len]; + qpw->rs_new.bv_val[qpw->rs_new.bv_len] = '\0'; + #ifdef DO_KRB5 /* Kerberos stuff */ do { krb5_error_code ret; hdb_entry ent; struct berval *keys; + size_t nkeys; int kvno, i; Attribute *a; @@ -396,7 +469,9 @@ static int smbk5pwd_exop_passwd( op->o_log_prefix, e->e_name.bv_val, 0 ); } - ret = _kadm5_set_keys(kadm_context, &ent, qpw->rs_new.bv_val); + ret = hdb_generate_key_set_password(context, ent.principal, + qpw->rs_new.bv_val, &ent.keys.val, &nkeys); + ent.keys.len = nkeys; hdb_seal_keys(context, db, &ent); krb5_free_principal( context, ent.principal ); @@ -415,7 +490,7 @@ static int smbk5pwd_exop_passwd( } BER_BVZERO( &keys[i] ); - _kadm5_free_keys(kadm_context, ent.keys.len, ent.keys.val); + hdb_free_keys(context, ent.keys.len, ent.keys.val); if ( i != ent.keys.len ) { ber_bvarray_free( keys ); @@ -595,7 +670,37 @@ static int smbk5pwd_exop_passwd( } } #endif /* DO_SAMBA */ + +#ifdef DO_SHADOW + /* shadow stuff */ + if ( SMBK5PWD_DO_SHADOW( pi ) && is_entry_objectclass(e, oc_shadowAccount, 0 ) ) { + struct berval *keys; + + ml = ch_malloc(sizeof(Modifications)); + if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next; + ml->sml_next = qpw->rs_mods; + qpw->rs_mods = ml; + + keys = ch_malloc( sizeof(struct berval) * 2); + BER_BVZERO( &keys[1] ); + keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) ); + keys[0].bv_len = snprintf(keys[0].bv_val, + LDAP_PVT_INTTYPE_CHARS(long), + "%ld", (long)(slap_get_time() / (60 * 60 * 24))); + + ml->sml_desc = ad_shadowLastChange; + ml->sml_op = LDAP_MOD_REPLACE; +#ifdef SLAP_MOD_INTERNAL + ml->sml_flags = SLAP_MOD_INTERNAL; +#endif + ml->sml_numvals = 1; + ml->sml_values = keys; + ml->sml_nvalues = NULL; + } +#endif /* DO_SHADOW */ + be_entry_release_r( op, e ); + qpw->rs_new.bv_val[qpw->rs_new.bv_len] = term; return SLAP_CB_CONTINUE; } @@ -656,6 +761,7 @@ static ConfigOCs smbk5pwd_cfocs[] = { static slap_verbmasks smbk5pwd_modules[] = { { BER_BVC( "krb5" ), SMBK5PWD_F_KRB5 }, { BER_BVC( "samba" ), SMBK5PWD_F_SAMBA }, + { BER_BVC( "shadow" ), SMBK5PWD_F_SHADOW }, { BER_BVNULL, -1 } }; @@ -714,10 +820,10 @@ smbk5pwd_cf_func( ConfigArgs *c ) pi->mode = 0; } else { - slap_mask_t m; + int i; - m = verb_to_mask( c->line, smbk5pwd_modules ); - pi->mode &= ~m; + i = verb_to_mask( c->line, smbk5pwd_modules ); + pi->mode &= ~smbk5pwd_modules[i].mask; } break; @@ -766,7 +872,7 @@ smbk5pwd_cf_func( ConfigArgs *c ) break; case PC_SMB_ENABLE: { - slap_mask_t mode = pi->mode, m; + slap_mask_t mode = pi->mode, m = 0; rc = verbs_to_mask( c->argc, c->argv, smbk5pwd_modules, &m ); if ( rc > 0 ) { @@ -801,6 +907,16 @@ smbk5pwd_cf_func( ConfigArgs *c ) } #endif /* ! DO_SAMBA */ +#ifndef DO_SHADOW + if ( SMBK5PWD_DO_SHADOW( pi ) ) { + Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: " + "<%s> module \"%s\" only allowed when compiled with -DDO_SHADOW.\n", + c->log, c->argv[ 0 ], c->argv[ rc ] ); + pi->mode = mode; + return 1; + } +#endif /* ! DO_SHADOW */ + { BackendDB db = *c->be; @@ -835,6 +951,7 @@ smbk5pwd_modules_init( smbk5pwd_t *pi ) { "krb5Key", &ad_krb5Key }, { "krb5KeyVersionNumber", &ad_krb5KeyVersionNumber }, { "krb5PrincipalName", &ad_krb5PrincipalName }, + { "krb5ValidEnd", &ad_krb5ValidEnd }, { NULL } }, #endif /* DO_KRB5 */ @@ -848,6 +965,12 @@ smbk5pwd_modules_init( smbk5pwd_t *pi ) { NULL } }, #endif /* DO_SAMBA */ +#ifdef DO_SHADOW + shadow_ad[] = { + { "shadowLastChange", &ad_shadowLastChange }, + { NULL } + }, +#endif /* DO_SHADOW */ dummy_ad; /* this is to silence the unused var warning */ @@ -903,7 +1026,7 @@ smbk5pwd_modules_init( smbk5pwd_t *pi ) char *err_str, *err_msg = ""; err_str = krb5_get_error_string( context ); if (!err_str) - err_msg = krb5_get_err_text( context, ret ); + err_msg = (char *)krb5_get_err_text( context, ret ); Debug( LDAP_DEBUG_ANY, "smbk5pwd: " "unable to initialize krb5 admin context: %s (%d).\n", err_str ? err_str : err_msg, ret, 0 ); @@ -947,6 +1070,35 @@ smbk5pwd_modules_init( smbk5pwd_t *pi ) } #endif /* DO_SAMBA */ +#ifdef DO_SHADOW + if ( SMBK5PWD_DO_SHADOW( pi ) && oc_shadowAccount == NULL ) { + int i, rc; + + oc_shadowAccount = oc_find( "shadowAccount" ); + if ( !oc_shadowAccount ) { + Debug( LDAP_DEBUG_ANY, "smbk5pwd: " + "unable to find \"shadowAccount\" objectClass.\n", + 0, 0, 0 ); + return -1; + } + + for ( i = 0; shadow_ad[ i ].name != NULL; i++ ) { + const char *text; + + *(shadow_ad[ i ].adp) = NULL; + + rc = slap_str2ad( shadow_ad[ i ].name, shadow_ad[ i ].adp, &text ); + if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ANY, "smbk5pwd: " + "unable to find \"%s\" attributeType: %s (%d).\n", + shadow_ad[ i ].name, text, rc ); + oc_shadowAccount = NULL; + return rc; + } + } + } +#endif /* DO_SHADOW */ + return 0; }