Back to index

openldap  2.4.31
smbk5pwd.c
Go to the documentation of this file.
00001 /* smbk5pwd.c - Overlay for managing Samba and Heimdal passwords */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2004-2012 The OpenLDAP Foundation.
00006  * Portions Copyright 2004-2005 by Howard Chu, Symas Corp.
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 /* ACKNOWLEDGEMENTS:
00018  * Support for table-driven configuration added by Pierangelo Masarati.
00019  * Support for sambaPwdMustChange and sambaPwdCanChange added by Marco D'Ettorre.
00020  * Support for shadowLastChange added by SATOH Fumiyasu @ OSS Technology, Inc.
00021  */
00022 
00023 #include <portable.h>
00024 
00025 #ifndef SLAPD_OVER_SMBK5PWD
00026 #define SLAPD_OVER_SMBK5PWD SLAPD_MOD_DYNAMIC
00027 #endif
00028 
00029 #ifdef SLAPD_OVER_SMBK5PWD
00030 
00031 #include <slap.h>
00032 #include <ac/errno.h>
00033 #include <ac/string.h>
00034 
00035 #include "config.h"
00036 
00037 #ifdef DO_KRB5
00038 #include <lber.h>
00039 #include <lber_pvt.h>
00040 #include <lutil.h>
00041 
00042 /* make ASN1_MALLOC_ENCODE use our allocator */
00043 #define malloc       ch_malloc
00044 
00045 #include <krb5.h>
00046 #include <kadm5/admin.h>
00047 #include <hdb.h>
00048 
00049 #ifndef HDB_INTERFACE_VERSION
00050 #define       HDB_MASTER_KEY_SET   master_key_set
00051 #else
00052 #define       HDB_MASTER_KEY_SET   hdb_master_key_set
00053 #endif
00054 
00055 static krb5_context context;
00056 static void *kadm_context;
00057 static kadm5_config_params conf;
00058 static HDB *db;
00059 
00060 static AttributeDescription *ad_krb5Key;
00061 static AttributeDescription *ad_krb5KeyVersionNumber;
00062 static AttributeDescription *ad_krb5PrincipalName;
00063 static AttributeDescription *ad_krb5ValidEnd;
00064 static ObjectClass *oc_krb5KDCEntry;
00065 #endif
00066 
00067 #ifdef DO_SAMBA
00068 #ifdef HAVE_GNUTLS
00069 #include <gcrypt.h>
00070 typedef unsigned char DES_cblock[8];
00071 #else
00072 #include <openssl/des.h>
00073 #include <openssl/md4.h>
00074 #endif
00075 #include "ldap_utf8.h"
00076 
00077 static AttributeDescription *ad_sambaLMPassword;
00078 static AttributeDescription *ad_sambaNTPassword;
00079 static AttributeDescription *ad_sambaPwdLastSet;
00080 static AttributeDescription *ad_sambaPwdMustChange;
00081 static AttributeDescription *ad_sambaPwdCanChange;
00082 static ObjectClass *oc_sambaSamAccount;
00083 #endif
00084 
00085 #ifdef DO_SHADOW
00086 static AttributeDescription *ad_shadowLastChange;
00087 static ObjectClass *oc_shadowAccount;
00088 #endif
00089 
00090 /* Per-instance configuration information */
00091 typedef struct smbk5pwd_t {
00092        unsigned      mode;
00093 #define       SMBK5PWD_F_KRB5             (0x1U)
00094 #define       SMBK5PWD_F_SAMBA     (0x2U)
00095 #define       SMBK5PWD_F_SHADOW    (0x4U)
00096 
00097 #define SMBK5PWD_DO_KRB5(pi)       ((pi)->mode & SMBK5PWD_F_KRB5)
00098 #define SMBK5PWD_DO_SAMBA(pi)      ((pi)->mode & SMBK5PWD_F_SAMBA)
00099 #define SMBK5PWD_DO_SHADOW(pi)     ((pi)->mode & SMBK5PWD_F_SHADOW)
00100 
00101 #ifdef DO_KRB5
00102        /* nothing yet */
00103 #endif
00104 
00105 #ifdef DO_SAMBA
00106        /* How many seconds before forcing a password change? */
00107        time_t smb_must_change;
00108        /* How many seconds after allowing a password change? */
00109        time_t  smb_can_change;
00110 #endif
00111 
00112 #ifdef DO_SHADOW
00113        /* nothing yet */
00114 #endif
00115 } smbk5pwd_t;
00116 
00117 static const unsigned SMBK5PWD_F_ALL      =
00118        0
00119 #ifdef DO_KRB5
00120        | SMBK5PWD_F_KRB5
00121 #endif
00122 #ifdef DO_SAMBA
00123        | SMBK5PWD_F_SAMBA
00124 #endif
00125 #ifdef DO_SHADOW
00126        | SMBK5PWD_F_SHADOW
00127 #endif
00128 ;
00129 
00130 static int smbk5pwd_modules_init( smbk5pwd_t *pi );
00131 
00132 #ifdef DO_SAMBA
00133 static const char hex[] = "0123456789abcdef";
00134 
00135 /* From liblutil/passwd.c... */
00136 static void lmPasswd_to_key(
00137        const char *lmPasswd,
00138        DES_cblock *key)
00139 {
00140        const unsigned char *lpw = (const unsigned char *)lmPasswd;
00141        unsigned char *k = (unsigned char *)key;
00142 
00143        /* make room for parity bits */
00144        k[0] = lpw[0];
00145        k[1] = ((lpw[0]&0x01)<<7) | (lpw[1]>>1);
00146        k[2] = ((lpw[1]&0x03)<<6) | (lpw[2]>>2);
00147        k[3] = ((lpw[2]&0x07)<<5) | (lpw[3]>>3);
00148        k[4] = ((lpw[3]&0x0F)<<4) | (lpw[4]>>4);
00149        k[5] = ((lpw[4]&0x1F)<<3) | (lpw[5]>>5);
00150        k[6] = ((lpw[5]&0x3F)<<2) | (lpw[6]>>6);
00151        k[7] = ((lpw[6]&0x7F)<<1);
00152 
00153 #ifdef HAVE_OPENSSL
00154        des_set_odd_parity( key );
00155 #endif
00156 }
00157 
00158 #define MAX_PWLEN 256
00159 #define       HASHLEN       16
00160 
00161 static void hexify(
00162        const char in[HASHLEN],
00163        struct berval *out
00164 )
00165 {
00166        int i;
00167        char *a;
00168        unsigned char *b;
00169 
00170        out->bv_val = ch_malloc(HASHLEN*2 + 1);
00171        out->bv_len = HASHLEN*2;
00172 
00173        a = out->bv_val;
00174        b = (unsigned char *)in;
00175        for (i=0; i<HASHLEN; i++) {
00176               *a++ = hex[*b >> 4];
00177               *a++ = hex[*b++ & 0x0f];
00178        }
00179        *a++ = '\0';
00180 }
00181 
00182 static void lmhash(
00183        struct berval *passwd,
00184        struct berval *hash
00185 )
00186 {
00187        char UcasePassword[15];
00188        DES_cblock key;
00189        DES_cblock StdText = "KGS!@#$%";
00190        DES_cblock hbuf[2];
00191 #ifdef HAVE_OPENSSL
00192        DES_key_schedule schedule;
00193 #elif defined(HAVE_GNUTLS)
00194        gcry_cipher_hd_t h = NULL;
00195        gcry_error_t err;
00196 
00197        err = gcry_cipher_open( &h, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC, 0 );
00198        if ( err ) return;
00199 #endif
00200 
00201        strncpy( UcasePassword, passwd->bv_val, 14 );
00202        UcasePassword[14] = '\0';
00203        ldap_pvt_str2upper( UcasePassword );
00204 
00205        lmPasswd_to_key( UcasePassword, &key );
00206 #ifdef HAVE_GNUTLS
00207        err = gcry_cipher_setkey( h, &key, sizeof(key) );
00208        if ( err == 0 ) {
00209               err = gcry_cipher_encrypt( h, &hbuf[0], sizeof(key), &StdText, sizeof(key) );
00210               if ( err == 0 ) {
00211                      gcry_cipher_reset( h );
00212                      lmPasswd_to_key( &UcasePassword[7], &key );
00213                      err = gcry_cipher_setkey( h, &key, sizeof(key) );
00214                      if ( err == 0 ) {
00215                             err = gcry_cipher_encrypt( h, &hbuf[1], sizeof(key), &StdText, sizeof(key) );
00216                      }
00217               }
00218               gcry_cipher_close( h );
00219        }
00220 #elif defined(HAVE_OPENSSL)
00221        des_set_key_unchecked( &key, schedule );
00222        des_ecb_encrypt( &StdText, &hbuf[0], schedule , DES_ENCRYPT );
00223 
00224        lmPasswd_to_key( &UcasePassword[7], &key );
00225        des_set_key_unchecked( &key, schedule );
00226        des_ecb_encrypt( &StdText, &hbuf[1], schedule , DES_ENCRYPT );
00227 #endif
00228 
00229        hexify( (char *)hbuf, hash );
00230 }
00231 
00232 static void nthash(
00233        struct berval *passwd,
00234        struct berval *hash
00235 )
00236 {
00237        /* Windows currently only allows 14 character passwords, but
00238         * may support up to 256 in the future. We assume this means
00239         * 256 UCS2 characters, not 256 bytes...
00240         */
00241        char hbuf[HASHLEN];
00242 #ifdef HAVE_OPENSSL
00243        MD4_CTX ctx;
00244 #endif
00245 
00246        if (passwd->bv_len > MAX_PWLEN*2)
00247               passwd->bv_len = MAX_PWLEN*2;
00248 
00249 #ifdef HAVE_OPENSSL
00250        MD4_Init( &ctx );
00251        MD4_Update( &ctx, passwd->bv_val, passwd->bv_len );
00252        MD4_Final( (unsigned char *)hbuf, &ctx );
00253 #elif defined(HAVE_GNUTLS)
00254        gcry_md_hash_buffer(GCRY_MD_MD4, hbuf, passwd->bv_val, passwd->bv_len );
00255 #endif
00256 
00257        hexify( hbuf, hash );
00258 }
00259 #endif /* DO_SAMBA */
00260 
00261 #ifdef DO_KRB5
00262 
00263 static int smbk5pwd_op_cleanup(
00264        Operation *op,
00265        SlapReply *rs )
00266 {
00267        slap_callback *cb;
00268 
00269        /* clear out the current key */
00270        ldap_pvt_thread_pool_setkey( op->o_threadctx, smbk5pwd_op_cleanup,
00271               NULL, 0, NULL, NULL );
00272 
00273        /* free the callback */
00274        cb = op->o_callback;
00275        op->o_callback = cb->sc_next;
00276        op->o_tmpfree( cb, op->o_tmpmemctx );
00277        return 0;
00278 }
00279 
00280 static int smbk5pwd_op_bind(
00281        Operation *op,
00282        SlapReply *rs )
00283 {
00284        /* If this is a simple Bind, stash the Op pointer so our chk
00285         * function can find it. Set a cleanup callback to clear it
00286         * out when the Bind completes.
00287         */
00288        if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) {
00289               slap_callback *cb;
00290               ldap_pvt_thread_pool_setkey( op->o_threadctx,
00291                      smbk5pwd_op_cleanup, op, 0, NULL, NULL );
00292               cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
00293               cb->sc_cleanup = smbk5pwd_op_cleanup;
00294               cb->sc_next = op->o_callback;
00295               op->o_callback = cb;
00296        }
00297        return SLAP_CB_CONTINUE;
00298 }
00299 
00300 static LUTIL_PASSWD_CHK_FUNC k5key_chk;
00301 static LUTIL_PASSWD_HASH_FUNC k5key_hash;
00302 static const struct berval k5key_scheme = BER_BVC("{K5KEY}");
00303 
00304 /* This password scheme stores no data in the userPassword attribute
00305  * other than the scheme name. It assumes the invoking entry is a
00306  * krb5KDCentry and compares the passed-in credentials against the
00307  * krb5Key attribute. The krb5Key may be multi-valued, but they are
00308  * simply multiple keytypes generated from the same input string, so
00309  * only the first value needs to be compared here.
00310  *
00311  * Since the lutil_passwd API doesn't pass the Entry object in, we
00312  * have to fetch it ourselves in order to get access to the other
00313  * attributes. We accomplish this with the help of the overlay's Bind
00314  * function, which stores the current Operation pointer in thread-specific
00315  * storage so we can retrieve it here. The Operation provides all
00316  * the necessary context for us to get Entry from the database.
00317  */
00318 static int k5key_chk(
00319        const struct berval *sc,
00320        const struct berval *passwd,
00321        const struct berval *cred,
00322        const char **text )
00323 {
00324        void *ctx, *op_tmp;
00325        Operation *op;
00326        int rc;
00327        Entry *e;
00328        Attribute *a;
00329        krb5_error_code ret;
00330        krb5_keyblock key;
00331        krb5_salt salt;
00332        hdb_entry ent;
00333 
00334        /* Find our thread context, find our Operation */
00335        ctx = ldap_pvt_thread_pool_context();
00336 
00337        if ( ldap_pvt_thread_pool_getkey( ctx, smbk5pwd_op_cleanup, &op_tmp, NULL )
00338                || !op_tmp )
00339               return LUTIL_PASSWD_ERR;
00340        op = op_tmp;
00341 
00342        rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
00343        if ( rc != LDAP_SUCCESS ) return LUTIL_PASSWD_ERR;
00344 
00345        rc = LUTIL_PASSWD_ERR;
00346        do {
00347               size_t l;
00348               Key ekey = {0};
00349 
00350               a = attr_find( e->e_attrs, ad_krb5PrincipalName );
00351               if (!a ) break;
00352 
00353               memset( &ent, 0, sizeof(ent) );
00354               ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal);
00355               if ( ret ) break;
00356 
00357               a = attr_find( e->e_attrs, ad_krb5ValidEnd );
00358               if (a) {
00359                      struct lutil_tm tm;
00360                      struct lutil_timet tt;
00361                      if ( lutil_parsetime( a->a_vals[0].bv_val, &tm ) == 0 &&
00362                             lutil_tm2time( &tm, &tt ) == 0 && tt.tt_usec < op->o_time ) {
00363                             /* Account is expired */
00364                             rc = LUTIL_PASSWD_ERR;
00365                             break;
00366                      }
00367               }
00368 
00369               krb5_get_pw_salt( context, ent.principal, &salt );
00370               krb5_free_principal( context, ent.principal );
00371 
00372               a = attr_find( e->e_attrs, ad_krb5Key );
00373               if ( !a ) break;
00374 
00375               ent.keys.len = 1;
00376               ent.keys.val = &ekey;
00377               decode_Key((unsigned char *) a->a_vals[0].bv_val,
00378                      (size_t) a->a_vals[0].bv_len, &ent.keys.val[0], &l);
00379               if ( db->HDB_MASTER_KEY_SET )
00380                      hdb_unseal_keys( context, db, &ent );
00381 
00382               krb5_string_to_key_salt( context, ekey.key.keytype, cred->bv_val,
00383                      salt, &key );
00384 
00385               krb5_free_salt( context, salt );
00386 
00387               if ( memcmp( ekey.key.keyvalue.data, key.keyvalue.data,
00388                      key.keyvalue.length ) == 0 ) rc = LUTIL_PASSWD_OK;
00389 
00390               krb5_free_keyblock_contents( context, &key );
00391               krb5_free_keyblock_contents( context, &ekey.key );
00392 
00393        } while(0);
00394        be_entry_release_r( op, e );
00395        return rc;
00396 }
00397 
00398 static int k5key_hash(
00399        const struct berval *scheme,
00400        const struct berval *passwd,
00401        struct berval *hash,
00402        const char **text )
00403 {
00404        ber_dupbv( hash, (struct berval *)&k5key_scheme );
00405        return LUTIL_PASSWD_OK;
00406 }
00407 #endif /* DO_KRB5 */
00408 
00409 static int smbk5pwd_exop_passwd(
00410        Operation *op,
00411        SlapReply *rs )
00412 {
00413        int rc;
00414        req_pwdexop_s *qpw = &op->oq_pwdexop;
00415        Entry *e;
00416        Modifications *ml;
00417        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00418        smbk5pwd_t *pi = on->on_bi.bi_private;
00419        char term;
00420 
00421        /* Not the operation we expected, pass it on... */
00422        if ( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) ) {
00423               return SLAP_CB_CONTINUE;
00424        }
00425 
00426        op->o_bd->bd_info = (BackendInfo *)on->on_info;
00427        rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
00428        if ( rc != LDAP_SUCCESS ) return rc;
00429 
00430        term = qpw->rs_new.bv_val[qpw->rs_new.bv_len];
00431        qpw->rs_new.bv_val[qpw->rs_new.bv_len] = '\0';
00432 
00433 #ifdef DO_KRB5
00434        /* Kerberos stuff */
00435        do {
00436               krb5_error_code ret;
00437               hdb_entry ent;
00438               struct berval *keys;
00439               size_t nkeys;
00440               int kvno, i;
00441               Attribute *a;
00442 
00443               if ( !SMBK5PWD_DO_KRB5( pi ) ) break;
00444 
00445               if ( !is_entry_objectclass(e, oc_krb5KDCEntry, 0 ) ) break;
00446 
00447               a = attr_find( e->e_attrs, ad_krb5PrincipalName );
00448               if ( !a ) break;
00449 
00450               memset( &ent, 0, sizeof(ent) );
00451               ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal);
00452               if ( ret ) break;
00453 
00454               a = attr_find( e->e_attrs, ad_krb5KeyVersionNumber );
00455               kvno = 0;
00456               if ( a ) {
00457                      if ( lutil_atoi( &kvno, a->a_vals[0].bv_val ) != 0 ) {
00458                             Debug( LDAP_DEBUG_ANY, "%s smbk5pwd EXOP: "
00459                                    "dn=\"%s\" unable to parse krb5KeyVersionNumber=\"%s\"\n",
00460                                    op->o_log_prefix, e->e_name.bv_val, a->a_vals[0].bv_val );
00461                      }
00462 
00463               } else {
00464                      /* shouldn't happen, this is a required attr */
00465                      Debug( LDAP_DEBUG_ANY, "%s smbk5pwd EXOP: "
00466                             "dn=\"%s\" missing krb5KeyVersionNumber\n",
00467                             op->o_log_prefix, e->e_name.bv_val, 0 );
00468               }
00469 
00470               ret = hdb_generate_key_set_password(context, ent.principal,
00471                      qpw->rs_new.bv_val, &ent.keys.val, &nkeys);
00472               ent.keys.len = nkeys;
00473               hdb_seal_keys(context, db, &ent);
00474               krb5_free_principal( context, ent.principal );
00475 
00476               keys = ch_malloc( (ent.keys.len + 1) * sizeof(struct berval));
00477 
00478               for (i = 0; i < ent.keys.len; i++) {
00479                      unsigned char *buf;
00480                      size_t len;
00481 
00482                      ASN1_MALLOC_ENCODE(Key, buf, len, &ent.keys.val[i], &len, ret);
00483                      if (ret != 0)
00484                             break;
00485                      
00486                      keys[i].bv_val = (char *)buf;
00487                      keys[i].bv_len = len;
00488               }
00489               BER_BVZERO( &keys[i] );
00490 
00491               hdb_free_keys(context, ent.keys.len, ent.keys.val);
00492 
00493               if ( i != ent.keys.len ) {
00494                      ber_bvarray_free( keys );
00495                      break;
00496               }
00497 
00498               ml = ch_malloc(sizeof(Modifications));
00499               if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next;
00500               ml->sml_next = qpw->rs_mods;
00501               qpw->rs_mods = ml;
00502 
00503               ml->sml_desc = ad_krb5Key;
00504               ml->sml_op = LDAP_MOD_REPLACE;
00505 #ifdef SLAP_MOD_INTERNAL
00506               ml->sml_flags = SLAP_MOD_INTERNAL;
00507 #endif
00508               ml->sml_numvals = i;
00509               ml->sml_values = keys;
00510               ml->sml_nvalues = NULL;
00511               
00512               ml = ch_malloc(sizeof(Modifications));
00513               ml->sml_next = qpw->rs_mods;
00514               qpw->rs_mods = ml;
00515               
00516               ml->sml_desc = ad_krb5KeyVersionNumber;
00517               ml->sml_op = LDAP_MOD_REPLACE;
00518 #ifdef SLAP_MOD_INTERNAL
00519               ml->sml_flags = SLAP_MOD_INTERNAL;
00520 #endif
00521               ml->sml_numvals = 1;
00522               ml->sml_values = ch_malloc( 2 * sizeof(struct berval));
00523               ml->sml_values[0].bv_val = ch_malloc( 64 );
00524               ml->sml_values[0].bv_len = sprintf(ml->sml_values[0].bv_val,
00525                      "%d", kvno+1 );
00526               BER_BVZERO( &ml->sml_values[1] );
00527               ml->sml_nvalues = NULL;
00528        } while ( 0 );
00529 #endif /* DO_KRB5 */
00530 
00531 #ifdef DO_SAMBA
00532        /* Samba stuff */
00533        if ( SMBK5PWD_DO_SAMBA( pi ) && is_entry_objectclass(e, oc_sambaSamAccount, 0 ) ) {
00534               struct berval *keys;
00535               ber_len_t j,l;
00536               wchar_t *wcs, wc;
00537               char *c, *d;
00538               struct berval pwd;
00539               
00540               /* Expand incoming UTF8 string to UCS4 */
00541               l = ldap_utf8_chars(qpw->rs_new.bv_val);
00542               wcs = ch_malloc((l+1) * sizeof(wchar_t));
00543 
00544               ldap_x_utf8s_to_wcs( wcs, qpw->rs_new.bv_val, l );
00545               
00546               /* Truncate UCS4 to UCS2 */
00547               c = (char *)wcs;
00548               for (j=0; j<l; j++) {
00549                      wc = wcs[j];
00550                      *c++ = wc & 0xff;
00551                      *c++ = (wc >> 8) & 0xff;
00552               }
00553               *c++ = 0;
00554               pwd.bv_val = (char *)wcs;
00555               pwd.bv_len = l * 2;
00556 
00557               ml = ch_malloc(sizeof(Modifications));
00558               if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next;
00559               ml->sml_next = qpw->rs_mods;
00560               qpw->rs_mods = ml;
00561 
00562               keys = ch_malloc( 2 * sizeof(struct berval) );
00563               BER_BVZERO( &keys[1] );
00564               nthash( &pwd, keys );
00565               
00566               ml->sml_desc = ad_sambaNTPassword;
00567               ml->sml_op = LDAP_MOD_REPLACE;
00568 #ifdef SLAP_MOD_INTERNAL
00569               ml->sml_flags = SLAP_MOD_INTERNAL;
00570 #endif
00571               ml->sml_numvals = 1;
00572               ml->sml_values = keys;
00573               ml->sml_nvalues = NULL;
00574 
00575               /* Truncate UCS2 to 8-bit ASCII */
00576               c = pwd.bv_val+1;
00577               d = pwd.bv_val+2;
00578               for (j=1; j<l; j++) {
00579                      *c++ = *d++;
00580                      d++;
00581               }
00582               pwd.bv_len /= 2;
00583               pwd.bv_val[pwd.bv_len] = '\0';
00584 
00585               ml = ch_malloc(sizeof(Modifications));
00586               ml->sml_next = qpw->rs_mods;
00587               qpw->rs_mods = ml;
00588 
00589               keys = ch_malloc( 2 * sizeof(struct berval) );
00590               BER_BVZERO( &keys[1] );
00591               lmhash( &pwd, keys );
00592               
00593               ml->sml_desc = ad_sambaLMPassword;
00594               ml->sml_op = LDAP_MOD_REPLACE;
00595 #ifdef SLAP_MOD_INTERNAL
00596               ml->sml_flags = SLAP_MOD_INTERNAL;
00597 #endif
00598               ml->sml_numvals = 1;
00599               ml->sml_values = keys;
00600               ml->sml_nvalues = NULL;
00601 
00602               ch_free(wcs);
00603 
00604               ml = ch_malloc(sizeof(Modifications));
00605               ml->sml_next = qpw->rs_mods;
00606               qpw->rs_mods = ml;
00607 
00608               keys = ch_malloc( 2 * sizeof(struct berval) );
00609               keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) );
00610               keys[0].bv_len = snprintf(keys[0].bv_val,
00611                      LDAP_PVT_INTTYPE_CHARS(long),
00612                      "%ld", slap_get_time());
00613               BER_BVZERO( &keys[1] );
00614               
00615               ml->sml_desc = ad_sambaPwdLastSet;
00616               ml->sml_op = LDAP_MOD_REPLACE;
00617 #ifdef SLAP_MOD_INTERNAL
00618               ml->sml_flags = SLAP_MOD_INTERNAL;
00619 #endif
00620               ml->sml_numvals = 1;
00621               ml->sml_values = keys;
00622               ml->sml_nvalues = NULL;
00623 
00624               if (pi->smb_must_change)
00625               {
00626                      ml = ch_malloc(sizeof(Modifications));
00627                      ml->sml_next = qpw->rs_mods;
00628                      qpw->rs_mods = ml;
00629 
00630                      keys = ch_malloc( 2 * sizeof(struct berval) );
00631                      keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) );
00632                      keys[0].bv_len = snprintf(keys[0].bv_val,
00633                                    LDAP_PVT_INTTYPE_CHARS(long),
00634                                    "%ld", slap_get_time() + pi->smb_must_change);
00635                      BER_BVZERO( &keys[1] );
00636 
00637                      ml->sml_desc = ad_sambaPwdMustChange;
00638                      ml->sml_op = LDAP_MOD_REPLACE;
00639 #ifdef SLAP_MOD_INTERNAL
00640                      ml->sml_flags = SLAP_MOD_INTERNAL;
00641 #endif
00642                      ml->sml_numvals = 1;
00643                      ml->sml_values = keys;
00644                      ml->sml_nvalues = NULL;
00645               }
00646 
00647               if (pi->smb_can_change)
00648               {
00649                      ml = ch_malloc(sizeof(Modifications));
00650                      ml->sml_next = qpw->rs_mods;
00651                      qpw->rs_mods = ml;
00652 
00653                      keys = ch_malloc( 2 * sizeof(struct berval) );
00654                      keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) );
00655                      keys[0].bv_len = snprintf(keys[0].bv_val,
00656                                    LDAP_PVT_INTTYPE_CHARS(long),
00657                                    "%ld", slap_get_time() + pi->smb_can_change);
00658                      BER_BVZERO( &keys[1] );
00659 
00660                      ml->sml_desc = ad_sambaPwdCanChange;
00661                      ml->sml_op = LDAP_MOD_REPLACE;
00662 #ifdef SLAP_MOD_INTERNAL
00663                      ml->sml_flags = SLAP_MOD_INTERNAL;
00664 #endif
00665                      ml->sml_numvals = 1;
00666                      ml->sml_values = keys;
00667                      ml->sml_nvalues = NULL;
00668               }
00669        }
00670 #endif /* DO_SAMBA */
00671 
00672 #ifdef DO_SHADOW
00673        /* shadow stuff */
00674        if ( SMBK5PWD_DO_SHADOW( pi ) && is_entry_objectclass(e, oc_shadowAccount, 0 ) ) {
00675               struct berval *keys;
00676 
00677               ml = ch_malloc(sizeof(Modifications));
00678               if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next;
00679               ml->sml_next = qpw->rs_mods;
00680               qpw->rs_mods = ml;
00681 
00682               keys = ch_malloc( sizeof(struct berval) * 2);
00683               BER_BVZERO( &keys[1] );
00684               keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) );
00685               keys[0].bv_len = snprintf(keys[0].bv_val,
00686                      LDAP_PVT_INTTYPE_CHARS(long),
00687                      "%ld", (long)(slap_get_time() / (60 * 60 * 24)));
00688 
00689               ml->sml_desc = ad_shadowLastChange;
00690               ml->sml_op = LDAP_MOD_REPLACE;
00691 #ifdef SLAP_MOD_INTERNAL
00692               ml->sml_flags = SLAP_MOD_INTERNAL;
00693 #endif
00694               ml->sml_numvals = 1;
00695               ml->sml_values = keys;
00696               ml->sml_nvalues = NULL;
00697        }
00698 #endif /* DO_SHADOW */
00699 
00700        be_entry_release_r( op, e );
00701        qpw->rs_new.bv_val[qpw->rs_new.bv_len] = term;
00702 
00703        return SLAP_CB_CONTINUE;
00704 }
00705 
00706 static slap_overinst smbk5pwd;
00707 
00708 /* back-config stuff */
00709 enum {
00710        PC_SMB_MUST_CHANGE = 1,
00711        PC_SMB_CAN_CHANGE,
00712        PC_SMB_ENABLE
00713 };
00714 
00715 static ConfigDriver smbk5pwd_cf_func;
00716 
00717 /*
00718  * NOTE: uses OID arcs OLcfgCtAt:1 and OLcfgCtOc:1
00719  */
00720 
00721 static ConfigTable smbk5pwd_cfats[] = {
00722        { "smbk5pwd-enable", "arg",
00723               2, 0, 0, ARG_MAGIC|PC_SMB_ENABLE, smbk5pwd_cf_func,
00724               "( OLcfgCtAt:1.1 NAME 'olcSmbK5PwdEnable' "
00725               "DESC 'Modules to be enabled' "
00726               "SYNTAX OMsDirectoryString )", NULL, NULL },
00727        { "smbk5pwd-must-change", "time",
00728               2, 2, 0, ARG_MAGIC|ARG_INT|PC_SMB_MUST_CHANGE, smbk5pwd_cf_func,
00729               "( OLcfgCtAt:1.2 NAME 'olcSmbK5PwdMustChange' "
00730               "DESC 'Credentials validity interval' "
00731               "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
00732        { "smbk5pwd-can-change", "time",
00733               2, 2, 0, ARG_MAGIC|ARG_INT|PC_SMB_CAN_CHANGE, smbk5pwd_cf_func,
00734               "( OLcfgCtAt:1.3 NAME 'olcSmbK5PwdCanChange' "
00735               "DESC 'Credentials minimum validity interval' "
00736               "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
00737 
00738        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
00739 };
00740 
00741 static ConfigOCs smbk5pwd_cfocs[] = {
00742        { "( OLcfgCtOc:1.1 "
00743               "NAME 'olcSmbK5PwdConfig' "
00744               "DESC 'smbk5pwd overlay configuration' "
00745               "SUP olcOverlayConfig "
00746               "MAY ( "
00747                      "olcSmbK5PwdEnable "
00748                      "$ olcSmbK5PwdMustChange "
00749                      "$ olcSmbK5PwdCanChange "
00750               ") )", Cft_Overlay, smbk5pwd_cfats },
00751 
00752        { NULL, 0, NULL }
00753 };
00754 
00755 /*
00756  * add here other functionalities; handle their initialization
00757  * as appropriate in smbk5pwd_modules_init().
00758  */
00759 static slap_verbmasks smbk5pwd_modules[] = {
00760        { BER_BVC( "krb5" ),        SMBK5PWD_F_KRB5      },
00761        { BER_BVC( "samba" ),              SMBK5PWD_F_SAMBA },
00762        { BER_BVC( "shadow" ),             SMBK5PWD_F_SHADOW },
00763        { BER_BVNULL,               -1 }
00764 };
00765 
00766 static int
00767 smbk5pwd_cf_func( ConfigArgs *c )
00768 {
00769        slap_overinst *on = (slap_overinst *)c->bi;
00770 
00771        int           rc = 0;
00772        smbk5pwd_t    *pi = on->on_bi.bi_private;
00773 
00774        if ( c->op == SLAP_CONFIG_EMIT ) {
00775               switch( c->type ) {
00776               case PC_SMB_MUST_CHANGE:
00777 #ifdef DO_SAMBA
00778                      c->value_int = pi->smb_must_change;
00779 #else /* ! DO_SAMBA */
00780                      c->value_int = 0;
00781 #endif /* ! DO_SAMBA */
00782                      break;
00783 
00784               case PC_SMB_CAN_CHANGE:
00785 #ifdef DO_SAMBA
00786                      c->value_int = pi->smb_can_change;
00787 #else /* ! DO_SAMBA */
00788                      c->value_int = 0;
00789 #endif /* ! DO_SAMBA */
00790                      break;
00791 
00792               case PC_SMB_ENABLE:
00793                      c->rvalue_vals = NULL;
00794                      if ( pi->mode ) {
00795                             mask_to_verbs( smbk5pwd_modules, pi->mode, &c->rvalue_vals );
00796                             if ( c->rvalue_vals == NULL ) {
00797                                    rc = 1;
00798                             }
00799                      }
00800                      break;
00801 
00802               default:
00803                      assert( 0 );
00804                      rc = 1;
00805               }
00806               return rc;
00807 
00808        } else if ( c->op == LDAP_MOD_DELETE ) {
00809               switch( c->type ) {
00810               case PC_SMB_MUST_CHANGE:
00811                      break;
00812 
00813                 case PC_SMB_CAN_CHANGE:
00814                         break;
00815 
00816               case PC_SMB_ENABLE:
00817                      if ( !c->line ) {
00818                             pi->mode = 0;
00819 
00820                      } else {
00821                             int i;
00822 
00823                             i = verb_to_mask( c->line, smbk5pwd_modules );
00824                             pi->mode &= ~smbk5pwd_modules[i].mask;
00825                      }
00826                      break;
00827 
00828               default:
00829                      assert( 0 );
00830                      rc = 1;
00831               }
00832               return rc;
00833        }
00834 
00835        switch( c->type ) {
00836        case PC_SMB_MUST_CHANGE:
00837 #ifdef DO_SAMBA
00838               if ( c->value_int < 0 ) {
00839                      Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
00840                             "<%s> invalid negative value \"%d\".",
00841                             c->log, c->argv[ 0 ], 0 );
00842                      return 1;
00843               }
00844               pi->smb_must_change = c->value_int;
00845 #else /* ! DO_SAMBA */
00846               Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
00847                      "<%s> only meaningful "
00848                      "when compiled with -DDO_SAMBA.\n",
00849                      c->log, c->argv[ 0 ], 0 );
00850               return 1;
00851 #endif /* ! DO_SAMBA */
00852               break;
00853 
00854         case PC_SMB_CAN_CHANGE:
00855 #ifdef DO_SAMBA
00856                 if ( c->value_int < 0 ) {
00857                         Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
00858                                 "<%s> invalid negative value \"%d\".",
00859                                 c->log, c->argv[ 0 ], 0 );
00860                         return 1;
00861                 }
00862                 pi->smb_can_change = c->value_int;
00863 #else /* ! DO_SAMBA */
00864                 Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
00865                         "<%s> only meaningful "
00866                         "when compiled with -DDO_SAMBA.\n",
00867                         c->log, c->argv[ 0 ], 0 );
00868                 return 1;
00869 #endif /* ! DO_SAMBA */
00870                 break;
00871 
00872        case PC_SMB_ENABLE: {
00873               slap_mask_t   mode = pi->mode, m = 0;
00874 
00875               rc = verbs_to_mask( c->argc, c->argv, smbk5pwd_modules, &m );
00876               if ( rc > 0 ) {
00877                      Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
00878                             "<%s> unknown module \"%s\".\n",
00879                             c->log, c->argv[ 0 ], c->argv[ rc ] );
00880                      return 1;
00881               }
00882 
00883               /* we can hijack the smbk5pwd_t structure because
00884                * from within the configuration, this is the only
00885                * active thread. */
00886               pi->mode |= m;
00887 
00888 #ifndef DO_KRB5
00889               if ( SMBK5PWD_DO_KRB5( pi ) ) {
00890                      Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
00891                             "<%s> module \"%s\" only allowed when compiled with -DDO_KRB5.\n",
00892                             c->log, c->argv[ 0 ], c->argv[ rc ] );
00893                      pi->mode = mode;
00894                      return 1;
00895               }
00896 #endif /* ! DO_KRB5 */
00897 
00898 #ifndef DO_SAMBA
00899               if ( SMBK5PWD_DO_SAMBA( pi ) ) {
00900                      Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
00901                             "<%s> module \"%s\" only allowed when compiled with -DDO_SAMBA.\n",
00902                             c->log, c->argv[ 0 ], c->argv[ rc ] );
00903                      pi->mode = mode;
00904                      return 1;
00905               }
00906 #endif /* ! DO_SAMBA */
00907 
00908 #ifndef DO_SHADOW
00909               if ( SMBK5PWD_DO_SHADOW( pi ) ) {
00910                      Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
00911                             "<%s> module \"%s\" only allowed when compiled with -DDO_SHADOW.\n",
00912                             c->log, c->argv[ 0 ], c->argv[ rc ] );
00913                      pi->mode = mode;
00914                      return 1;
00915               }
00916 #endif /* ! DO_SHADOW */
00917 
00918               {
00919                      BackendDB     db = *c->be;
00920 
00921                      /* Re-initialize the module, because
00922                       * the configuration might have changed */
00923                      db.bd_info = (BackendInfo *)on;
00924                      rc = smbk5pwd_modules_init( pi );
00925                      if ( rc ) {
00926                             pi->mode = mode;
00927                             return 1;
00928                      }
00929               }
00930 
00931               } break;
00932 
00933        default:
00934               assert( 0 );
00935               return 1;
00936        }
00937        return rc;
00938 }
00939 
00940 static int
00941 smbk5pwd_modules_init( smbk5pwd_t *pi )
00942 {
00943        static struct {
00944               const char           *name;
00945               AttributeDescription **adp;
00946        }
00947 #ifdef DO_KRB5
00948        krb5_ad[] = {
00949               { "krb5Key",                &ad_krb5Key },
00950               { "krb5KeyVersionNumber",   &ad_krb5KeyVersionNumber },
00951               { "krb5PrincipalName",             &ad_krb5PrincipalName },
00952               { "krb5ValidEnd",           &ad_krb5ValidEnd },
00953               { NULL }
00954        },
00955 #endif /* DO_KRB5 */
00956 #ifdef DO_SAMBA
00957        samba_ad[] = {
00958               { "sambaLMPassword",        &ad_sambaLMPassword },
00959               { "sambaNTPassword",        &ad_sambaNTPassword },
00960               { "sambaPwdLastSet",        &ad_sambaPwdLastSet },
00961               { "sambaPwdMustChange",            &ad_sambaPwdMustChange },
00962               { "sambaPwdCanChange",             &ad_sambaPwdCanChange },
00963               { NULL }
00964        },
00965 #endif /* DO_SAMBA */
00966 #ifdef DO_SHADOW
00967        shadow_ad[] = {
00968               { "shadowLastChange",              &ad_shadowLastChange },
00969               { NULL }
00970        },
00971 #endif /* DO_SHADOW */
00972        dummy_ad;
00973 
00974        /* this is to silence the unused var warning */
00975        dummy_ad.name = NULL;
00976 
00977 #ifdef DO_KRB5
00978        if ( SMBK5PWD_DO_KRB5( pi ) && oc_krb5KDCEntry == NULL ) {
00979               krb5_error_code      ret;
00980               extern HDB    *_kadm5_s_get_db(void *);
00981 
00982               int           i, rc;
00983 
00984               /* Make sure all of our necessary schema items are loaded */
00985               oc_krb5KDCEntry = oc_find( "krb5KDCEntry" );
00986               if ( !oc_krb5KDCEntry ) {
00987                      Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
00988                             "unable to find \"krb5KDCEntry\" objectClass.\n",
00989                             0, 0, 0 );
00990                      return -1;
00991               }
00992 
00993               for ( i = 0; krb5_ad[ i ].name != NULL; i++ ) {
00994                      const char    *text;
00995 
00996                      *(krb5_ad[ i ].adp) = NULL;
00997 
00998                      rc = slap_str2ad( krb5_ad[ i ].name, krb5_ad[ i ].adp, &text );
00999                      if ( rc != LDAP_SUCCESS ) {
01000                             Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
01001                                    "unable to find \"%s\" attributeType: %s (%d).\n",
01002                                    krb5_ad[ i ].name, text, rc );
01003                             oc_krb5KDCEntry = NULL;
01004                             return rc;
01005                      }
01006               }
01007 
01008               /* Initialize Kerberos context */
01009               ret = krb5_init_context(&context);
01010               if (ret) {
01011                      Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
01012                             "unable to initialize krb5 context (%d).\n",
01013                             ret, 0, 0 );
01014                      oc_krb5KDCEntry = NULL;
01015                      return -1;
01016               }
01017 
01018               ret = kadm5_s_init_with_password_ctx( context,
01019                      KADM5_ADMIN_SERVICE,
01020                      NULL,
01021                      KADM5_ADMIN_SERVICE,
01022                      &conf, 0, 0, &kadm_context );
01023               if (ret) {
01024                      char *err_str, *err_msg = "<unknown error>";
01025                      err_str = krb5_get_error_string( context );
01026                      if (!err_str)
01027                             err_msg = (char *)krb5_get_err_text( context, ret );
01028                      Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
01029                             "unable to initialize krb5 admin context: %s (%d).\n",
01030                             err_str ? err_str : err_msg, ret, 0 );
01031                      if (err_str)
01032                             krb5_free_error_string( context, err_str );
01033                      krb5_free_context( context );
01034                      oc_krb5KDCEntry = NULL;
01035                      return -1;
01036               }
01037 
01038               db = _kadm5_s_get_db( kadm_context );
01039        }
01040 #endif /* DO_KRB5 */
01041 
01042 #ifdef DO_SAMBA
01043        if ( SMBK5PWD_DO_SAMBA( pi ) && oc_sambaSamAccount == NULL ) {
01044               int           i, rc;
01045 
01046               oc_sambaSamAccount = oc_find( "sambaSamAccount" );
01047               if ( !oc_sambaSamAccount ) {
01048                      Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
01049                             "unable to find \"sambaSamAccount\" objectClass.\n",
01050                             0, 0, 0 );
01051                      return -1;
01052               }
01053 
01054               for ( i = 0; samba_ad[ i ].name != NULL; i++ ) {
01055                      const char    *text;
01056 
01057                      *(samba_ad[ i ].adp) = NULL;
01058 
01059                      rc = slap_str2ad( samba_ad[ i ].name, samba_ad[ i ].adp, &text );
01060                      if ( rc != LDAP_SUCCESS ) {
01061                             Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
01062                                    "unable to find \"%s\" attributeType: %s (%d).\n",
01063                                    samba_ad[ i ].name, text, rc );
01064                             oc_sambaSamAccount = NULL;
01065                             return rc;
01066                      }
01067               }
01068        }
01069 #endif /* DO_SAMBA */
01070 
01071 #ifdef DO_SHADOW
01072        if ( SMBK5PWD_DO_SHADOW( pi ) && oc_shadowAccount == NULL ) {
01073               int           i, rc;
01074 
01075               oc_shadowAccount = oc_find( "shadowAccount" );
01076               if ( !oc_shadowAccount ) {
01077                      Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
01078                             "unable to find \"shadowAccount\" objectClass.\n",
01079                             0, 0, 0 );
01080                      return -1;
01081               }
01082 
01083               for ( i = 0; shadow_ad[ i ].name != NULL; i++ ) {
01084                      const char    *text;
01085 
01086                      *(shadow_ad[ i ].adp) = NULL;
01087 
01088                      rc = slap_str2ad( shadow_ad[ i ].name, shadow_ad[ i ].adp, &text );
01089                      if ( rc != LDAP_SUCCESS ) {
01090                             Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
01091                                    "unable to find \"%s\" attributeType: %s (%d).\n",
01092                                    shadow_ad[ i ].name, text, rc );
01093                             oc_shadowAccount = NULL;
01094                             return rc;
01095                      }
01096               }
01097        }
01098 #endif /* DO_SHADOW */
01099 
01100        return 0;
01101 }
01102 
01103 static int
01104 smbk5pwd_db_init(BackendDB *be, ConfigReply *cr)
01105 {
01106        slap_overinst *on = (slap_overinst *)be->bd_info;
01107        smbk5pwd_t    *pi;
01108 
01109        pi = ch_calloc( 1, sizeof( smbk5pwd_t ) );
01110        if ( pi == NULL ) {
01111               return 1;
01112        }
01113        on->on_bi.bi_private = (void *)pi;
01114 
01115        return 0;
01116 }
01117 
01118 static int
01119 smbk5pwd_db_open(BackendDB *be, ConfigReply *cr)
01120 {
01121        slap_overinst *on = (slap_overinst *)be->bd_info;
01122        smbk5pwd_t    *pi = (smbk5pwd_t *)on->on_bi.bi_private;
01123 
01124        int    rc;
01125 
01126        if ( pi->mode == 0 ) {
01127               pi->mode = SMBK5PWD_F_ALL;
01128        }
01129 
01130        rc = smbk5pwd_modules_init( pi );
01131        if ( rc ) {
01132               return rc;
01133        }
01134 
01135        return 0;
01136 }
01137 
01138 static int
01139 smbk5pwd_db_destroy(BackendDB *be, ConfigReply *cr)
01140 {
01141        slap_overinst *on = (slap_overinst *)be->bd_info;
01142        smbk5pwd_t    *pi = (smbk5pwd_t *)on->on_bi.bi_private;
01143 
01144        if ( pi ) {
01145               ch_free( pi );
01146        }
01147 
01148        return 0;
01149 }
01150 
01151 int
01152 smbk5pwd_initialize(void)
01153 {
01154        int           rc;
01155 
01156        smbk5pwd.on_bi.bi_type = "smbk5pwd";
01157 
01158        smbk5pwd.on_bi.bi_db_init = smbk5pwd_db_init;
01159        smbk5pwd.on_bi.bi_db_open = smbk5pwd_db_open;
01160        smbk5pwd.on_bi.bi_db_destroy = smbk5pwd_db_destroy;
01161 
01162        smbk5pwd.on_bi.bi_extended = smbk5pwd_exop_passwd;
01163     
01164 #ifdef DO_KRB5
01165        smbk5pwd.on_bi.bi_op_bind = smbk5pwd_op_bind;
01166 
01167        lutil_passwd_add( (struct berval *)&k5key_scheme, k5key_chk, k5key_hash );
01168 #endif
01169 
01170        smbk5pwd.on_bi.bi_cf_ocs = smbk5pwd_cfocs;
01171 
01172        rc = config_register_schema( smbk5pwd_cfats, smbk5pwd_cfocs );
01173        if ( rc ) {
01174               return rc;
01175        }
01176 
01177        return overlay_register( &smbk5pwd );
01178 }
01179 
01180 #if SLAPD_OVER_SMBK5PWD == SLAPD_MOD_DYNAMIC
01181 int init_module(int argc, char *argv[]) {
01182        return smbk5pwd_initialize();
01183 }
01184 #endif
01185 
01186 #endif /* defined(SLAPD_OVER_SMBK5PWD) */