diff -N -r -u contrib/slapd-modules/smbk5pwd/Makefile contrib/slapd-modules/smbk5pwd/Makefile --- contrib/slapd-modules/smbk5pwd/Makefile 1969-12-31 18:00:00.000000000 -0600 +++ contrib/slapd-modules/smbk5pwd/Makefile 2004-04-02 05:06:38.000000000 -0600 @@ -0,0 +1,35 @@ +# $OpenLDAP: pkg/ldap/contrib/slapd-modules/smbk5pwd/Makefile,v 1.1 2004/04/02 11:06:38 hyc Exp $ +# Copyright 2004 Howard Chu, Symas Corp. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted only as authorized by the OpenLDAP +# Public License. +# +# A copy of this license is available in the file LICENSE in the +# top-level directory of the distribution or, alternatively, at +# . + +LIBTOOL=../../../libtool +OPT=-g -O2 +CC=gcc + + +HEIMDAL_INC=-I/usr/include/heimdal +SSL_INC= +LDAP_INC=-I../../../include -I../../../servers/slapd +INCS=$(LDAP_INC) $(HEIMDAL_INC) $(SSL_INC) + +HEIMDAL_LIB=-L/usr/lib -lkrb5 -lkadm5srv +SSL_LIB=-lcrypto +LDAP_LIB=-lldap_r -llber +LIBS=$(LDAP_LIB) $(HEIMDAL_LIB) $(SSL_LIB) + +all: smbk5pwd.la + + +smbk5pwd.lo: smbk5pwd.c + $(LIBTOOL) --mode=compile $(CC) $(OPT) $(DEFS) $(INCS) -c $? + +smbk5pwd.la: smbk5pwd.lo + $(LIBTOOL) --mode=link $(CC) $(OPT) -version-info 0:0:0 \ + -rpath /usr/local/libexec/openldap -module -o $@ $? $(LIBS) diff -N -r -u contrib/slapd-modules/smbk5pwd/README contrib/slapd-modules/smbk5pwd/README --- contrib/slapd-modules/smbk5pwd/README 1969-12-31 18:00:00.000000000 -0600 +++ contrib/slapd-modules/smbk5pwd/README 2005-07-09 23:36:39.000000000 -0500 @@ -0,0 +1,59 @@ +Copyright 2004-2005 Howard Chu, Symas Corp. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +A copy of this license is available in the file LICENSE in the +top-level directory of the distribution or, alternatively, at +. + +This directory contains a slapd overlay, smbk5pwd, that extends the +PasswordModify Extended Operation to update Kerberos keys and Samba +password hashes for an LDAP user. + +The Kerberos support is written for Heimdal using its hdb-ldap backend. +If a PasswordModify is performed on an entry that has the krb5KDCEntry +objectclass, then the krb5Key and krb5KeyVersionNumber will be updated +using the new password in the PasswordModify request. Additionally, a +new "{K5KEY}" password hash mechanism is provided. For krb5KDCEntries that +have this hash specifier in their userPassword attribute, Simple Binds +will be checked against the Kerberos keys of the Entry. No data is +needed after the "{K5KEY}" hash specifier in the userPassword, it is +looked up from the Entry directly. + +The Samba support is written using the Samba 3.0 LDAP schema. If a +PasswordModify is performed on an entry that has the sambaSamAccount +objectclass, then the sambaLMPassword, sambaNTPassword, and sambaPwdLastSet +attributes will be updated accordingly. + +To use the overlay, add: + + include /krb5-kdc.schema + include /samba.schema + + moduleload smbk5pwd.so + ... + + database bdb + ... + overlay smbk5pwd + +to your slapd configuration file. (You should obtain the necessary schema +files from the Heimdal and/or Samba distributions. At this time, there +are several known errors in these schema files that you will have to +correct before they will load in slapd.) + +The provided Makefile builds both Kerberos and Samba support by default. +You must edit the Makefile to insure that the correct include and library +paths are used. You can change the DEFS macro if you only want one or the +other of Kerberos or Samba support. + +This overlay is only set up to be built as a dynamically loaded module. +On most platforms, in order for the module to be usable, all of the +library dependencies must also be available as shared libraries. + +If you need to build the overlay statically, you will have to move it into the +slapd/overlays directory and edit the Makefile and overlays.c to reference +it. You will also have to define SLAPD_OVER_SMBK5PWD to SLAPD_MOD_STATIC, +and add the relevant libraries to the main slapd link command. diff -N -r -u contrib/slapd-modules/smbk5pwd/smbk5pwd.c contrib/slapd-modules/smbk5pwd/smbk5pwd.c --- contrib/slapd-modules/smbk5pwd/smbk5pwd.c 1969-12-31 18:00:00.000000000 -0600 +++ contrib/slapd-modules/smbk5pwd/smbk5pwd.c 2005-08-12 10:25:19.000000000 -0500 @@ -0,0 +1,574 @@ +/* smbk5pwd.c - Overlay for managing Samba and Heimdal passwords */ +/* $OpenLDAP: pkg/ldap/contrib/slapd-modules/smbk5pwd/smbk5pwd.c,v 1.1.2.5 2005/08/12 15:25:19 kurt Exp $ */ +/* + * Copyright 2004-2005 by Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * . + */ + +#include + +#ifndef SLAPD_OVER_SMBK5PWD +#define SLAPD_OVER_SMBK5PWD SLAPD_MOD_DYNAMIC +#endif + +#ifdef SLAPD_OVER_SMBK5PWD + +#include +#include + +#ifdef DO_KRB5 +#include +#include +#include +#include + +/* make ASN1_MALLOC_ENCODE use our allocator */ +#define malloc ch_malloc + +#include +#include +#include + +#ifndef HDB_INTERFACE_VERSION +#define HDB_MASTER_KEY_SET master_key_set +#else +#define HDB_MASTER_KEY_SET hdb_master_key_set +#endif + +static krb5_context context; +static void *kadm_context; +static kadm5_config_params conf; +static HDB *db; + +static AttributeDescription *ad_krb5Key; +static AttributeDescription *ad_krb5KeyVersionNumber; +static AttributeDescription *ad_krb5PrincipalName; +static ObjectClass *oc_krb5KDCEntry; +#endif + +#ifdef DO_SAMBA +#include +#include + +static AttributeDescription *ad_sambaLMPassword; +static AttributeDescription *ad_sambaNTPassword; +static AttributeDescription *ad_sambaPwdLastSet; +static ObjectClass *oc_sambaSamAccount; +#endif + +#if 0 +static void smbk5pwd_destroy() { + kadm5_destroy(kadm_context); + krb5_free_context(context); +} +#endif + +#ifdef DO_SAMBA +static const char hex[] = "0123456789abcdef"; + +/* From liblutil/passwd.c... */ +static void lmPasswd_to_key( + const char *lmPasswd, + des_cblock *key) +{ + const unsigned char *lpw = (const unsigned char *)lmPasswd; + unsigned char *k = (unsigned char *)key; + + /* make room for parity bits */ + k[0] = lpw[0]; + k[1] = ((lpw[0]&0x01)<<7) | (lpw[1]>>1); + k[2] = ((lpw[1]&0x03)<<6) | (lpw[2]>>2); + k[3] = ((lpw[2]&0x07)<<5) | (lpw[3]>>3); + k[4] = ((lpw[3]&0x0F)<<4) | (lpw[4]>>4); + k[5] = ((lpw[4]&0x1F)<<3) | (lpw[5]>>5); + k[6] = ((lpw[5]&0x3F)<<2) | (lpw[6]>>6); + k[7] = ((lpw[6]&0x7F)<<1); + + des_set_odd_parity( key ); +} + +#define MAX_PWLEN 256 +#define HASHLEN 16 + +static void hexify( + const char in[HASHLEN], + struct berval *out +) +{ + int i; + char *a; + unsigned char *b; + + out->bv_val = ch_malloc(HASHLEN*2 + 1); + out->bv_len = HASHLEN*2; + + a = out->bv_val; + b = (unsigned char *)in; + for (i=0; i> 4]; + *a++ = hex[*b++ & 0x0f]; + } + *a++ = '\0'; +} + +static void lmhash( + struct berval *passwd, + struct berval *hash +) +{ + char UcasePassword[15]; + des_cblock key; + des_key_schedule schedule; + des_cblock StdText = "KGS!@#$%"; + des_cblock hbuf[2]; + + strncpy( UcasePassword, passwd->bv_val, 14 ); + UcasePassword[14] = '\0'; + ldap_pvt_str2upper( UcasePassword ); + + lmPasswd_to_key( UcasePassword, &key ); + 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 ); + + hexify( (char *)hbuf, hash ); +} + +static void nthash( + struct berval *passwd, + struct berval *hash +) +{ + /* Windows currently only allows 14 character passwords, but + * may support up to 256 in the future. We assume this means + * 256 UCS2 characters, not 256 bytes... + */ + char hbuf[HASHLEN]; + int i; + MD4_CTX ctx; + + if (passwd->bv_len > MAX_PWLEN*2) + passwd->bv_len = MAX_PWLEN*2; + + MD4_Init( &ctx ); + MD4_Update( &ctx, passwd->bv_val, passwd->bv_len ); + MD4_Final( (unsigned char *)hbuf, &ctx ); + + hexify( hbuf, hash ); +} +#endif /* DO_SAMBA */ + +#ifdef DO_KRB5 + +static int smbk5pwd_op_cleanup( + Operation *op, + SlapReply *rs ) +{ + slap_callback *cb; + + /* clear out the current key */ + ldap_pvt_thread_pool_setkey( op->o_threadctx, smbk5pwd_op_cleanup, + NULL, NULL ); + + /* free the callback */ + cb = op->o_callback; + op->o_callback = cb->sc_next; + op->o_tmpfree( cb, op->o_tmpmemctx ); + return 0; +} + +static int smbk5pwd_op_bind( + Operation *op, + SlapReply *rs ) +{ + /* If this is a simple Bind, stash the Op pointer so our chk + * function can find it. Set a cleanup callback to clear it + * out when the Bind completes. + */ + if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) { + slap_callback *cb; + ldap_pvt_thread_pool_setkey( op->o_threadctx, smbk5pwd_op_cleanup, op, + NULL ); + cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx ); + cb->sc_cleanup = smbk5pwd_op_cleanup; + cb->sc_next = op->o_callback; + op->o_callback = cb; + } + return SLAP_CB_CONTINUE; +} + +static LUTIL_PASSWD_CHK_FUNC k5key_chk; +static LUTIL_PASSWD_HASH_FUNC k5key_hash; +static const struct berval k5key_scheme = BER_BVC("{K5KEY}"); + +/* This password scheme stores no data in the userPassword attribute + * other than the scheme name. It assumes the invoking entry is a + * krb5KDCentry and compares the passed-in credentials against the + * krb5Key attribute. The krb5Key may be multi-valued, but they are + * simply multiple keytypes generated from the same input string, so + * only the first value needs to be compared here. + * + * Since the lutil_passwd API doesn't pass the Entry object in, we + * have to fetch it ourselves in order to get access to the other + * attributes. We accomplish this with the help of the overlay's Bind + * function, which stores the current Operation pointer in thread-specific + * storage so we can retrieve it here. The Operation provides all + * the necessary context for us to get Entry from the database. + */ +static int k5key_chk( + const struct berval *sc, + const struct berval *passwd, + const struct berval *cred, + const char **text ) +{ + void *ctx; + Operation *op; + int rc; + Entry *e; + Attribute *a; + krb5_error_code ret; + krb5_keyblock key; + krb5_salt salt; + hdb_entry ent; + + /* Find our thread context, find our Operation */ + ctx = ldap_pvt_thread_pool_context(); + + if ( ldap_pvt_thread_pool_getkey( ctx, smbk5pwd_op_cleanup, (void **)&op, NULL ) || + !op ) + return LUTIL_PASSWD_ERR; + + rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); + if ( rc != LDAP_SUCCESS ) return LUTIL_PASSWD_ERR; + + rc = LUTIL_PASSWD_ERR; + do { + size_t l; + Key ekey = {0}; + + a = attr_find( e->e_attrs, ad_krb5PrincipalName ); + if (!a ) break; + + memset( &ent, 0, sizeof(ent) ); + ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal); + if ( ret ) break; + krb5_get_pw_salt( context, ent.principal, &salt ); + krb5_free_principal( context, ent.principal ); + + a = attr_find( e->e_attrs, ad_krb5Key ); + if ( !a ) break; + + ent.keys.len = 1; + ent.keys.val = &ekey; + decode_Key((unsigned char *) a->a_vals[0].bv_val, + (size_t) a->a_vals[0].bv_len, &ent.keys.val[0], &l); + if ( db->HDB_MASTER_KEY_SET ) + hdb_unseal_keys( context, db, &ent ); + + krb5_string_to_key_salt( context, ekey.key.keytype, cred->bv_val, + salt, &key ); + + krb5_free_salt( context, salt ); + + if ( memcmp( ekey.key.keyvalue.data, key.keyvalue.data, + key.keyvalue.length ) == 0 ) rc = LUTIL_PASSWD_OK; + + krb5_free_keyblock_contents( context, &key ); + krb5_free_keyblock_contents( context, &ekey.key ); + + } while(0); + be_entry_release_r( op, e ); + return rc; +} + +static int k5key_hash( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + ber_dupbv( hash, (struct berval *)&k5key_scheme ); + return LUTIL_PASSWD_OK; +} +#endif /* DO_KRB5 */ + +static int smbk5pwd_exop_passwd( + Operation *op, + SlapReply *rs ) +{ + int i, rc; + req_pwdexop_s *qpw = &op->oq_pwdexop; + Entry *e; + Attribute *a; + Modifications *ml; + slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; + + /* Not the operation we expected, pass it on... */ + if ( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) ) { + return SLAP_CB_CONTINUE; + } + + op->o_bd->bd_info = (BackendInfo *)on->on_info; + rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); + if ( rc != LDAP_SUCCESS ) return rc; + +#ifdef DO_KRB5 + /* Kerberos stuff */ + do { + krb5_error_code ret; + hdb_entry ent; + struct berval *keys; + int kvno; + + if ( !is_entry_objectclass(e, oc_krb5KDCEntry, 0 ) ) break; + + a = attr_find( e->e_attrs, ad_krb5PrincipalName ); + if ( !a ) break; + + 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_krb5KeyVersionNumber ); + if ( a ) { + kvno = atoi(a->a_vals[0].bv_val); + } else { + /* shouldn't happen, this is a required attr */ + kvno = 0; + } + + ret = _kadm5_set_keys(kadm_context, &ent, qpw->rs_new.bv_val); + hdb_seal_keys(context, db, &ent); + krb5_free_principal( context, ent.principal ); + + keys = ch_malloc( (ent.keys.len + 1) * sizeof(struct berval)); + + for (i = 0; i < ent.keys.len; i++) { + unsigned char *buf; + size_t len; + + ASN1_MALLOC_ENCODE(Key, buf, len, &ent.keys.val[i], &len, ret); + if (ret != 0) + break; + + keys[i].bv_val = (char *)buf; + keys[i].bv_len = len; + } + keys[i].bv_val = NULL; + keys[i].bv_len = 0; + + _kadm5_free_keys(kadm_context, ent.keys.len, ent.keys.val); + + if ( i != ent.keys.len ) { + ber_bvarray_free( keys ); + break; + } + + 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; + + ml->sml_desc = ad_krb5Key; + ml->sml_op = LDAP_MOD_REPLACE; +#ifdef SLAP_MOD_INTERNAL + ml->sml_flags = SLAP_MOD_INTERNAL; +#endif + ml->sml_values = keys; + ml->sml_nvalues = NULL; + + ml = ch_malloc(sizeof(Modifications)); + ml->sml_next = qpw->rs_mods; + qpw->rs_mods = ml; + + ml->sml_desc = ad_krb5KeyVersionNumber; + ml->sml_op = LDAP_MOD_REPLACE; +#ifdef SLAP_MOD_INTERNAL + ml->sml_flags = SLAP_MOD_INTERNAL; +#endif + ml->sml_values = ch_malloc( 2 * sizeof(struct berval)); + ml->sml_values[0].bv_val = ch_malloc( 64 ); + ml->sml_values[0].bv_len = sprintf(ml->sml_values[0].bv_val, + "%d", kvno+1 ); + ml->sml_values[1].bv_val = NULL; + ml->sml_values[1].bv_len = 0; + ml->sml_nvalues = NULL; + } while(0); +#endif /* DO_KRB5 */ + +#ifdef DO_SAMBA + /* Samba stuff */ + if ( is_entry_objectclass(e, oc_sambaSamAccount, 0 ) ) { + struct berval *keys; + ber_len_t j,l; + wchar_t *wcs, wc; + char *c, *d; + struct berval pwd; + + /* Expand incoming UTF8 string to UCS4 */ + l = ldap_utf8_chars(qpw->rs_new.bv_val); + wcs = ch_malloc((l+1) * sizeof(wchar_t)); + + ldap_x_utf8s_to_wcs( wcs, qpw->rs_new.bv_val, l ); + + /* Truncate UCS4 to UCS2 */ + c = (char *)wcs; + for (j=0; j> 8) & 0xff; + } + *c++ = 0; + pwd.bv_val = (char *)wcs; + pwd.bv_len = l * 2; + + 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( 2 * sizeof(struct berval) ); + keys[1].bv_val = NULL; + keys[1].bv_len = 0; + nthash( &pwd, keys ); + + ml->sml_desc = ad_sambaNTPassword; + ml->sml_op = LDAP_MOD_REPLACE; +#ifdef SLAP_MOD_INTERNAL + ml->sml_flags = SLAP_MOD_INTERNAL; +#endif + ml->sml_values = keys; + ml->sml_nvalues = NULL; + + /* Truncate UCS2 to 8-bit ASCII */ + c = pwd.bv_val+1; + d = pwd.bv_val+2; + for (j=1; jsml_next = qpw->rs_mods; + qpw->rs_mods = ml; + + keys = ch_malloc( 2 * sizeof(struct berval) ); + keys[1].bv_val = NULL; + keys[1].bv_len = 0; + lmhash( &pwd, keys ); + + ml->sml_desc = ad_sambaLMPassword; + ml->sml_op = LDAP_MOD_REPLACE; +#ifdef SLAP_MOD_INTERNAL + ml->sml_flags = SLAP_MOD_INTERNAL; +#endif + ml->sml_values = keys; + ml->sml_nvalues = NULL; + + ch_free(wcs); + + ml = ch_malloc(sizeof(Modifications)); + ml->sml_next = qpw->rs_mods; + qpw->rs_mods = ml; + + keys = ch_malloc( 2 * sizeof(struct berval) ); + keys[1].bv_val = NULL; + keys[1].bv_len = 0; + keys[0].bv_val = ch_malloc(16); + keys[0].bv_len = sprintf(keys[0].bv_val, "%d", + slap_get_time()); + + ml->sml_desc = ad_sambaPwdLastSet; + ml->sml_op = LDAP_MOD_REPLACE; +#ifdef SLAP_MOD_INTERNAL + ml->sml_flags = SLAP_MOD_INTERNAL; +#endif + ml->sml_values = keys; + ml->sml_nvalues = NULL; + } +#endif /* DO_SAMBA */ + be_entry_release_r( op, e ); + + return SLAP_CB_CONTINUE; +} + +static slap_overinst smbk5pwd; + +int smbk5pwd_init() { + int rc; + const char *text; + +#ifdef DO_KRB5 + krb5_error_code ret; + extern HDB * _kadm5_s_get_db(void *); + + /* Make sure all of our necessary schema items are loaded */ + oc_krb5KDCEntry = oc_find("krb5KDCEntry"); + if ( !oc_krb5KDCEntry ) return -1; + + rc = slap_str2ad( "krb5Key", &ad_krb5Key, &text ); + if ( rc ) return rc; + rc = slap_str2ad( "krb5KeyVersionNumber", &ad_krb5KeyVersionNumber, &text ); + if ( rc ) return rc; + rc = slap_str2ad( "krb5PrincipalName", &ad_krb5PrincipalName, &text ); + if ( rc ) return rc; + + /* Initialize Kerberos context */ + ret = krb5_init_context(&context); + if (ret) { + return -1; + } + + ret = kadm5_s_init_with_password_ctx( context, + KADM5_ADMIN_SERVICE, + NULL, + KADM5_ADMIN_SERVICE, + &conf, 0, 0, &kadm_context ); + + db = _kadm5_s_get_db(kadm_context); +#endif /* DO_KRB5 */ + +#ifdef DO_SAMBA + oc_sambaSamAccount = oc_find("sambaSamAccount"); + if ( !oc_sambaSamAccount ) return -1; + + rc = slap_str2ad( "sambaLMPassword", &ad_sambaLMPassword, &text ); + if ( rc ) return rc; + rc = slap_str2ad( "sambaNTPassword", &ad_sambaNTPassword, &text ); + if ( rc ) return rc; + rc = slap_str2ad( "sambaPwdLastSet", &ad_sambaPwdLastSet, &text ); + if ( rc ) return rc; +#endif /* DO_SAMBA */ + + smbk5pwd.on_bi.bi_type = "smbk5pwd"; + smbk5pwd.on_bi.bi_extended = smbk5pwd_exop_passwd; + +#ifdef DO_KRB5 + smbk5pwd.on_bi.bi_op_bind = smbk5pwd_op_bind; + + lutil_passwd_add( (struct berval *)&k5key_scheme, k5key_chk, k5key_hash ); +#endif + + return overlay_register( &smbk5pwd ); +} + +#if SLAPD_OVER_SMBK5PWD == SLAPD_MOD_DYNAMIC +int init_module(int argc, char *argv[]) { + return smbk5pwd_init(); +} +#endif + +#endif /* defined(SLAPD_OVER_SMBK5PWD) */