diff options
author | Günther Deschner <gd@samba.org> | 2005-01-22 03:37:09 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 10:55:08 -0500 |
commit | b4afdc08d5336e4a337e453443d7af1d8655a31a (patch) | |
tree | 3d2e3351c4e767cbd05006b349006bb427ed3ec1 /source3/lib | |
parent | 686ceda3c3d3510f873d44c7bbb89d9134e0cf88 (diff) | |
download | samba-b4afdc08d5336e4a337e453443d7af1d8655a31a.tar.gz samba-b4afdc08d5336e4a337e453443d7af1d8655a31a.tar.bz2 samba-b4afdc08d5336e4a337e453443d7af1d8655a31a.zip |
r4925: Migrate Account Policies to passdb (esp. replicating ldapsam).
Does automated migration from account_policy.tdb v1 and v2 and offers a
pdbedit-Migration interface. Jerry, please feel free to revert that if
you have other plans.
Guenther
(This used to be commit 75af83dfcd8ef365b4b1180453060ae5176389f5)
Diffstat (limited to 'source3/lib')
-rw-r--r-- | source3/lib/account_pol.c | 424 | ||||
-rw-r--r-- | source3/lib/smbldap.c | 88 |
2 files changed, 412 insertions, 100 deletions
diff --git a/source3/lib/account_pol.c b/source3/lib/account_pol.c index 5997d9180a..b81a9fe34d 100644 --- a/source3/lib/account_pol.c +++ b/source3/lib/account_pol.c @@ -3,6 +3,7 @@ * account policy storage * Copyright (C) Jean François Micouleau 1998-2001. * Copyright (C) Andrew Bartlett 2002 + * Copyright (C) Guenther Deschner 2004-2005 * * 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 @@ -22,7 +23,14 @@ #include "includes.h" static TDB_CONTEXT *tdb; -#define DATABASE_VERSION 2 +/* cache all entries for 60 seconds for to save ldap-queries (cache is updated + * after this period if admins do not use pdbedit or usermanager but manipulate + * ldap directly) - gd */ + +#define DATABASE_VERSION 3 +#define AP_LASTSET "LAST_CACHE_UPDATE" +#define AP_MIGRATED "ACCOUNT POLICIES WERE MIGRATED TO PASSDB" +#define AP_TTL 60 extern DOM_SID global_sid_World; extern DOM_SID global_sid_Builtin_Administrators; @@ -32,100 +40,35 @@ extern DOM_SID global_sid_Builtin_Print_Operators; extern DOM_SID global_sid_Builtin_Backup_Operators; -/**************************************************************************** - Set default for a field if it is empty -****************************************************************************/ - -static void set_default_on_empty(int field, uint32 value) -{ - if (account_policy_get(field, NULL)) - return; - account_policy_set(field, value); - return; -} - -/**************************************************************************** - Open the account policy tdb. -****************************************************************************/ - -BOOL init_account_policy(void) -{ - const char *vstring = "INFO/version"; - uint32 version; - - if (tdb) - return True; - tdb = tdb_open_log(lock_path("account_policy.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); - if (!tdb) { - DEBUG(0,("Failed to open account policy database\n")); - return False; - } - - /* handle a Samba upgrade */ - tdb_lock_bystring(tdb, vstring,0); - if (!tdb_fetch_uint32(tdb, vstring, &version) || version != DATABASE_VERSION) { - tdb_store_uint32(tdb, vstring, DATABASE_VERSION); - - set_default_on_empty( - AP_MIN_PASSWORD_LEN, - MINPASSWDLENGTH);/* 5 chars minimum */ - set_default_on_empty( - AP_PASSWORD_HISTORY, - 0); /* don't keep any old password */ - set_default_on_empty( - AP_USER_MUST_LOGON_TO_CHG_PASS, - 0); /* don't force user to logon */ - set_default_on_empty( - AP_MAX_PASSWORD_AGE, - (uint32)-1); /* don't expire */ - set_default_on_empty( - AP_MIN_PASSWORD_AGE, - 0); /* 0 days */ - set_default_on_empty( - AP_LOCK_ACCOUNT_DURATION, - 30); /* lockout for 30 minutes */ - set_default_on_empty( - AP_RESET_COUNT_TIME, - 30); /* reset after 30 minutes */ - set_default_on_empty( - AP_BAD_ATTEMPT_LOCKOUT, - 0); /* don't lockout */ - set_default_on_empty( - AP_TIME_TO_LOGOUT, - -1); /* don't force logout */ - set_default_on_empty( - AP_REFUSE_MACHINE_PW_CHANGE, - 0); /* allow machine pw changes */ - } - tdb_unlock_bystring(tdb, vstring); - - /* These exist by default on NT4 in [HKLM\SECURITY\Policy\Accounts] */ - - privilege_create_account( &global_sid_World ); - privilege_create_account( &global_sid_Builtin_Administrators ); - privilege_create_account( &global_sid_Builtin_Account_Operators ); - privilege_create_account( &global_sid_Builtin_Server_Operators ); - privilege_create_account( &global_sid_Builtin_Print_Operators ); - privilege_create_account( &global_sid_Builtin_Backup_Operators ); - - return True; -} - -static const struct { +struct ap_table { int field; const char *string; -} account_policy_names[] = { - {AP_MIN_PASSWORD_LEN, "min password length"}, - {AP_PASSWORD_HISTORY, "password history"}, - {AP_USER_MUST_LOGON_TO_CHG_PASS, "user must logon to change password"}, - {AP_MAX_PASSWORD_AGE, "maximum password age (seconds since 1970)"}, - {AP_MIN_PASSWORD_AGE,"minimum password age (seconds since 1970)"}, - {AP_LOCK_ACCOUNT_DURATION, "lockout duration"}, - {AP_RESET_COUNT_TIME, "reset count minutes"}, - {AP_BAD_ATTEMPT_LOCKOUT, "bad lockout attempt"}, - {AP_TIME_TO_LOGOUT, "disconnect time"}, - {AP_REFUSE_MACHINE_PW_CHANGE, "refuse machine password change"}, - {0, NULL} + uint32 default_val; + const char *comment; +}; + +static const struct ap_table account_policy_names[] = { + {AP_MIN_PASSWORD_LEN, "min password length", MINPASSWDLENGTH, + "Minimal password length (default: 5)"}, + {AP_PASSWORD_HISTORY, "password history", 0, + "Length of Password History Entries (default: 0 => off)" }, + {AP_USER_MUST_LOGON_TO_CHG_PASS, "user must logon to change password", 0, + "Force Users to logon for password change (default: 0 => off, 2 => on)"}, + {AP_MAX_PASSWORD_AGE, "maximum password age", (uint32)-1, + "Maximum password age, in seconds (default: -1 => never expire passwords)"}, + {AP_MIN_PASSWORD_AGE,"minimum password age", 0, + "Minimal password age, in seconds (default: 0 => allow immediate password change)"}, + {AP_LOCK_ACCOUNT_DURATION, "lockout duration", 30, + "Lockout duration in minutes (default: 30, -1 => forever)"}, + {AP_RESET_COUNT_TIME, "reset count minutes", 30, + "Reset time after lockout in minutes (default: 30)"}, + {AP_BAD_ATTEMPT_LOCKOUT, "bad lockout attempt", 0, + "Lockout users after bad logon attempts (default: 0 => off)"}, + {AP_TIME_TO_LOGOUT, "disconnect time", -1, + "Disconnect Users outside logon hours (default: -1 => off, 0 => on)"}, + {AP_REFUSE_MACHINE_PW_CHANGE, "refuse machine password change", 0, + "Allow Machine Password changes (default: 0 => off)"}, + {0, NULL, 0, ""} }; char *account_policy_names_list(void) @@ -156,7 +99,7 @@ char *account_policy_names_list(void) Get the account policy name as a string from its #define'ed number ****************************************************************************/ -static const char *decode_account_policy_name(int field) +const char *decode_account_policy_name(int field) { int i; for (i=0; account_policy_names[i].string; i++) { @@ -168,6 +111,21 @@ static const char *decode_account_policy_name(int field) } /**************************************************************************** +Get the account policy comment as a string from its #define'ed number +****************************************************************************/ + +const char *account_policy_get_comment(int field) +{ + int i; + for (i=0; account_policy_names[i].string; i++) { + if (field == account_policy_names[i].field) + return account_policy_names[i].comment; + } + return NULL; + +} + +/**************************************************************************** Get the account policy name as a string from its #define'ed number ****************************************************************************/ @@ -182,15 +140,222 @@ int account_policy_name_to_fieldnum(const char *name) } -/**************************************************************************** -****************************************************************************/ +/***************************************************************************** +Update LAST-Set counter inside the cache +*****************************************************************************/ + +static BOOL account_policy_cache_timestamp(uint32 *value, BOOL update) +{ + pstring key; + uint32 val = 0; + time_t now; + + slprintf(key, sizeof(key)-1, "%s", AP_LASTSET); + + if (!init_account_policy()) + return False; + + if (!tdb_fetch_uint32(tdb, key, &val) && !update) { + DEBUG(10,("failed to get last set timestamp of cache\n")); + return False; + } + + *value = val; + + DEBUG(10, ("account policy cache lastset was: %s\n", http_timestring(val))); + + if (update) { + + now = time(NULL); + + if (!tdb_store_uint32(tdb, key, (uint32)now)) { + DEBUG(1, ("tdb_store_uint32 failed for %s\n", key)); + return False; + } + DEBUG(10, ("account policy cache lastset now: %s\n", http_timestring(now))); + *value = now; + } + + return True; +} + +/***************************************************************************** +Get default value for account policy +*****************************************************************************/ + +BOOL account_policy_get_default(int account_policy, uint32 *val) +{ + int i; + for (i=0; account_policy_names[i].field; i++) { + if (account_policy_names[i].field == account_policy) { + *val = account_policy_names[i].default_val; + return True; + } + } + DEBUG(0,("no default for account_policy index %d found. This should never happen\n", + account_policy)); + return False; +} + +/***************************************************************************** + Set default for a field if it is empty +*****************************************************************************/ + +static BOOL account_policy_set_default_on_empty(int account_policy) +{ + + uint32 value; + + if (!account_policy_get(account_policy, &value) && + !account_policy_get_default(account_policy, &value)) { + return False; + } + + return account_policy_set(account_policy, value); +} + +/***************************************************************************** +Check migration success, set marker if required +*****************************************************************************/ + +static BOOL already_migrated_account_policies(BOOL store_migration_success) +{ + pstring key; + uint32 value; + + slprintf(key, sizeof(key)-1, "%s", AP_MIGRATED); + + if (tdb_fetch_uint32(tdb, key, &value)) { + return True; + } + + if (store_migration_success) { + + if (!tdb_store_uint32(tdb, key, 1)) { + DEBUG(1, ("tdb_store_uint32 failed for %s\n", key)); + return False; + } + return True; + } + + return False; +} + +/***************************************************************************** +Migrate account policies to passdb +*****************************************************************************/ + +static BOOL migrate_account_policy_names_to_passdb(void) +{ + int i, tmp_val; + BOOL got_pol; + + if (already_migrated_account_policies(False)) { + return True; + } + + DEBUG(1,("start migrating account policies into passdb\n")); + + for (i=1; decode_account_policy_name(i) != NULL; i++) { + + got_pol = False; + + if (pdb_get_account_policy(i, &tmp_val)) { + DEBUG(10,("account policy already in passdb\n")); + got_pol = True; + } + + if (!got_pol && !account_policy_get(i, &tmp_val)) { + DEBUG(0,("very weird: could not get value for account policy\n")); + return False; + } + + DEBUGADD(1,("\tmigrating account policy (#%d: %s with value: %d) to passdb\n", + i, (char *)decode_account_policy_name(i), tmp_val)); + + /* set policy via new passdb api */ + if (!pdb_set_account_policy(i, tmp_val)) { + DEBUG(0,("failed to set account_policy\n")); + return False; + } + + } + + if (!already_migrated_account_policies(True)) { + DEBUG(0,("could not store marker for account policy migration in the tdb\n")); + return False; + } + + DEBUGADD(1,("succesfully migrated account policies into passdb\n")); + + return True; +} + +/***************************************************************************** + Open the account policy tdb. +***`*************************************************************************/ + +BOOL init_account_policy(void) +{ + + const char *vstring = "INFO/version"; + uint32 version; + int i; + + if (tdb) + return True; + + tdb = tdb_open_log(lock_path("account_policy.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + if (!tdb) { + DEBUG(0,("Failed to open account policy database\n")); + return False; + } + + /* handle a Samba upgrade */ + tdb_lock_bystring(tdb, vstring,0); + if (!tdb_fetch_uint32(tdb, vstring, &version) || version != DATABASE_VERSION) { + + tdb_store_uint32(tdb, vstring, DATABASE_VERSION); + + for (i=0; account_policy_names[i].field; i++) { + + if (!account_policy_set_default_on_empty(account_policy_names[i].field)) { + DEBUG(0,("failed to set default value in account policy tdb\n")); + return False; + } + } + } + + if (!migrate_account_policy_names_to_passdb()) { + DEBUG(0,("Could not migrate account policy tdb to passdb.\n")); + return False; + } + + tdb_unlock_bystring(tdb, vstring); + + /* These exist by default on NT4 in [HKLM\SECURITY\Policy\Accounts] */ + + privilege_create_account( &global_sid_World ); + privilege_create_account( &global_sid_Builtin_Administrators ); + privilege_create_account( &global_sid_Builtin_Account_Operators ); + privilege_create_account( &global_sid_Builtin_Server_Operators ); + privilege_create_account( &global_sid_Builtin_Print_Operators ); + privilege_create_account( &global_sid_Builtin_Backup_Operators ); + + return True; +} + +/***************************************************************************** +Internal function +*****************************************************************************/ BOOL account_policy_get(int field, uint32 *value) { fstring name; uint32 regval; - if(!init_account_policy())return False; + if (!init_account_policy()) + return False; if (value) *value = 0; @@ -213,12 +378,15 @@ BOOL account_policy_get(int field, uint32 *value) /**************************************************************************** +Get an account policy from a (migrated tdb) ****************************************************************************/ + BOOL account_policy_set(int field, uint32 value) { fstring name; - if(!init_account_policy())return False; + if (!init_account_policy()) + return False; fstrcpy(name, decode_account_policy_name(field)); if (!*name) { @@ -227,7 +395,7 @@ BOOL account_policy_set(int field, uint32 value) } if (!tdb_store_uint32(tdb, name, value)) { - DEBUG(1, ("tdb_store_uint32 failed for field %d (%s) on value %u", field, name, value)); + DEBUG(1, ("tdb_store_uint32 failed for field %d (%s) on value %u\n", field, name, value)); return False; } @@ -237,6 +405,64 @@ BOOL account_policy_set(int field, uint32 value) } /**************************************************************************** +Set an account policy in the cache +****************************************************************************/ + +BOOL cache_account_policy_set(int field, uint32 value) +{ + uint32 lastset, i; + + for (i=0; account_policy_names[i].field; i++) { + + if (account_policy_names[i].field == field) { + + DEBUG(10,("cache_account_policy_set: updating account pol cache\n")); + + if (!account_policy_set(field, value)) { + return False; + } + + if (!account_policy_cache_timestamp(&lastset, True)) { + DEBUG(10,("cache_account_policy_set: failed to get lastest cache update timestamp\n")); + return False; + } + + DEBUG(10,("cache_account_policy_set: cache valid until: %s\n", http_timestring(lastset+AP_TTL))); + } + } + + return True; +} + +/***************************************************************************** +Get an account policy from the cache +*****************************************************************************/ + +BOOL cache_account_policy_get(int field, uint32 *value) +{ + uint32 lastset, i; + + if (!account_policy_cache_timestamp(&lastset, False)) { + DEBUG(10,("cache_account_policy_get: failed to get latest cache update timestamp\n")); + return False; + } + + if ((lastset + AP_TTL) < (uint32)time(NULL) ) { + DEBUG(10,("cache_account_policy_get: no valid cache entry (cache expired)\n")); + return False; + } + + for (i=0; account_policy_names[i].field; i++) { + if (account_policy_names[i].field == field) { + return account_policy_get(field, value); + } + } + + return False; +} + + +/**************************************************************************** ****************************************************************************/ TDB_CONTEXT *get_account_pol_tdb( void ) diff --git a/source3/lib/smbldap.c b/source3/lib/smbldap.c index 7908bc254d..e242a6c3b1 100644 --- a/source3/lib/smbldap.c +++ b/source3/lib/smbldap.c @@ -208,6 +208,15 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { { LDAP_ATTR_LIST_END, NULL } }; +/* attributes used for account policies */ + +ATTRIB_MAP_ENTRY acctpol_attr_list[] = { + { LDAP_ATTR_OBJCLASS, "objectClass" }, + { LDAP_ATTR_ACCOUNT_POLICY_NAME,"sambaAccountPolicyName" }, + { LDAP_ATTR_ACCOUNT_POLICY_VAL, "sambaAccountPolicyValue" }, + { LDAP_ATTR_LIST_END, NULL }, +}; + /********************************************************************** perform a simple table lookup and return the attribute name **********************************************************************/ @@ -1244,6 +1253,82 @@ NTSTATUS smbldap_init(TALLOC_CTX *mem_ctx, const char *location, struct smbldap_ } /********************************************************************** + Add the account-policies below the sambaDomain object to LDAP, +*********************************************************************/ +static NTSTATUS add_new_domain_account_policies(struct smbldap_state *ldap_state, + const char *domain_name) +{ + NTSTATUS ntstatus = NT_STATUS_UNSUCCESSFUL; + int i, ldap_op, policy_default, rc; + const char *policy_string = NULL; + const char *policy_comment = NULL; + pstring dn; + fstring policy_default_str; + + DEBUG(3,("Adding new account policies for domain\n")); + ldap_op = LDAP_MOD_ADD; + + for (i=1; decode_account_policy_name(i) != NULL; i++) { + LDAPMod **mods = NULL; + + policy_string = decode_account_policy_name(i); + if (!policy_string) { + DEBUG(0,("add_new_domain_account_policies: ops. no policy!\n")); + return ntstatus; + } + + policy_comment = account_policy_get_comment(i); + if (!policy_comment) { + DEBUG(0,("add_new_domain_account_policies: no description for policy found\n")); + return ntstatus; + } + + if (!account_policy_get_default(i, &policy_default)) { + DEBUG(0,("add_new_domain_account_policies: failed to get default account policy\n")); + return ntstatus; + } + + slprintf(policy_default_str, sizeof(policy_default_str) - 1, "%i", policy_default); + + pstr_sprintf(dn, "%s=%s,%s=%s,%s", + get_attr_key2string(acctpol_attr_list, LDAP_ATTR_ACCOUNT_POLICY_NAME), policy_string, + get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN), get_global_sam_name(), + lp_ldap_suffix()); + + smbldap_set_mod( &mods, ldap_op, "objectClass", LDAP_OBJ_ACCOUNT_POLICY ); + + smbldap_set_mod( &mods, ldap_op, + get_attr_key2string(acctpol_attr_list, LDAP_ATTR_ACCOUNT_POLICY_NAME), + policy_string); + + smbldap_set_mod( &mods, ldap_op, + get_attr_key2string(acctpol_attr_list, LDAP_ATTR_ACCOUNT_POLICY_VAL), + policy_default_str); + + smbldap_set_mod( &mods, ldap_op, "description", policy_comment); + + rc = smbldap_add(ldap_state, dn, mods); + + if (rc!=LDAP_SUCCESS) { + char *ld_error = NULL; + ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error); + DEBUG(1,("failed to add account policy dn= %s with: %s\n\t%s\n", + dn, ldap_err2string(rc), + ld_error?ld_error:"unknown")); + SAFE_FREE(ld_error); + + ldap_mods_free(mods, True); + return ntstatus; + } + + DEBUG(2,("added: domain account policy = [%s] in the LDAP database\n", policy_string)); + ldap_mods_free(mods, True); + } + + return NT_STATUS_OK; +} + +/********************************************************************** Add the sambaDomain to LDAP, so we don't have to search for this stuff again. This is a once-add operation for now. @@ -1397,7 +1482,8 @@ NTSTATUS smbldap_search_domain_info(struct smbldap_state *ldap_state, DEBUG(3, ("Got no domain info entries for domain\n")); ldap_msgfree(*result); *result = NULL; - if (try_add && NT_STATUS_IS_OK(ret = add_new_domain_info(ldap_state, domain_name))) { + if (try_add && NT_STATUS_IS_OK(ret = add_new_domain_info(ldap_state, domain_name)) + && NT_STATUS_IS_OK(ret = add_new_domain_account_policies(ldap_state, domain_name))) { return smbldap_search_domain_info(ldap_state, result, domain_name, False); } else { |