/* 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 <http://www.openldap.org/>.
+ *
+ * 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
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
-/*
+/* 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 <portable.h>
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 <gcrypt.h>
+typedef unsigned char DES_cblock[8];
+#elif HAVE_OPENSSL
#include <openssl/des.h>
#include <openssl/md4.h>
+#else
+#error Unsupported crypto backend.
+#endif
#include "ldap_utf8.h"
static AttributeDescription *ad_sambaLMPassword;
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 */
/* 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 =
#ifdef DO_SAMBA
| SMBK5PWD_F_SAMBA
#endif
+#ifdef DO_SHADOW
+ | SMBK5PWD_F_SHADOW
+#endif
;
static int smbk5pwd_modules_init( smbk5pwd_t *pi );
/* 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;
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
)
{
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 );
}
* 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 );
}
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 */
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 );
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 ) ) {
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;
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 );
}
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 );
}
}
#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;
}
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 }
};
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;
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 ) {
}
#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;
{ "krb5Key", &ad_krb5Key },
{ "krb5KeyVersionNumber", &ad_krb5KeyVersionNumber },
{ "krb5PrincipalName", &ad_krb5PrincipalName },
+ { "krb5ValidEnd", &ad_krb5ValidEnd },
{ NULL }
},
#endif /* DO_KRB5 */
{ 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 */
char *err_str, *err_msg = "<unknown error>";
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 );
}
#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;
}