summaryrefslogtreecommitdiff
path: root/source3/nsswitch
diff options
context:
space:
mode:
Diffstat (limited to 'source3/nsswitch')
-rw-r--r--source3/nsswitch/pam_winbind.c991
-rw-r--r--source3/nsswitch/pam_winbind.h56
-rw-r--r--source3/nsswitch/wb_client.c16
-rw-r--r--source3/nsswitch/wbinfo.c159
-rw-r--r--source3/nsswitch/winbindd.c14
-rw-r--r--source3/nsswitch/winbindd.h17
-rw-r--r--source3/nsswitch/winbindd_ads.c19
-rw-r--r--source3/nsswitch/winbindd_cache.c858
-rw-r--r--source3/nsswitch/winbindd_cm.c26
-rw-r--r--source3/nsswitch/winbindd_cred_cache.c270
-rw-r--r--source3/nsswitch/winbindd_creds.c162
-rw-r--r--source3/nsswitch/winbindd_dual.c217
-rw-r--r--source3/nsswitch/winbindd_group.c99
-rw-r--r--source3/nsswitch/winbindd_misc.c17
-rw-r--r--source3/nsswitch/winbindd_nss.h78
-rw-r--r--source3/nsswitch/winbindd_pam.c1026
-rw-r--r--source3/nsswitch/winbindd_passdb.c172
-rw-r--r--source3/nsswitch/winbindd_reconnect.c32
-rw-r--r--source3/nsswitch/winbindd_rpc.c69
-rw-r--r--source3/nsswitch/winbindd_sid.c50
-rw-r--r--source3/nsswitch/winbindd_user.c8
-rw-r--r--source3/nsswitch/winbindd_util.c13
22 files changed, 3833 insertions, 536 deletions
diff --git a/source3/nsswitch/pam_winbind.c b/source3/nsswitch/pam_winbind.c
index 61c01daa16..57e05dc4bb 100644
--- a/source3/nsswitch/pam_winbind.c
+++ b/source3/nsswitch/pam_winbind.c
@@ -3,12 +3,14 @@
Copyright Andrew Tridgell <tridge@samba.org> 2000
Copyright Tim Potter <tpot@samba.org> 2000
Copyright Andrew Bartlett <abartlet@samba.org> 2002
+ Copyright Guenther Deschner <gd@samba.org> 2005-2006
largely based on pam_userdb by Cristian Gafton <gafton@redhat.com>
also contains large slabs of code from pam_unix by Elliot Lee <sopwith@redhat.com>
(see copyright below for full details)
*/
+#include "includes.h"
#include "pam_winbind.h"
/* data tokens */
@@ -27,41 +29,122 @@ static void _pam_log(int err, const char *format, ...)
closelog();
}
+static void _pam_log_debug(int ctrl, int err, const char *format, ...)
+{
+ va_list args;
+
+ if (!(ctrl & WINBIND_DEBUG_ARG)) {
+ return;
+ }
+
+ va_start(args, format);
+ openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
static int _pam_parse(int argc, const char **argv)
{
- int ctrl;
+ int ctrl = 0;
+
+ load_case_tables();
+
+ if (!lp_load(dyn_CONFIGFILE,True,False,False,True)) {
+ return -1;
+ }
+
+ if (lp_parm_bool(-1, "pam_winbind", "cached_login", False)) {
+ ctrl |= WINBIND_CACHED_LOGIN;
+ }
+ if (lp_parm_bool(-1, "pam_winbind", "krb5_auth", False)) {
+ ctrl |= WINBIND_KRB5_AUTH;
+ }
+ if (lp_parm_const_string(-1, "pam_winbind", "krb5_ccache_type", NULL) != NULL) {
+ ctrl |= WINBIND_KRB5_CCACHE_TYPE;
+ }
+ if ((lp_parm_const_string(-1, "pam_winbind", "require-membership-of", NULL) != NULL) ||
+ (lp_parm_const_string(-1, "pam_winbind", "require_membership_of", NULL) != NULL)) {
+ ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
+ }
+ if (lp_parm_bool(-1, "pam_winbind", "create_homedir", False)) {
+ ctrl |= WINBIND_CREATE_HOMEDIR;
+ }
+
/* step through arguments */
- for (ctrl = 0; argc-- > 0; ++argv) {
+ for (; argc-- > 0; ++argv) {
/* generic options */
-
- if (!strcmp(*argv,"debug"))
+
+ if (!StrCaseCmp(*argv, "debug"))
ctrl |= WINBIND_DEBUG_ARG;
- else if (!strcasecmp(*argv, "use_authtok"))
+ else if (strequal(*argv, "use_authtok"))
ctrl |= WINBIND_USE_AUTHTOK_ARG;
- else if (!strcasecmp(*argv, "use_first_pass"))
+ else if (strequal(*argv, "use_first_pass"))
ctrl |= WINBIND_USE_FIRST_PASS_ARG;
- else if (!strcasecmp(*argv, "try_first_pass"))
+ else if (strequal(*argv, "try_first_pass"))
ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
- else if (!strcasecmp(*argv, "unknown_ok"))
+ else if (strequal(*argv, "unknown_ok"))
ctrl |= WINBIND_UNKNOWN_OK_ARG;
- else if (!strncasecmp(*argv, "require_membership_of", strlen("require_membership_of")))
+ else if (strnequal(*argv, "require_membership_of", strlen("require_membership_of")))
ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
- else if (!strncasecmp(*argv, "require-membership-of", strlen("require-membership-of")))
+ else if (strnequal(*argv, "require-membership-of", strlen("require-membership-of")))
ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
+ else if (strequal(*argv, "krb5_auth"))
+ ctrl |= WINBIND_KRB5_AUTH;
+ else if (strnequal(*argv, "krb5_ccache_type", strlen("krb5_ccache_type")))
+ ctrl |= WINBIND_KRB5_CCACHE_TYPE;
+ else if (strequal(*argv, "cached_login"))
+ ctrl |= WINBIND_CACHED_LOGIN;
+ else if (strequal(*argv, "create_homedir"))
+ ctrl |= WINBIND_CREATE_HOMEDIR;
else {
_pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
}
+
}
-
return ctrl;
-}
+};
static void _pam_winbind_cleanup_func(pam_handle_t *pamh, void *data, int error_status)
{
SAFE_FREE(data);
}
+static const struct ntstatus_errors {
+ const char *ntstatus_string;
+ const char *error_string;
+} ntstatus_errors[] = {
+ {"NT_STATUS_OK", "Success"},
+ {"NT_STATUS_BACKUP_CONTROLLER", "No primary Domain Controler available"},
+ {"NT_STATUS_PWD_TOO_SHORT", "Password too short"},
+ {"NT_STATUS_PWD_TOO_RECENT", "The password of this user is too recent to change"},
+ {"NT_STATUS_PWD_HISTORY_CONFLICT", "Password is already in password history"},
+ {"NT_STATUS_PASSWORD_EXPIRED", "Your password has expired"},
+ {"NT_STATUS_PASSWORD_MUST_CHANGE", "You need to change your password now"},
+ {"NT_STATUS_INVALID_WORKSTATION", "You are not allowed to logon from this workstation"},
+ {"NT_STATUS_INVALID_LOGON_HOURS", "You are not allowed to logon at this time"},
+ {"NT_STATUS_ACCOUNT_EXPIRED", "Your account has expired. Please contact your System administrator"}, /* SCNR */
+ {"NT_STATUS_ACCOUNT_DISABLED", "Your account is disabled. Please contact your System administrator"}, /* SCNR */
+ {"NT_STATUS_ACCOUNT_LOCKED_OUT", "Your account has been locked. Please contact your System administrator"}, /* SCNR */
+ {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", "Invalid Trust Account"},
+ {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", "Invalid Trust Account"},
+ {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", "Invalid Trust Account"},
+ {"NT_STATUS_ACCESS_DENIED", "Access is denied"},
+ {NULL, NULL}
+};
+
+const char *_get_ntstatus_error_string(const char *nt_status_string)
+{
+ int i;
+ for (i=0; ntstatus_errors[i].ntstatus_string != NULL; i++) {
+ if (strequal(ntstatus_errors[i].ntstatus_string, nt_status_string)) {
+ return ntstatus_errors[i].error_string;
+ }
+ }
+ return NULL;
+}
+
/* --- authentication management functions --- */
/* Attempt a conversation */
@@ -70,16 +153,16 @@ static int converse(pam_handle_t *pamh, int nargs,
struct pam_message **message,
struct pam_response **response)
{
- int retval;
- struct pam_conv *conv;
-
- retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv ) ;
- if (retval == PAM_SUCCESS) {
- retval = conv->conv(nargs, (const struct pam_message **)message,
- response, conv->appdata_ptr);
- }
+ int retval;
+ struct pam_conv *conv;
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv );
+ if (retval == PAM_SUCCESS) {
+ retval = conv->conv(nargs, (const struct pam_message **)message,
+ response, conv->appdata_ptr);
+ }
- return retval; /* propagate error status */
+ return retval; /* propagate error status */
}
@@ -103,11 +186,23 @@ static int _make_remark(pam_handle_t * pamh, int type, const char *text)
return retval;
}
-static int pam_winbind_request(enum winbindd_cmd req_type,
+static int _make_remark_format(pam_handle_t * pamh, int type, const char *format, ...)
+{
+ va_list args;
+ char *var;
+
+ va_start(args, format);
+ vasprintf(&var, format, args);
+ va_end(args);
+
+ return _make_remark(pamh, type, var);
+}
+
+static int pam_winbind_request(pam_handle_t * pamh, int ctrl,
+ enum winbindd_cmd req_type,
struct winbindd_request *request,
struct winbindd_response *response)
{
-
/* Fill in request and send down pipe */
init_request(request, req_type);
@@ -140,19 +235,20 @@ static int pam_winbind_request(enum winbindd_cmd req_type,
return PAM_SERVICE_ERR;
}
}
-
+
return PAM_SUCCESS;
}
-static int pam_winbind_request_log(enum winbindd_cmd req_type,
- struct winbindd_request *request,
- struct winbindd_response *response,
+static int pam_winbind_request_log(pam_handle_t * pamh,
int ctrl,
+ enum winbindd_cmd req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response,
const char *user)
{
int retval;
- retval = pam_winbind_request(req_type, request, response);
+ retval = pam_winbind_request(pamh, ctrl, req_type, request, response);
switch (retval) {
case PAM_AUTH_ERR:
@@ -168,13 +264,12 @@ static int pam_winbind_request_log(enum winbindd_cmd req_type,
_pam_log(LOG_WARNING, "user `%s' password expired", user);
return retval;
case PAM_NEW_AUTHTOK_REQD:
- /* password expired */
+ /* new password required */
_pam_log(LOG_WARNING, "user `%s' new password required", user);
return retval;
case PAM_USER_UNKNOWN:
/* the user does not exist */
- if (ctrl & WINBIND_DEBUG_ARG)
- _pam_log(LOG_NOTICE, "user `%s' not found",
+ _pam_log_debug(ctrl, LOG_NOTICE, "user `%s' not found",
user);
if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
return PAM_IGNORE;
@@ -191,6 +286,7 @@ static int pam_winbind_request_log(enum winbindd_cmd req_type,
/* Otherwise, the authentication looked good */
_pam_log(LOG_NOTICE, "user '%s' OK", user);
}
+
return retval;
default:
/* we don't know anything about this return value */
@@ -201,24 +297,67 @@ static int pam_winbind_request_log(enum winbindd_cmd req_type,
}
/* talk to winbindd */
-static int winbind_auth_request(const char *user, const char *pass, const char *member, int ctrl)
+static int winbind_auth_request(pam_handle_t * pamh,
+ int ctrl,
+ const char *user,
+ const char *pass,
+ const char *member,
+ const char *cctype,
+ int process_result)
{
struct winbindd_request request;
struct winbindd_response response;
+ int ret;
ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
strncpy(request.data.auth.user, user,
- sizeof(request.data.auth.user)-1);
+ sizeof(request.data.auth.user)-1);
strncpy(request.data.auth.pass, pass,
- sizeof(request.data.auth.pass)-1);
+ sizeof(request.data.auth.pass)-1);
+
+ request.data.auth.krb5_cc_type[0] = '\0';
+ request.data.auth.uid = -1;
+
+ request.flags = WBFLAG_PAM_INFO3_TEXT | WBFLAG_PAM_CONTACT_TRUSTDOM;
+
+ if (ctrl & WINBIND_KRB5_AUTH) {
+
+ struct passwd *pwd = NULL;
+
+ _pam_log_debug(ctrl, LOG_DEBUG, "enabling krb5 login flag\n");
+
+ request.flags |= WBFLAG_PAM_KRB5 | WBFLAG_PAM_FALLBACK_AFTER_KRB5;
+
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ return PAM_USER_UNKNOWN;
+ }
+ request.data.auth.uid = pwd->pw_uid;
+ }
- if (member == NULL )
- return pam_winbind_request_log(WINBINDD_PAM_AUTH, &request, &response, ctrl, user);
+ if (ctrl & WINBIND_CACHED_LOGIN) {
+ _pam_log_debug(ctrl, LOG_DEBUG, "enabling cached login flag\n");
+ request.flags |= WBFLAG_PAM_CACHED_LOGIN;
+ }
+
+ if (cctype != NULL) {
+ strncpy(request.data.auth.krb5_cc_type, cctype,
+ sizeof(request.data.auth.krb5_cc_type) - 1);
+ _pam_log_debug(ctrl, LOG_DEBUG, "enabling request for a %s krb5 ccache\n", cctype);
+ }
+
+ request.data.auth.require_membership_of_sid[0] = '\0';
+
+ if (member != NULL) {
+ strncpy(request.data.auth.require_membership_of_sid, member,
+ sizeof(request.data.auth.require_membership_of_sid)-1);
+ }
/* lookup name? */
- if (!strncmp("S-", member, 2) == 0) {
+ if ( (member != NULL) && (strncmp("S-", member, 2) != 0) ) {
struct winbindd_request sid_request;
struct winbindd_response sid_response;
@@ -226,55 +365,195 @@ static int winbind_auth_request(const char *user, const char *pass, const char *
ZERO_STRUCT(sid_request);
ZERO_STRUCT(sid_response);
- if (ctrl & WINBIND_DEBUG_ARG)
- _pam_log(LOG_DEBUG, "no sid given, looking up: %s\n", member);
+ _pam_log_debug(ctrl, LOG_DEBUG, "no sid given, looking up: %s\n", member);
/* fortunatly winbindd can handle non-separated names */
- strcpy(sid_request.data.name.name, member);
+ fstrcpy(sid_request.data.name.name, member);
- if (pam_winbind_request_log(WINBINDD_LOOKUPNAME, &sid_request, &sid_response, ctrl, user)) {
+ if (pam_winbind_request_log(pamh, ctrl, WINBINDD_LOOKUPNAME, &sid_request, &sid_response, user)) {
_pam_log(LOG_INFO, "could not lookup name: %s\n", member);
return PAM_AUTH_ERR;
}
member = sid_response.data.sid.sid;
+
+ strncpy(request.data.auth.require_membership_of_sid, member,
+ sizeof(request.data.auth.require_membership_of_sid)-1);
}
+
+ ret = pam_winbind_request_log(pamh, ctrl, WINBINDD_PAM_AUTH, &request, &response, user);
- strncpy(request.data.auth.require_membership_of_sid, member,
- sizeof(request.data.auth.require_membership_of_sid)-1);
+ if ((ctrl & WINBIND_KRB5_AUTH) &&
+ response.data.auth.krb5ccname[0] != '\0') {
+
+ char var[PATH_MAX];
+
+ _pam_log_debug(ctrl, LOG_DEBUG, "request returned KRB5CCNAME: %s",
+ response.data.auth.krb5ccname);
- return pam_winbind_request_log(WINBINDD_PAM_AUTH, &request, &response, ctrl, user);
+ snprintf(var, sizeof(var), "KRB5CCNAME=%s", response.data.auth.krb5ccname);
+
+ ret = pam_putenv(pamh, var);
+ if (ret != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "failed to set KRB5CCNAME to %s", var);
+ return ret;
+ }
+ }
+
+ if (!process_result) {
+ return ret;
+ }
+
+ if (ret) {
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PASSWORD_EXPIRED");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PASSWORD_MUST_CHANGE");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_INVALID_WORKSTATION");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_INVALID_LOGON_HOURS");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCOUNT_EXPIRED");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCOUNT_DISABLED");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCOUNT_LOCKED_OUT");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT");
+ }
+
+ /* handle the case where the auth was ok, but the password must expire right now */
+ /* good catch from Ralf Haferkamp: an expiry of "never" is translated to -1 */
+ if ((response.data.auth.policy.expire > 0) &&
+ (response.data.auth.info3.pass_last_set_time + response.data.auth.policy.expire < time(NULL))) {
+
+ ret = PAM_AUTHTOK_EXPIRED;
+
+ _pam_log_debug(ctrl, LOG_DEBUG,"Password has expired (Password was last set: %d, "
+ "the policy says it should expire here %d (now it's: %d)\n",
+ response.data.auth.info3.pass_last_set_time,
+ response.data.auth.info3.pass_last_set_time + response.data.auth.policy.expire,
+ time(NULL));
+
+ PAM_WB_REMARK_DIRECT_RET(pamh, "NT_STATUS_PASSWORD_EXPIRED");
+
+ }
+
+ /* warn a user if the password is about to expire soon */
+ if ((response.data.auth.policy.expire) &&
+ (response.data.auth.info3.pass_last_set_time + response.data.auth.policy.expire > time(NULL) ) ) {
+
+ int days = response.data.auth.policy.expire / SECONDS_PER_DAY;
+ if (days <= DAYS_TO_WARN_BEFORE_PWD_EXPIRES) {
+ _make_remark_format(pamh, PAM_TEXT_INFO, "Your password will expire in %d days", days);
+ }
+ }
+
+ if (response.data.auth.info3.user_flgs & LOGON_CACHED_ACCOUNT) {
+ _make_remark(pamh, PAM_ERROR_MSG, "Logging on using cached account. Network ressources can be unavailable");
+ }
+
+ /* save the CIFS homedir for pam_cifs / pam_mount */
+ if (response.data.auth.info3.home_dir[0] != '\0') {
+ char *buf;
+
+ if (!asprintf(&buf, "%s", response.data.auth.info3.home_dir)) {
+ return PAM_BUF_ERR;
+ }
+
+ pam_set_data( pamh, PAM_WINBIND_HOMEDIR, (void *)buf, _pam_winbind_cleanup_func);
+ }
+
+ return ret;
}
/* talk to winbindd */
-static int winbind_chauthtok_request(const char *user, const char *oldpass,
- const char *newpass, int ctrl)
+static int winbind_chauthtok_request(pam_handle_t * pamh,
+ int ctrl,
+ const char *user,
+ const char *oldpass,
+ const char *newpass)
{
struct winbindd_request request;
struct winbindd_response response;
+ int ret;
ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
- if (request.data.chauthtok.user == NULL) return -2;
+ if (request.data.chauthtok.user == NULL) return -2;
strncpy(request.data.chauthtok.user, user,
- sizeof(request.data.chauthtok.user) - 1);
-
- if (oldpass != NULL) {
- strncpy(request.data.chauthtok.oldpass, oldpass,
- sizeof(request.data.chauthtok.oldpass) - 1);
- } else {
- request.data.chauthtok.oldpass[0] = '\0';
- }
-
- if (newpass != NULL) {
- strncpy(request.data.chauthtok.newpass, newpass,
- sizeof(request.data.chauthtok.newpass) - 1);
- } else {
- request.data.chauthtok.newpass[0] = '\0';
- }
+ sizeof(request.data.chauthtok.user) - 1);
+
+ if (oldpass != NULL) {
+ strncpy(request.data.chauthtok.oldpass, oldpass,
+ sizeof(request.data.chauthtok.oldpass) - 1);
+ } else {
+ request.data.chauthtok.oldpass[0] = '\0';
+ }
- return pam_winbind_request_log(WINBINDD_PAM_CHAUTHTOK, &request, &response, ctrl, user);
+ if (newpass != NULL) {
+ strncpy(request.data.chauthtok.newpass, newpass,
+ sizeof(request.data.chauthtok.newpass) - 1);
+ } else {
+ request.data.chauthtok.newpass[0] = '\0';
+ }
+
+ if (ctrl & WINBIND_KRB5_AUTH) {
+ request.flags = WBFLAG_PAM_KRB5 | WBFLAG_PAM_CONTACT_TRUSTDOM;
+ }
+
+ ret = pam_winbind_request_log(pamh, ctrl, WINBINDD_PAM_CHAUTHTOK, &request, &response, user);
+
+ if (ret == PAM_SUCCESS) {
+ return ret;
+ }
+
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_BACKUP_CONTROLLER");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCESS_DENIED");
+
+ /* TODO: tell the min pwd length ? */
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PWD_TOO_SHORT");
+
+ /* TODO: tell the minage ? */
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PWD_TOO_RECENT");
+
+ /* TODO: tell the history length ? */
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PWD_HISTORY_CONFLICT");
+
+ if (strequal(response.data.auth.nt_status_string, "NT_STATUS_PASSWORD_RESTRICTION")) {
+
+ /* FIXME: avoid to send multiple PAM messages after another */
+ switch (response.data.auth.reject_reason) {
+ case 0:
+ break;
+ case REJECT_REASON_TOO_SHORT:
+ PAM_WB_REMARK_DIRECT(pamh, "NT_STATUS_PWD_TOO_SHORT");
+ break;
+ case REJECT_REASON_IN_HISTORY:
+ PAM_WB_REMARK_DIRECT(pamh, "NT_STATUS_PWD_HISTORY_CONFLICT");
+ break;
+ case REJECT_REASON_NOT_COMPLEX:
+ _make_remark(pamh, PAM_ERROR_MSG, "Password does not meet complexity requirements");
+ break;
+ default:
+ _pam_log_debug(ctrl, LOG_DEBUG,
+ "unknown password change reject reason: %d",
+ response.data.auth.reject_reason);
+ break;
+ }
+
+ _make_remark_format(pamh, PAM_ERROR_MSG,
+ "Your password must be at least %d characters; "
+ "cannot repeat any of the your previous %d passwords"
+ "%s. "
+ "Please type a different password. "
+ "Type a password which meets these requirements in both text boxes.",
+ response.data.auth.policy.min_length_password,
+ response.data.auth.policy.password_history,
+ (response.data.auth.policy.password_properties & DOMAIN_PASSWORD_COMPLEX) ?
+ "; must contain capitals, numerals or punctuation; and cannot contain your account or full name" :
+ "");
+
+ }
+
+ return ret;
}
/*
@@ -293,21 +572,21 @@ static int valid_user(const char *user)
static char *_pam_delete(register char *xx)
{
- _pam_overwrite(xx);
- _pam_drop(xx);
- return NULL;
+ _pam_overwrite(xx);
+ _pam_drop(xx);
+ return NULL;
}
/*
* obtain a password from the user
*/
-static int _winbind_read_password(pam_handle_t * pamh
- ,unsigned int ctrl
- ,const char *comment
- ,const char *prompt1
- ,const char *prompt2
- ,const char **pass)
+static int _winbind_read_password(pam_handle_t * pamh,
+ unsigned int ctrl,
+ const char *comment,
+ const char *prompt1,
+ const char *prompt2,
+ const char **pass)
{
int authtok_flag;
int retval;
@@ -391,16 +670,15 @@ static int _winbind_read_password(pam_handle_t * pamh
if (retval == PAM_SUCCESS) { /* a good conversation */
- token = x_strdup(resp[i - replies].resp);
+ token = SMB_STRDUP(resp[i - replies].resp);
if (token != NULL) {
if (replies == 2) {
-
/* verify that password entered correctly */
if (!resp[i - 1].resp
- || strcmp(token, resp[i - 1].resp)) {
+ || StrCaseCmp(token, resp[i - 1].resp)) {
_pam_delete(token); /* mistyped */
retval = PAM_AUTHTOK_RECOVER_ERR;
- _make_remark(pamh ,PAM_ERROR_MSG, MISTYPED_PASS);
+ _make_remark(pamh, PAM_ERROR_MSG, MISTYPED_PASS);
}
}
} else {
@@ -423,8 +701,7 @@ static int _winbind_read_password(pam_handle_t * pamh
}
if (retval != PAM_SUCCESS) {
- if (on(WINBIND_DEBUG_ARG, ctrl))
- _pam_log(LOG_DEBUG,
+ _pam_log_debug(ctrl, LOG_DEBUG,
"unable to obtain a password");
return retval;
}
@@ -434,10 +711,8 @@ static int _winbind_read_password(pam_handle_t * pamh
retval = pam_set_item(pamh, authtok_flag, token);
_pam_delete(token); /* clean it up */
- if (retval != PAM_SUCCESS
- || (retval = pam_get_item(pamh, authtok_flag
- ,(const void **) &item))
- != PAM_SUCCESS) {
+ if (retval != PAM_SUCCESS ||
+ (retval = pam_get_item(pamh, authtok_flag, (const void **) &item)) != PAM_SUCCESS) {
_pam_log(LOG_CRIT, "error manipulating password");
return retval;
@@ -450,92 +725,145 @@ static int _winbind_read_password(pam_handle_t * pamh
return PAM_SUCCESS;
}
+const char *get_conf_item_string(int argc,
+ const char **argv,
+ int ctrl,
+ const char *item,
+ int flag)
+{
+ int i = 0;
+ char *parm = NULL;
+ const char *parm_opt = NULL;
+
+ if (!(ctrl & flag)) {
+ goto out;
+ }
+
+ /* let the pam opt take precedence over the smb.conf option */
+ parm_opt = lp_parm_const_string(-1, "pam_winbind", item, NULL);
+
+ for ( i=0; i<argc; i++ ) {
+
+ if ((strncmp(argv[i], item, strlen(item)) == 0)) {
+ char *p;
+
+ parm = SMB_STRDUP(argv[i]);
+
+ if ( (p = strchr( parm, '=' )) == NULL) {
+ _pam_log(LOG_INFO, "no \"=\" delimiter for \"%s\" found\n", item);
+ goto out;
+ }
+ SAFE_FREE(parm);
+ _pam_log_debug(ctrl, LOG_INFO, "PAM config: %s '%s'\n", item, p+1);
+ return p + 1;
+ }
+ }
+
+ _pam_log_debug(ctrl, LOG_INFO, "CONFIG file: %s '%s'\n", item, parm_opt);
+out:
+ SAFE_FREE(parm);
+ return parm_opt;
+}
+
+const char *get_krb5_cc_type_from_config(int argc, const char **argv, int ctrl)
+{
+ return get_conf_item_string(argc, argv, ctrl, "krb5_ccache_type", WINBIND_KRB5_CCACHE_TYPE);
+}
+
+const char *get_member_from_config(int argc, const char **argv, int ctrl)
+{
+ const char *ret = NULL;
+ ret = get_conf_item_string(argc, argv, ctrl, "require_membership_of", WINBIND_REQUIRED_MEMBERSHIP);
+ if (ret) {
+ return ret;
+ }
+ return get_conf_item_string(argc, argv, ctrl, "require-membership-of", WINBIND_REQUIRED_MEMBERSHIP);
+}
+
PAM_EXTERN
int pam_sm_authenticate(pam_handle_t *pamh, int flags,
int argc, const char **argv)
{
- const char *username;
- const char *password;
- const char *member = NULL;
- int retval = PAM_AUTH_ERR;
- int i;
-
- /* parse arguments */
- int ctrl = _pam_parse(argc, argv);
-
- /* Get the username */
- retval = pam_get_user(pamh, &username, NULL);
- if ((retval != PAM_SUCCESS) || (!username)) {
- if (ctrl & WINBIND_DEBUG_ARG)
- _pam_log(LOG_DEBUG,"can not get the username");
- return PAM_SERVICE_ERR;
- }
-
- retval = _winbind_read_password(pamh, ctrl, NULL,
- "Password: ", NULL,
- &password);
-
- if (retval != PAM_SUCCESS) {
- _pam_log(LOG_ERR, "Could not retrieve user's password");
- return PAM_AUTHTOK_ERR;
- }
-
- if (ctrl & WINBIND_DEBUG_ARG) {
-
- /* Let's not give too much away in the log file */
+ const char *username;
+ const char *password;
+ const char *member = NULL;
+ const char *cctype = NULL;
+ int retval = PAM_AUTH_ERR;
+
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+ if (ctrl == -1) {
+ return PAM_SYSTEM_ERR;
+ }
+
+ _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_authenticate");
+
+ /* Get the username */
+ retval = pam_get_user(pamh, &username, NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ _pam_log_debug(ctrl, LOG_DEBUG, "can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ retval = _winbind_read_password(pamh, ctrl, NULL,
+ "Password: ", NULL,
+ &password);
+
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "Could not retrieve user's password");
+ return PAM_AUTHTOK_ERR;
+ }
+
+ /* Let's not give too much away in the log file */
#ifdef DEBUG_PASSWORD
- _pam_log(LOG_INFO, "Verify user `%s' with password `%s'",
- username, password);
+ _pam_log_debug(ctrl, LOG_INFO, "Verify user `%s' with password `%s'",
+ username, password);
#else
- _pam_log(LOG_INFO, "Verify user `%s'", username);
+ _pam_log_debug(ctrl, LOG_INFO, "Verify user `%s'", username);
#endif
- }
- if (ctrl & WINBIND_REQUIRED_MEMBERSHIP) {
-
- for ( i=0; i<argc; i++ ) {
+ member = get_member_from_config(argc, argv, ctrl);
- if ((strncmp(argv[i], "require_membership_of", strlen("require_membership_of")) == 0) ||
- (strncmp(argv[i], "require-membership-of", strlen("require-membership-of")) == 0)) {
+ cctype = get_krb5_cc_type_from_config(argc, argv, ctrl);
- char *p;
- char *parm = strdup(argv[i]);
+ /* Now use the username to look up password */
+ retval = winbind_auth_request(pamh, ctrl, username, password, member, cctype, True);
- if ( (p = strchr( parm, '=' )) == NULL) {
- _pam_log(LOG_INFO, "no \"=\" delimiter for \"require_membership_of\" found\n");
- break;
- }
+ if (retval == PAM_NEW_AUTHTOK_REQD ||
+ retval == PAM_AUTHTOK_EXPIRED) {
- member = strdup(p+1);
- }
- }
- }
+ char *buf;
- /* Now use the username to look up password */
- retval = winbind_auth_request(username, password, member, ctrl);
- if (retval == PAM_NEW_AUTHTOK_REQD ||
- retval == PAM_AUTHTOK_EXPIRED) {
-
- char *buf;
-
- if (!asprintf(&buf, "%d", retval)) {
- return PAM_BUF_ERR;
- }
+ if (!asprintf(&buf, "%d", retval)) {
+ return PAM_BUF_ERR;
+ }
- pam_set_data( pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, (void *)buf, _pam_winbind_cleanup_func);
+ pam_set_data( pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, (void *)buf, _pam_winbind_cleanup_func);
- return PAM_SUCCESS;
- }
-
- return retval;
+ return PAM_SUCCESS;
+ }
+
+ return retval;
}
PAM_EXTERN
int pam_sm_setcred(pam_handle_t *pamh, int flags,
int argc, const char **argv)
{
- return PAM_SUCCESS;
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+ if (ctrl == -1) {
+ return PAM_SYSTEM_ERR;
+ }
+
+ _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_setcred");
+
+ if (flags & PAM_DELETE_CRED) {
+ return pam_sm_close_session(pamh, flags, argc, argv);
+ }
+
+ return PAM_SUCCESS;
}
/*
@@ -546,110 +874,234 @@ PAM_EXTERN
int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
int argc, const char **argv)
{
- const char *username;
- void *tmp = NULL;
+ const char *username;
+ int retval = PAM_USER_UNKNOWN;
+ void *tmp = NULL;
+
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+ if (ctrl == -1) {
+ return PAM_SYSTEM_ERR;
+ }
- int retval = PAM_USER_UNKNOWN;
+ _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_acct_mgmt");
- /* parse arguments */
- int ctrl = _pam_parse(argc, argv);
- /* Get the username */
- retval = pam_get_user(pamh, &username, NULL);
- if ((retval != PAM_SUCCESS) || (!username)) {
- if (ctrl & WINBIND_DEBUG_ARG)
- _pam_log(LOG_DEBUG,"can not get the username");
- return PAM_SERVICE_ERR;
- }
+ /* Get the username */
+ retval = pam_get_user(pamh, &username, NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ _pam_log_debug(ctrl, LOG_DEBUG,"can not get the username");
+ return PAM_SERVICE_ERR;
+ }
- /* Verify the username */
- retval = valid_user(username);
- switch (retval) {
+ /* Verify the username */
+ retval = valid_user(username);
+ switch (retval) {
case -1:
- /* some sort of system error. The log was already printed */
- return PAM_SERVICE_ERR;
+ /* some sort of system error. The log was already printed */
+ return PAM_SERVICE_ERR;
case 1:
- /* the user does not exist */
- if (ctrl & WINBIND_DEBUG_ARG)
- _pam_log(LOG_NOTICE, "user `%s' not found",
- username);
- if (ctrl & WINBIND_UNKNOWN_OK_ARG)
- return PAM_IGNORE;
- return PAM_USER_UNKNOWN;
+ /* the user does not exist */
+ _pam_log_debug(ctrl, LOG_NOTICE, "user `%s' not found", username);
+ if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
+ return PAM_IGNORE;
+ }
+ return PAM_USER_UNKNOWN;
case 0:
- pam_get_data( pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, (const void **)&tmp);
-
- if (tmp != NULL) {
- retval = atoi(tmp);
- switch (retval) {
- case PAM_AUTHTOK_EXPIRED:
- /* fall through, since new token is required in this case */
- case PAM_NEW_AUTHTOK_REQD:
- _pam_log(LOG_WARNING, "pam_sm_acct_mgmt success but %s is set",
- PAM_WINBIND_NEW_AUTHTOK_REQD);
- _pam_log(LOG_NOTICE, "user '%s' needs new password", username);
- /* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */
- return PAM_NEW_AUTHTOK_REQD;
- default:
- _pam_log(LOG_WARNING, "pam_sm_acct_mgmt success");
- _pam_log(LOG_NOTICE, "user '%s' granted access", username);
- return PAM_SUCCESS;
- }
- }
-
- /* Otherwise, the authentication looked good */
- _pam_log(LOG_NOTICE, "user '%s' granted access", username);
- return PAM_SUCCESS;
+ pam_get_data( pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, (const void **)&tmp);
+ if (tmp != NULL) {
+ retval = atoi(tmp);
+ switch (retval) {
+ case PAM_AUTHTOK_EXPIRED:
+ /* fall through, since new token is required in this case */
+ case PAM_NEW_AUTHTOK_REQD:
+ _pam_log(LOG_WARNING, "pam_sm_acct_mgmt success but %s is set",
+ PAM_WINBIND_NEW_AUTHTOK_REQD);
+ _pam_log(LOG_NOTICE, "user '%s' needs new password", username);
+ /* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */
+ return PAM_NEW_AUTHTOK_REQD;
+ default:
+ _pam_log(LOG_WARNING, "pam_sm_acct_mgmt success");
+ _pam_log(LOG_NOTICE, "user '%s' granted access", username);
+ return PAM_SUCCESS;
+ }
+ }
+
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' granted access", username);
+ return PAM_SUCCESS;
default:
- /* we don't know anything about this return value */
- _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
- retval, username);
- return PAM_SERVICE_ERR;
- }
-
- /* should not be reached */
- return PAM_IGNORE;
+ /* we don't know anything about this return value */
+ _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s')",
+ retval, username);
+ return PAM_SERVICE_ERR;
+ }
+
+ /* should not be reached */
+ return PAM_IGNORE;
}
+
PAM_EXTERN
int pam_sm_open_session(pam_handle_t *pamh, int flags,
- int argc, const char **argv)
+ int argc, const char **argv)
{
- /* parse arguments */
- int ctrl = _pam_parse(argc, argv);
- if (ctrl & WINBIND_DEBUG_ARG)
- _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_open_session handler");
- return PAM_SUCCESS;
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+ if (ctrl == -1) {
+ return PAM_SYSTEM_ERR;
+ }
+
+ _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_open_session handler");
+
+
+ if (ctrl & WINBIND_CREATE_HOMEDIR) {
+
+ struct passwd *pwd = NULL;
+ const char *username;
+ int ret;
+ fstring tok;
+ fstring create_dir;
+ SMB_STRUCT_STAT sbuf;
+
+ /* Get the username */
+ ret = pam_get_user(pamh, &username, NULL);
+ if ((ret != PAM_SUCCESS) || (!username)) {
+ _pam_log_debug(ctrl, LOG_DEBUG, "can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ pwd = getpwnam(username);
+ if (pwd == NULL) {
+ _pam_log_debug(ctrl, LOG_DEBUG, "can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ _pam_log_debug(ctrl, LOG_DEBUG, "homedir is: %s", pwd->pw_dir);
+
+ if (directory_exist(pwd->pw_dir, &sbuf)) {
+ return PAM_SUCCESS;
+ }
+
+ fstrcpy(create_dir, "/");
+ while (next_token((const char **)&pwd->pw_dir, tok, "/", sizeof(tok))) {
+
+ mode_t mode = 0755;
+
+ fstrcat(create_dir, tok);
+ fstrcat(create_dir, "/");
+
+ if (!directory_exist(create_dir, &sbuf)) {
+ if (mkdir(create_dir, mode) != 0) {
+ _pam_log(LOG_ERR, "could not create dir: %s (%s)",
+ create_dir, strerror(errno));
+ return PAM_SERVICE_ERR;
+ }
+ }
+ }
+
+ if (sys_chown(create_dir, pwd->pw_uid, pwd->pw_gid) != 0) {
+ _pam_log(LOG_ERR, "failed to chown user homedir: %s (%s)",
+ create_dir, strerror(errno));
+ return PAM_SERVICE_ERR;
+ }
+ }
+
+ return PAM_SUCCESS;
}
+
PAM_EXTERN
int pam_sm_close_session(pam_handle_t *pamh, int flags,
- int argc, const char **argv)
+ int argc, const char **argv)
{
- /* parse arguments */
- int ctrl = _pam_parse(argc, argv);
- if (ctrl & WINBIND_DEBUG_ARG)
- _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_close_session handler");
- return PAM_SUCCESS;
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+ if (ctrl == -1) {
+ return PAM_SYSTEM_ERR;
+ }
+
+ _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_close_session handler");
+
+ if (!(flags & PAM_DELETE_CRED)) {
+ return PAM_SUCCESS;
+ }
+
+ if (ctrl & WINBIND_KRB5_AUTH) {
+
+ /* destroy the ccache here */
+ struct winbindd_request request;
+ struct winbindd_response response;
+ const char *user;
+ const char *ccname = NULL;
+ struct passwd *pwd = NULL;
+
+ int retval;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ retval = pam_get_user(pamh, &user, "Username: ");
+ if (retval == PAM_SUCCESS) {
+ if (user == NULL) {
+ _pam_log(LOG_ERR, "username was NULL!");
+ return PAM_USER_UNKNOWN;
+ }
+ if (retval == PAM_SUCCESS) {
+ _pam_log_debug(ctrl, LOG_DEBUG, "username [%s] obtained", user);
+ }
+ } else {
+ _pam_log_debug(ctrl, LOG_DEBUG, "could not identify user");
+ return retval;
+ }
+
+ ccname = pam_getenv(pamh, "KRB5CCNAME");
+ if (ccname == NULL) {
+ _pam_log_debug(ctrl, LOG_DEBUG, "user has no KRB5CCNAME environment");
+ return PAM_BUF_ERR;
+ }
+
+ fstrcpy(request.data.logoff.user, user);
+ fstrcpy(request.data.logoff.krb5ccname, ccname);
+
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ return PAM_USER_UNKNOWN;
+ }
+ request.data.logoff.uid = pwd->pw_uid;
+
+ request.flags = WBFLAG_PAM_KRB5 | WBFLAG_PAM_CONTACT_TRUSTDOM;
+
+ return pam_winbind_request_log(pamh, ctrl, WINBINDD_PAM_LOGOFF, &request, &response, user);
+ }
+
+ return PAM_SUCCESS;
}
-PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
- int argc, const char **argv)
+PAM_EXTERN
+int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
+ int argc, const char **argv)
{
unsigned int lctrl;
int retval;
- unsigned int ctrl = _pam_parse(argc, argv);
+ unsigned int ctrl;
/* <DO NOT free() THESE> */
const char *user;
- const char *member = NULL;
char *pass_old, *pass_new;
/* </DO NOT free() THESE> */
- char *Announce;
+ fstring Announce;
int retry = 0;
+ ctrl = _pam_parse(argc, argv);
+ if (ctrl == -1) {
+ return PAM_SYSTEM_ERR;
+ }
+
+ _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_chauthtok");
+
/*
* First get the name of a user
*/
@@ -659,13 +1111,13 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
_pam_log(LOG_ERR, "username was NULL!");
return PAM_USER_UNKNOWN;
}
- if (retval == PAM_SUCCESS && on(WINBIND_DEBUG_ARG, ctrl))
- _pam_log(LOG_DEBUG, "username [%s] obtained",
+ if (retval == PAM_SUCCESS) {
+ _pam_log_debug(ctrl, LOG_DEBUG, "username [%s] obtained",
user);
+ }
} else {
- if (on(WINBIND_DEBUG_ARG, ctrl))
- _pam_log(LOG_DEBUG,
- "password - could not identify user");
+ _pam_log_debug(ctrl, LOG_DEBUG,
+ "password - could not identify user");
return retval;
}
@@ -678,33 +1130,24 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
/* instruct user what is happening */
#define greeting "Changing password for "
- Announce = (char *) malloc(sizeof(greeting) + strlen(user));
- if (Announce == NULL) {
- _pam_log(LOG_CRIT,
- "password - out of memory");
- return PAM_BUF_ERR;
- }
- (void) strcpy(Announce, greeting);
- (void) strcpy(Announce + sizeof(greeting) - 1, user);
+ fstrcpy(Announce, greeting);
+ fstrcat(Announce, user);
#undef greeting
lctrl = ctrl | WINBIND__OLD_PASSWORD;
- retval = _winbind_read_password(pamh, lctrl
- ,Announce
- ,"(current) NT password: "
- ,NULL
- ,(const char **) &pass_old);
- free(Announce);
-
+ retval = _winbind_read_password(pamh, lctrl,
+ Announce,
+ "(current) NT password: ",
+ NULL,
+ (const char **) &pass_old);
if (retval != PAM_SUCCESS) {
- _pam_log(LOG_NOTICE
- ,"password - (old) token not obtained");
+ _pam_log(LOG_NOTICE, "password - (old) token not obtained");
return retval;
}
/* verify that this is the password for this user */
- retval = winbind_auth_request(user, pass_old, member, ctrl);
-
+ retval = winbind_auth_request(pamh, ctrl, user, pass_old, NULL, NULL, False);
+
if (retval != PAM_ACCT_EXPIRED
&& retval != PAM_AUTHTOK_EXPIRED
&& retval != PAM_NEW_AUTHTOK_REQD
@@ -716,8 +1159,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
pass_old = NULL;
if (retval != PAM_SUCCESS) {
- _pam_log(LOG_CRIT,
- "failed to set PAM_OLDAUTHTOK");
+ _pam_log(LOG_CRIT, "failed to set PAM_OLDAUTHTOK");
}
} else if (flags & PAM_UPDATE_AUTHTOK) {
@@ -729,8 +1171,8 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
* get the old token back.
*/
- retval = pam_get_item(pamh, PAM_OLDAUTHTOK
- ,(const void **) &pass_old);
+ retval = pam_get_item(pamh, PAM_OLDAUTHTOK,
+ (const void **) &pass_old);
if (retval != PAM_SUCCESS) {
_pam_log(LOG_NOTICE, "user not authenticated");
@@ -750,17 +1192,15 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
* password -- needed for pluggable password strength checking
*/
- retval = _winbind_read_password(pamh, lctrl
- ,NULL
- ,"Enter new NT password: "
- ,"Retype new NT password: "
- ,(const char **) &pass_new);
+ retval = _winbind_read_password(pamh, lctrl,
+ NULL,
+ "Enter new NT password: ",
+ "Retype new NT password: ",
+ (const char **) &pass_new);
if (retval != PAM_SUCCESS) {
- if (on(WINBIND_DEBUG_ARG, ctrl)) {
- _pam_log(LOG_ALERT
- ,"password - new password not obtained");
- }
+ _pam_log_debug(ctrl, LOG_ALERT
+ ,"password - new password not obtained");
pass_old = NULL;/* tidy up */
return retval;
}
@@ -781,14 +1221,30 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
* rebuild the password database file.
*/
- retval = winbind_chauthtok_request(user, pass_old, pass_new, ctrl);
- _pam_overwrite(pass_new);
- _pam_overwrite(pass_old);
- pass_old = pass_new = NULL;
+ retval = winbind_chauthtok_request(pamh, ctrl, user, pass_old, pass_new);
+ if (retval) {
+ _pam_overwrite(pass_new);
+ _pam_overwrite(pass_old);
+ pass_old = pass_new = NULL;
+ return retval;
+ }
+
+ /* just in case we need krb5 creds after a password change over msrpc */
+
+ if (ctrl & WINBIND_KRB5_AUTH) {
+
+ const char *member = get_member_from_config(argc, argv, ctrl);
+ const char *cctype = get_krb5_cc_type_from_config(argc, argv, ctrl);
+
+ retval = winbind_auth_request(pamh, ctrl, user, pass_new, member, cctype, False);
+ _pam_overwrite(pass_new);
+ _pam_overwrite(pass_old);
+ pass_old = pass_new = NULL;
+ }
} else {
retval = PAM_SERVICE_ERR;
}
-
+
return retval;
}
@@ -797,13 +1253,13 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
/* static module data */
struct pam_module _pam_winbind_modstruct = {
- MODULE_NAME,
- pam_sm_authenticate,
- pam_sm_setcred,
- pam_sm_acct_mgmt,
- pam_sm_open_session,
- pam_sm_close_session,
- pam_sm_chauthtok
+ MODULE_NAME,
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
};
#endif
@@ -812,6 +1268,7 @@ struct pam_module _pam_winbind_modstruct = {
* Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
* Copyright (c) Tim Potter <tpot@samba.org> 2000
* Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
+ * Copyright (c) Guenther Deschner <gd@samba.org> 2005-2006
* Copyright (c) Jan Rêkorajski 1999.
* Copyright (c) Andrew G. Morgan 1996-8.
* Copyright (c) Alex O. Yuriev, 1996.
diff --git a/source3/nsswitch/pam_winbind.h b/source3/nsswitch/pam_winbind.h
index 86ba977287..1e38269e0e 100644
--- a/source3/nsswitch/pam_winbind.h
+++ b/source3/nsswitch/pam_winbind.h
@@ -17,6 +17,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
+#include <time.h>
#include <config.h>
@@ -83,8 +84,10 @@ do { \
#define WINBIND_USE_FIRST_PASS_ARG (1<<4)
#define WINBIND__OLD_PASSWORD (1<<5)
#define WINBIND_REQUIRED_MEMBERSHIP (1<<6)
-
-#define PAM_WINBIND_NEW_AUTHTOK_REQD "PAM_WINBIND_NEW_AUTHTOK_REQD"
+#define WINBIND_KRB5_AUTH (1<<7)
+#define WINBIND_KRB5_CCACHE_TYPE (1<<8)
+#define WINBIND_CACHED_LOGIN (1<<9)
+#define WINBIND_CREATE_HOMEDIR (1<<10)
/*
* here is the string to inform the user that the new passwords they
@@ -96,4 +99,53 @@ do { \
#define on(x, y) (x & y)
#define off(x, y) (!(x & y))
+#define PAM_WINBIND_NEW_AUTHTOK_REQD "PAM_WINBIND_NEW_AUTHTOK_REQD"
+#define PAM_WINBIND_HOMEDIR "PAM_WINBIND_HOMEDIR"
+
+#define SECONDS_PER_DAY 86400
+
+#define DAYS_TO_WARN_BEFORE_PWD_EXPIRES 5
+
#include "winbind_client.h"
+
+#define PAM_WB_REMARK_DIRECT(h,x)\
+{\
+ const char *error_string = NULL; \
+ error_string = _get_ntstatus_error_string(x);\
+ if (error_string != NULL) {\
+ _make_remark(h, PAM_ERROR_MSG, error_string);\
+ } else {\
+ _make_remark(h, PAM_ERROR_MSG, x);\
+ };\
+};
+
+#define PAM_WB_REMARK_DIRECT_RET(h,x)\
+{\
+ const char *error_string = NULL; \
+ error_string = _get_ntstatus_error_string(x);\
+ if (error_string != NULL) {\
+ _make_remark(h, PAM_ERROR_MSG, error_string);\
+ return ret;\
+ };\
+ _make_remark(h, PAM_ERROR_MSG, x);\
+ return ret;\
+};
+
+#define PAM_WB_REMARK_CHECK_RESPONSE_RET(h,x,y)\
+{\
+ const char *ntstatus = x.data.auth.nt_status_string; \
+ const char *error_string = NULL; \
+ if (strequal(ntstatus,y)) {\
+ error_string = _get_ntstatus_error_string(y);\
+ if (error_string != NULL) {\
+ _make_remark(h, PAM_ERROR_MSG, error_string);\
+ return ret;\
+ };\
+ if (x.data.auth.error_string[0] != '\0') {\
+ _make_remark(h, PAM_ERROR_MSG, x.data.auth.error_string);\
+ return ret;\
+ };\
+ _make_remark(h, PAM_ERROR_MSG, y);\
+ return ret;\
+ };\
+};
diff --git a/source3/nsswitch/wb_client.c b/source3/nsswitch/wb_client.c
index fcab76b033..ff0f15a122 100644
--- a/source3/nsswitch/wb_client.c
+++ b/source3/nsswitch/wb_client.c
@@ -247,7 +247,7 @@ BOOL winbind_gid_to_sid(DOM_SID *sid, gid_t gid)
return (result == NSS_STATUS_SUCCESS);
}
-BOOL winbind_allocate_rid(uint32 *rid)
+BOOL winbind_allocate_uid(uid_t *uid)
{
struct winbindd_request request;
struct winbindd_response response;
@@ -260,18 +260,19 @@ BOOL winbind_allocate_rid(uint32 *rid)
/* Make request */
- result = winbindd_request_response(WINBINDD_ALLOCATE_RID, &request, &response);
+ result = winbindd_request_response(WINBINDD_ALLOCATE_UID,
+ &request, &response);
if (result != NSS_STATUS_SUCCESS)
return False;
/* Copy out result */
- *rid = response.data.rid;
+ *uid = response.data.uid;
return True;
}
-BOOL winbind_allocate_rid_and_gid(uint32 *rid, gid_t *gid)
+BOOL winbind_allocate_gid(gid_t *gid)
{
struct winbindd_request request;
struct winbindd_response response;
@@ -284,15 +285,14 @@ BOOL winbind_allocate_rid_and_gid(uint32 *rid, gid_t *gid)
/* Make request */
- result = winbindd_request_response(WINBINDD_ALLOCATE_RID_AND_GID, &request,
- &response);
+ result = winbindd_request_response(WINBINDD_ALLOCATE_GID,
+ &request, &response);
if (result != NSS_STATUS_SUCCESS)
return False;
/* Copy out result */
- *rid = response.data.rid_and_gid.rid;
- *gid = response.data.rid_and_gid.gid;
+ *gid = response.data.gid;
return True;
}
diff --git a/source3/nsswitch/wbinfo.c b/source3/nsswitch/wbinfo.c
index 793a05f179..571a1b3e19 100644
--- a/source3/nsswitch/wbinfo.c
+++ b/source3/nsswitch/wbinfo.c
@@ -199,7 +199,7 @@ static BOOL wbinfo_get_userdomgroups(const char *user_sid)
return False;
if (response.data.num_entries != 0)
- d_printf("%s", (char *)response.extra_data);
+ printf("%s", (char *)response.extra_data);
SAFE_FREE(response.extra_data);
@@ -260,15 +260,19 @@ static BOOL wbinfo_wins_byip(char *ip)
/* List trusted domains */
-static BOOL wbinfo_list_domains(void)
+static BOOL wbinfo_list_domains(BOOL list_all_domains)
{
+ struct winbindd_request request;
struct winbindd_response response;
+ ZERO_STRUCT(request);
ZERO_STRUCT(response);
/* Send request */
- if (winbindd_request_response(WINBINDD_LIST_TRUSTDOM, NULL, &response) !=
+ request.data.list_all_domains = list_all_domains;
+
+ if (winbindd_request_response(WINBINDD_LIST_TRUSTDOM, &request, &response) !=
NSS_STATUS_SUCCESS)
return False;
@@ -510,14 +514,26 @@ static BOOL wbinfo_sid_to_gid(char *sid)
return True;
}
-static BOOL wbinfo_allocate_rid(void)
+static BOOL wbinfo_allocate_uid(void)
{
- uint32 rid;
+ uid_t uid;
- if (!winbind_allocate_rid(&rid))
+ if (!winbind_allocate_uid(&uid))
return False;
- d_printf("New rid: %d\n", rid);
+ d_printf("New uid: %d\n", uid);
+
+ return True;
+}
+
+static BOOL wbinfo_allocate_gid(void)
+{
+ gid_t gid;
+
+ if (!winbind_allocate_gid(&gid))
+ return False;
+
+ d_printf("New gid: %d\n", gid);
return True;
}
@@ -577,6 +593,67 @@ static BOOL wbinfo_lookupname(char *name)
/* Authenticate a user with a plaintext password */
+static BOOL wbinfo_auth_krb5(char *username, const char *cctype, uint32 flags)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *p;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, p + 1);
+ *p = '%';
+ } else
+ fstrcpy(request.data.auth.user, username);
+
+ request.flags = flags;
+
+ fstrcpy(request.data.auth.krb5_cc_type, cctype);
+
+ request.data.auth.uid = geteuid();
+
+ result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
+
+ /* Display response */
+
+ d_printf("plaintext kerberos password authentication for [%s] %s (requesting cctype: %s)\n",
+ username, (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed", cctype);
+
+ if (response.data.auth.nt_status)
+ d_fprintf(stderr, "error code was %s (0x%x)\nerror messsage was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+
+ if (result == NSS_STATUS_SUCCESS) {
+
+ if (request.flags & WBFLAG_PAM_INFO3_TEXT) {
+ if (response.data.auth.info3.user_flgs & LOGON_CACHED_ACCOUNT) {
+ d_printf("user_flgs: LOGON_CACHED_ACCOUNT\n");
+ }
+ }
+
+ if (response.data.auth.krb5ccname[0] != '\0') {
+ d_printf("credentials were put in: %s\n", response.data.auth.krb5ccname);
+ } else {
+ d_printf("no credentials cached\n");
+ }
+ }
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Authenticate a user with a plaintext password */
+
static BOOL wbinfo_auth(char *username)
{
struct winbindd_request request;
@@ -968,7 +1045,10 @@ enum {
OPT_GETDCNAME,
OPT_USERDOMGROUPS,
OPT_USERSIDS,
- OPT_SEPARATOR
+ OPT_ALLOCATE_UID,
+ OPT_ALLOCATE_GID,
+ OPT_SEPARATOR,
+ OPT_LIST_ALL_DOMAINS
};
int main(int argc, char **argv)
@@ -997,9 +1077,13 @@ int main(int argc, char **argv)
{ "gid-to-sid", 'G', POPT_ARG_INT, &int_arg, 'G', "Converts gid to sid", "GID" },
{ "sid-to-uid", 'S', POPT_ARG_STRING, &string_arg, 'S', "Converts sid to uid", "SID" },
{ "sid-to-gid", 'Y', POPT_ARG_STRING, &string_arg, 'Y', "Converts sid to gid", "SID" },
- { "allocate-rid", 'A', POPT_ARG_NONE, 0, 'A', "Get a new RID out of idmap" },
+ { "allocate-uid", 0, POPT_ARG_NONE, 0, OPT_ALLOCATE_UID,
+ "Get a new UID out of idmap" },
+ { "allocate-gid", 0, POPT_ARG_NONE, 0, OPT_ALLOCATE_GID,
+ "Get a new GID out of idmap" },
{ "check-secret", 't', POPT_ARG_NONE, 0, 't', "Check shared secret" },
{ "trusted-domains", 'm', POPT_ARG_NONE, 0, 'm', "List trusted domains" },
+ { "all-domains", 0, POPT_ARG_NONE, 0, OPT_LIST_ALL_DOMAINS, "List all domains (trusted and own domain)" },
{ "sequence", 0, POPT_ARG_NONE, 0, OPT_SEQUENCE, "Show sequence numbers of all domains" },
{ "domain-info", 'D', POPT_ARG_STRING, &string_arg, 'D', "Show most of the info we have about the domain" },
{ "user-groups", 'r', POPT_ARG_STRING, &string_arg, 'r', "Get user groups", "USER" },
@@ -1016,6 +1100,11 @@ int main(int argc, char **argv)
#ifdef WITH_FAKE_KASERVER
{ "klog", 'k', POPT_ARG_STRING, &string_arg, 'k', "set an AFS token from winbind", "user%password" },
#endif
+#ifdef HAVE_KRB5
+ { "krb5auth", 'K', POPT_ARG_STRING, &string_arg, 'K', "authenticate user using Kerberos", "user%password" },
+ /* destroys wbinfo --help output */
+ /* "user%password,DOM\\user%password,user@EXAMPLE.COM,EXAMPLE.COM\\user%password" }, */
+#endif
{ "separator", 0, POPT_ARG_NONE, 0, OPT_SEPARATOR, "Get the active winbind separator", NULL },
POPT_COMMON_VERSION
POPT_TABLEEND
@@ -1120,9 +1209,15 @@ int main(int argc, char **argv)
goto done;
}
break;
- case 'A':
- if (!wbinfo_allocate_rid()) {
- d_fprintf(stderr, "Could not allocate a RID\n");
+ case OPT_ALLOCATE_UID:
+ if (!wbinfo_allocate_uid()) {
+ d_fprintf(stderr, "Could not allocate a uid\n");
+ goto done;
+ }
+ break;
+ case OPT_ALLOCATE_GID:
+ if (!wbinfo_allocate_gid()) {
+ d_fprintf(stderr, "Could not allocate a gid\n");
goto done;
}
break;
@@ -1133,7 +1228,7 @@ int main(int argc, char **argv)
}
break;
case 'm':
- if (!wbinfo_list_domains()) {
+ if (!wbinfo_list_domains(False)) {
d_fprintf(stderr, "Could not list trusted domains\n");
goto done;
}
@@ -1190,6 +1285,38 @@ int main(int argc, char **argv)
goto done;
break;
}
+ case 'K': {
+ BOOL got_error = False;
+ uint32 flags = WBFLAG_PAM_KRB5 |
+ WBFLAG_PAM_CACHED_LOGIN |
+ WBFLAG_PAM_FALLBACK_AFTER_KRB5 |
+ WBFLAG_PAM_INFO3_TEXT;
+ fstring tok;
+ int i;
+ const char *arg[] = { string_arg, NULL };
+ const char *cctypes[] = { "FILE",
+ "KCM",
+ "KCM:0",
+ "Garbage",
+ NULL,
+ "0"};
+
+ while (next_token(arg, tok, LIST_SEP, sizeof(tok))) {
+
+ for (i=0; i < ARRAY_SIZE(cctypes); i++) {
+ if (!wbinfo_auth_krb5(tok, cctypes[i], flags)) {
+ d_fprintf(stderr, "Could not authenticate user [%s] with "
+ "Kerberos (ccache: %s)\n", tok, cctypes[i]);
+ got_error = True;
+ }
+ }
+ }
+
+ if (got_error)
+ goto done;
+
+ break;
+ }
case 'k':
if (!wbinfo_klog(string_arg)) {
d_fprintf(stderr, "Could not klog user\n");
@@ -1198,7 +1325,7 @@ int main(int argc, char **argv)
break;
case 'p':
if (!wbinfo_ping()) {
- d_fprintf(stderr, "Could not ping winbindd!\n");
+ d_fprintf(stderr, "could not ping winbindd!\n");
goto done;
}
break;
@@ -1223,6 +1350,10 @@ int main(int argc, char **argv)
d_printf("%c\n", sep);
break;
}
+ case OPT_LIST_ALL_DOMAINS:
+ if (!wbinfo_list_domains(True)) {
+ goto done;
+ }
/* generic configuration options */
case OPT_DOMAIN_NAME:
break;
diff --git a/source3/nsswitch/winbindd.c b/source3/nsswitch/winbindd.c
index bbcf2b5e88..4a269bac17 100644
--- a/source3/nsswitch/winbindd.c
+++ b/source3/nsswitch/winbindd.c
@@ -120,7 +120,7 @@ static void winbindd_status(void)
if (DEBUGLEVEL >= 2 && winbindd_num_clients()) {
DEBUG(2, ("\tclient list:\n"));
for(tmp = winbindd_client_list(); tmp; tmp = tmp->next) {
- DEBUG(2, ("\t\tpid %lu, sock %d\n",
+ DEBUGADD(2, ("\t\tpid %lu, sock %d\n",
(unsigned long)tmp->pid, tmp->sock));
}
}
@@ -250,6 +250,7 @@ static struct winbindd_dispatch_table {
{ WINBINDD_PAM_AUTH, winbindd_pam_auth, "PAM_AUTH" },
{ WINBINDD_PAM_AUTH_CRAP, winbindd_pam_auth_crap, "AUTH_CRAP" },
{ WINBINDD_PAM_CHAUTHTOK, winbindd_pam_chauthtok, "CHAUTHTOK" },
+ { WINBINDD_PAM_LOGOFF, winbindd_pam_logoff, "PAM_LOGOFF" },
/* Enumeration functions */
@@ -270,9 +271,8 @@ static struct winbindd_dispatch_table {
{ WINBINDD_SID_TO_GID, winbindd_sid_to_gid, "SID_TO_GID" },
{ WINBINDD_UID_TO_SID, winbindd_uid_to_sid, "UID_TO_SID" },
{ WINBINDD_GID_TO_SID, winbindd_gid_to_sid, "GID_TO_SID" },
- { WINBINDD_ALLOCATE_RID, winbindd_allocate_rid, "ALLOCATE_RID" },
- { WINBINDD_ALLOCATE_RID_AND_GID, winbindd_allocate_rid_and_gid,
- "ALLOCATE_RID_AND_GID" },
+ { WINBINDD_ALLOCATE_UID, winbindd_allocate_uid, "ALLOCATE_UID" },
+ { WINBINDD_ALLOCATE_GID, winbindd_allocate_uid, "ALLOCATE_GID" },
/* Miscellaneous */
@@ -1062,7 +1062,11 @@ int main(int argc, char **argv)
as to SIGHUP signal */
message_register(MSG_SMB_CONF_UPDATED, msg_reload_services);
message_register(MSG_SHUTDOWN, msg_shutdown);
-
+
+ /* Handle online/offline messages. */
+ message_register(MSG_WINBIND_OFFLINE,winbind_msg_offline);
+ message_register(MSG_WINBIND_ONLINE,winbind_msg_online);
+
poptFreeContext(pc);
netsamlogon_cache_init(); /* Non-critical */
diff --git a/source3/nsswitch/winbindd.h b/source3/nsswitch/winbindd.h
index 00a0233055..e81102571c 100644
--- a/source3/nsswitch/winbindd.h
+++ b/source3/nsswitch/winbindd.h
@@ -143,7 +143,9 @@ struct winbindd_child {
struct winbindd_domain *domain;
pstring logfilename;
+ TALLOC_CTX *mem_ctx;
struct fd_event event;
+ struct timed_event *timed_event;
struct winbindd_async_request *requests;
};
@@ -157,7 +159,8 @@ struct winbindd_domain {
BOOL native_mode; /* is this a win2k domain in native mode ? */
BOOL active_directory; /* is this a win2k active directory ? */
BOOL primary; /* is this our primary domain ? */
- BOOL internal; /* BUILTIN and member SAM */
+ BOOL internal; /* BUILTIN and member SAM */
+ BOOL online; /* is this domain available ? */
/* Lookup methods for this domain (LDAP or RPC) */
struct winbindd_methods *methods;
@@ -268,6 +271,16 @@ struct winbindd_methods {
/* return the current global sequence number */
NTSTATUS (*sequence_number)(struct winbindd_domain *domain, uint32 *seq);
+ /* return the lockout policy */
+ NTSTATUS (*lockout_policy)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ SAM_UNK_INFO_12 *lockout_policy);
+
+ /* return the lockout policy */
+ NTSTATUS (*password_policy)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ SAM_UNK_INFO_1 *password_policy);
+
/* enumerate trusted domains */
NTSTATUS (*trusted_domains)(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
@@ -305,7 +318,7 @@ struct winbindd_idmap_methods {
#define WINBINDD_ESTABLISH_LOOP 30
#define WINBINDD_RESCAN_FREQ 300
-
+#define WINBINDD_PAM_AUTH_KRB5_RENEW_TIME 2592000 /* one month */
#define DOM_SEQUENCE_NONE ((uint32)-1)
#endif /* _WINBINDD_H */
diff --git a/source3/nsswitch/winbindd_ads.c b/source3/nsswitch/winbindd_ads.c
index 29129e823a..a866365042 100644
--- a/source3/nsswitch/winbindd_ads.c
+++ b/source3/nsswitch/winbindd_ads.c
@@ -102,6 +102,8 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
ads->auth.realm = SMB_STRDUP( lp_realm() );
}
+ ads->auth.renewable = 1;
+
status = ads_connect(ads);
if (!ADS_ERR_OK(status) || !ads->config.realm) {
extern struct winbindd_methods msrpc_methods, cache_methods;
@@ -206,8 +208,10 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain,
name = ads_pull_username(ads, mem_ctx, msg);
gecos = ads_pull_string(ads, mem_ctx, msg, "name");
if (use_nss_info("sfu")) {
- homedir = ads_pull_string(ads, mem_ctx, msg, ads->schema.sfu_homedir_attr);
- shell = ads_pull_string(ads, mem_ctx, msg, ads->schema.sfu_shell_attr);
+ homedir = ads_pull_string(ads, mem_ctx, msg,
+ ads->schema.sfu_homedir_attr);
+ shell = ads_pull_string(ads, mem_ctx, msg,
+ ads->schema.sfu_shell_attr);
}
if (!ads_pull_sid(ads, msg, "objectSid",
@@ -474,8 +478,10 @@ static NTSTATUS query_user(struct winbindd_domain *domain,
info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
if (use_nss_info("sfu")) {
- info->homedir = ads_pull_string(ads, mem_ctx, msg, ads->schema.sfu_homedir_attr);
- info->shell = ads_pull_string(ads, mem_ctx, msg, ads->schema.sfu_shell_attr);
+ info->homedir = ads_pull_string(ads, mem_ctx, msg,
+ ads->schema.sfu_homedir_attr);
+ info->shell = ads_pull_string(ads, mem_ctx, msg,
+ ads->schema.sfu_shell_attr);
}
if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
@@ -872,8 +878,7 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain,
struct ds_domain_trust *domains = NULL;
int count = 0;
int i;
- /* i think we only need our forest and downlevel trusted domains */
- uint32 flags = DS_DOMAIN_IN_FOREST | DS_DOMAIN_DIRECT_OUTBOUND;
+ uint32 flags = DS_DOMAIN_DIRECT_OUTBOUND;
struct rpc_pipe_client *cli;
DEBUG(3,("ads: trusted_domains\n"));
@@ -946,6 +951,8 @@ struct winbindd_methods ads_methods = {
msrpc_lookup_useraliases,
lookup_groupmem,
sequence_number,
+ msrpc_lockout_policy,
+ msrpc_password_policy,
trusted_domains,
};
diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c
index 9ecfb1ff6e..28633757c0 100644
--- a/source3/nsswitch/winbindd_cache.c
+++ b/source3/nsswitch/winbindd_cache.c
@@ -6,7 +6,7 @@
Copyright (C) Andrew Tridgell 2001
Copyright (C) Gerald Carter 2003
Copyright (C) Volker Lendecke 2005
-
+ Copyright (C) Guenther Deschner 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
@@ -29,6 +29,12 @@
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_WINBIND
+/* Global online/offline state - False when online. winbindd starts up online
+ and sets this to true if the first query fails and there's an entry in
+ the cache tdb telling us to stay offline. */
+
+static BOOL global_winbindd_offline_state;
+
struct winbind_cache {
TDB_CONTEXT *tdb;
};
@@ -44,29 +50,6 @@ struct cache_entry {
static struct winbind_cache *wcache;
-/* flush the cache */
-void wcache_flush_cache(void)
-{
- extern BOOL opt_nocache;
-
- if (!wcache)
- return;
- if (wcache->tdb) {
- tdb_close(wcache->tdb);
- wcache->tdb = NULL;
- }
- if (opt_nocache)
- return;
-
- wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
- TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
-
- if (!wcache->tdb) {
- DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
- }
- DEBUG(10,("wcache_flush_cache success\n"));
-}
-
void winbindd_check_cache_size(time_t t)
{
static time_t last_check_time;
@@ -188,6 +171,22 @@ static uint32 centry_uint32(struct cache_entry *centry)
}
/*
+ pull a uint16 from a cache entry
+*/
+static uint16 centry_uint16(struct cache_entry *centry)
+{
+ uint16 ret;
+ if (centry->len - centry->ofs < 2) {
+ DEBUG(0,("centry corruption? needed 2 bytes, have %d\n",
+ centry->len - centry->ofs));
+ smb_panic("centry_uint16");
+ }
+ ret = CVAL(centry->data, centry->ofs);
+ centry->ofs += 2;
+ return ret;
+}
+
+/*
pull a uint8 from a cache entry
*/
static uint8 centry_uint8(struct cache_entry *centry)
@@ -203,6 +202,40 @@ static uint8 centry_uint8(struct cache_entry *centry)
return ret;
}
+/*
+ pull a NTTIME from a cache entry
+*/
+static NTTIME centry_nttime(struct cache_entry *centry)
+{
+ NTTIME ret;
+ if (centry->len - centry->ofs < 8) {
+ DEBUG(0,("centry corruption? needed 8 bytes, have %d\n",
+ centry->len - centry->ofs));
+ smb_panic("centry_nttime");
+ }
+ ret.low = IVAL(centry->data, centry->ofs);
+ centry->ofs += 4;
+ ret.high = IVAL(centry->data, centry->ofs);
+ centry->ofs += 4;
+ return ret;
+}
+
+/*
+ pull a time_t from a cache entry
+*/
+static time_t centry_time(struct cache_entry *centry)
+{
+ time_t ret;
+ if (centry->len - centry->ofs < sizeof(time_t)) {
+ DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
+ sizeof(time_t), centry->len - centry->ofs));
+ smb_panic("centry_time");
+ }
+ ret = IVAL(centry->data, centry->ofs); /* FIXME: correct ? */
+ centry->ofs += sizeof(time_t);
+ return ret;
+}
+
/* pull a string from a cache entry, using the supplied
talloc context
*/
@@ -403,10 +436,28 @@ done:
*/
static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
{
+ /* If we've been told to be offline - stay in that state... */
+ if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
+ DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
+ keystr, domain->name ));
+ return False;
+ }
+
+ /* when the domain is offline and we havent checked in the last 30
+ * seconds if it has become online again, return the cached entry.
+ * This deals with transient offline states... */
+
+ if (!domain->online &&
+ !NT_STATUS_IS_OK(check_negative_conn_cache(domain->name, domain->dcname))) {
+ DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
+ keystr, domain->name ));
+ return False;
+ }
+
/* if the server is OK and our cache entry came from when it was down then
the entry is invalid */
- if (domain->sequence_number != DOM_SEQUENCE_NONE &&
- centry->sequence_number == DOM_SEQUENCE_NONE) {
+ if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
+ (centry->sequence_number == DOM_SEQUENCE_NONE)) {
DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
keystr, domain->name ));
return True;
@@ -428,35 +479,17 @@ static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, s
return True;
}
-/*
- fetch an entry from the cache, with a varargs key. auto-fetch the sequence
- number and return status
-*/
-static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
- struct winbindd_domain *domain,
- const char *format, ...) PRINTF_ATTRIBUTE(3,4);
-static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
- struct winbindd_domain *domain,
- const char *format, ...)
+static struct cache_entry *wcache_fetch_raw(char *kstr)
{
- va_list ap;
- char *kstr;
TDB_DATA data;
struct cache_entry *centry;
TDB_DATA key;
- refresh_sequence_number(domain, False);
-
- va_start(ap, format);
- smb_xvasprintf(&kstr, format, ap);
- va_end(ap);
-
key.dptr = kstr;
key.dsize = strlen(kstr);
data = tdb_fetch(wcache->tdb, key);
if (!data.dptr) {
/* a cache miss */
- free(kstr);
return NULL;
}
@@ -467,16 +500,44 @@ static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
if (centry->len < 8) {
/* huh? corrupt cache? */
- DEBUG(10,("wcache_fetch: Corrupt cache for key %s domain %s (len < 8) ?\n",
- kstr, domain->name ));
+ DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
centry_free(centry);
- free(kstr);
return NULL;
}
centry->status = NT_STATUS(centry_uint32(centry));
centry->sequence_number = centry_uint32(centry);
+ return centry;
+}
+
+/*
+ fetch an entry from the cache, with a varargs key. auto-fetch the sequence
+ number and return status
+*/
+static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
+ struct winbindd_domain *domain,
+ const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
+ struct winbindd_domain *domain,
+ const char *format, ...)
+{
+ va_list ap;
+ char *kstr;
+ struct cache_entry *centry;
+
+ refresh_sequence_number(domain, False);
+
+ va_start(ap, format);
+ smb_xvasprintf(&kstr, format, ap);
+ va_end(ap);
+
+ centry = wcache_fetch_raw(kstr);
+ if (centry == NULL) {
+ free(kstr);
+ return NULL;
+ }
+
if (centry_expired(domain, kstr, centry)) {
DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
@@ -522,6 +583,16 @@ static void centry_put_uint32(struct cache_entry *centry, uint32 v)
}
/*
+ push a uint16 into a centry
+*/
+static void centry_put_uint16(struct cache_entry *centry, uint16 v)
+{
+ centry_expand(centry, 2);
+ SIVAL(centry->data, centry->ofs, v);
+ centry->ofs += 2;
+}
+
+/*
push a uint8 into a centry
*/
static void centry_put_uint8(struct cache_entry *centry, uint8 v)
@@ -563,6 +634,28 @@ static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
}
/*
+ push a NTTIME into a centry
+*/
+static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
+{
+ centry_expand(centry, 8);
+ SIVAL(centry->data, centry->ofs, nt.low);
+ centry->ofs += 4;
+ SIVAL(centry->data, centry->ofs, nt.high);
+ centry->ofs += 4;
+}
+
+/*
+ push a time_t into a centry
+*/
+static void centry_put_time(struct cache_entry *centry, time_t t)
+{
+ centry_expand(centry, sizeof(time_t));
+ SIVAL(centry->data, centry->ofs, t); /* FIXME: is this correct ?? */
+ centry->ofs += sizeof(time_t);
+}
+
+/*
start a centry for output. When finished, call centry_end()
*/
struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
@@ -666,6 +759,129 @@ static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WI
centry_free(centry);
}
+static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
+{
+ struct cache_entry *centry;
+
+ centry = centry_start(domain, status);
+ if (!centry)
+ return;
+
+ centry_put_nttime(centry, lockout_policy->duration);
+ centry_put_nttime(centry, lockout_policy->reset_count);
+ centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
+
+ centry_end(centry, "LOC_POL/%s", domain->name);
+
+ DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
+
+ centry_free(centry);
+}
+
+static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *password_policy)
+{
+ struct cache_entry *centry;
+
+ centry = centry_start(domain, status);
+ if (!centry)
+ return;
+
+ centry_put_uint16(centry, password_policy->min_length_password);
+ centry_put_uint16(centry, password_policy->password_history);
+ centry_put_uint32(centry, password_policy->password_properties);
+ centry_put_nttime(centry, password_policy->expire);
+ centry_put_nttime(centry, password_policy->min_passwordage);
+
+ centry_end(centry, "PWD_POL/%s", domain->name);
+
+ DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
+
+ centry_free(centry);
+}
+
+NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ TDB_DATA data;
+ fstring key_str;
+
+ if (!cache->tdb) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
+
+ data = tdb_fetch(cache->tdb, make_tdb_data(key_str, strlen(key_str)));
+ if (!data.dptr) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Lookup creds for a SID */
+NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ const uint8 **cached_nt_pass)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ time_t t;
+
+ if (!cache->tdb) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
+
+ if (!centry) {
+ DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
+ sid_string_static(sid)));
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ t = centry_time(centry);
+ *cached_nt_pass = (const uint8 *)centry_string(centry, mem_ctx);
+
+ dump_data(10, (const char *)cached_nt_pass, NT_HASH_LEN);
+ status = centry->status;
+
+ DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status %s\n",
+ sid_string_static(sid), get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+}
+
+NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ const uint8 nt_pass[NT_HASH_LEN])
+{
+ struct cache_entry *centry;
+ fstring sid_string;
+ NTSTATUS status = NT_STATUS_OK; /* ??? */
+
+ centry = centry_start(domain, status);
+ if (!centry) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
+
+ centry_put_time(centry, time(NULL));
+ centry_put_string(centry, (const char *)nt_pass);
+ centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
+
+ DEBUG(10,("wcache_save_creds: %s\n", sid_string));
+
+ centry_free(centry);
+
+ return NT_STATUS_OK;
+}
+
/* Query display info. This is the basic user list fn */
static NTSTATUS query_user_list(struct winbindd_domain *domain,
@@ -991,7 +1207,9 @@ do_query:
status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
/* and save it */
- wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
+ if (domain->online || !is_null_sid(sid)) {
+ wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
+ }
if (NT_STATUS_IS_OK(status)) {
strupper_m(CONST_DISCARD(char *,domain_name));
@@ -1390,7 +1608,9 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
return NT_STATUS_OK;
}
-/* enumerate trusted domains */
+/* enumerate trusted domains
+ * (we need to have the list of trustdoms in the cache when we go offline) -
+ * Guenther */
static NTSTATUS trusted_domains(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
uint32 *num_domains,
@@ -1398,16 +1618,184 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain,
char ***alt_names,
DOM_SID **dom_sids)
{
- get_cache(domain);
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ int i;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
+
+ if (!centry) {
+ goto do_query;
+ }
+
+ *num_domains = centry_uint32(centry);
+
+ (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
+ (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
+ (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
+
+ if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
+ smb_panic("trusted_domains out of memory");
+ }
+
+ for (i=0; i<(*num_domains); i++) {
+ (*names)[i] = centry_string(centry, mem_ctx);
+ (*alt_names)[i] = centry_string(centry, mem_ctx);
+ centry_sid(centry, &(*dom_sids)[i]);
+ }
+ status = centry->status;
+
+ DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ (*num_domains) = 0;
+ (*dom_sids) = NULL;
+ (*names) = NULL;
+ (*alt_names) = NULL;
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
domain->name ));
+
+ status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
+ names, alt_names, dom_sids);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+
+ centry_put_uint32(centry, *num_domains);
+
+ for (i=0; i<(*num_domains); i++) {
+ centry_put_string(centry, (*names)[i]);
+ centry_put_string(centry, (*alt_names)[i]);
+ centry_put_sid(centry, &(*dom_sids)[i]);
+ }
+
+ centry_end(centry, "TRUSTDOMS/%s", domain->name);
+
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* get lockout policy */
+static NTSTATUS lockout_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ SAM_UNK_INFO_12 *lockout_policy){
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
+
+ if (!centry)
+ goto do_query;
+
+ lockout_policy->duration = centry_nttime(centry);
+ lockout_policy->reset_count = centry_nttime(centry);
+ lockout_policy->bad_attempt_lockout = centry_uint16(centry);
+
+ status = centry->status;
+
+ DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ ZERO_STRUCTP(lockout_policy);
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->lockout_policy(domain, mem_ctx, lockout_policy);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ wcache_save_lockout_policy(domain, status, lockout_policy);
+
+ return status;
+}
+
+/* get password policy */
+static NTSTATUS password_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ SAM_UNK_INFO_1 *password_policy)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
+
+ if (!centry)
+ goto do_query;
+
+ password_policy->min_length_password = centry_uint16(centry);
+ password_policy->password_history = centry_uint16(centry);
+ password_policy->password_properties = centry_uint32(centry);
+ password_policy->expire = centry_nttime(centry);
+ password_policy->min_passwordage = centry_nttime(centry);
+
+ status = centry->status;
+
+ DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ ZERO_STRUCTP(password_policy);
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->password_policy(domain, mem_ctx, password_policy);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ wcache_save_password_policy(domain, status, password_policy);
- /* we don't cache this call */
- return domain->backend->trusted_domains(domain, mem_ctx, num_domains,
- names, alt_names, dom_sids);
+ return status;
}
+
/* Invalidate cached user and group lists coherently */
static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
@@ -1448,22 +1836,6 @@ void wcache_invalidate_cache(void)
}
}
-/* the ADS backend methods are exposed via this structure */
-struct winbindd_methods cache_methods = {
- True,
- query_user_list,
- enum_dom_groups,
- enum_local_groups,
- name_to_sid,
- sid_to_name,
- query_user,
- lookup_usergroups,
- lookup_useraliases,
- lookup_groupmem,
- sequence_number,
- trusted_domains,
-};
-
static BOOL init_wcache(void)
{
if (wcache == NULL) {
@@ -1474,8 +1846,10 @@ static BOOL init_wcache(void)
if (wcache->tdb != NULL)
return True;
- wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
- TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
+ /* when working offline we must not clear the cache on restart */
+ wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
+ WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
+ TDB_DEFAULT /*TDB_CLEAR_IF_FIRST*/, O_RDWR|O_CREAT, 0600);
if (wcache->tdb == NULL) {
DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
@@ -1577,6 +1951,25 @@ BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
return True;
}
+void cache_cleanup_response(pid_t pid)
+{
+ fstring key_str;
+
+ if (!init_wcache())
+ return;
+
+ DEBUG(10,("Cleaning up response for pid %d\n", pid));
+
+ fstr_sprintf(key_str, "DR/%d", pid);
+ tdb_delete(wcache->tdb, string_tdb_data(key_str));
+
+ fstr_sprintf(key_str, "DE/%d", pid);
+ tdb_delete(wcache->tdb, string_tdb_data(key_str));
+
+ return;
+}
+
+
BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
const char **domain_name, const char **name,
enum SID_NAME_USE *type)
@@ -1613,6 +2006,48 @@ BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
return NT_STATUS_IS_OK(status);
}
+BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ struct winbindd_domain *domain;
+ struct winbind_cache *cache;
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ fstring uname;
+
+ domain = find_lookup_domain_from_name(domain_name);
+ if (domain == NULL) {
+ return False;
+ }
+
+ cache = get_cache(domain);
+
+ if (cache->tdb == NULL) {
+ return False;
+ }
+
+ fstrcpy(uname, name);
+ strupper_m(uname);
+
+ centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
+ if (centry == NULL) {
+ return False;
+ }
+
+ if (NT_STATUS_IS_OK(centry->status)) {
+ *type = (enum SID_NAME_USE)centry_uint32(centry);
+ centry_sid(centry, sid);
+ }
+
+ status = centry->status;
+ centry_free(centry);
+
+ return NT_STATUS_IS_OK(status);
+}
+
void cache_sid2name(struct winbindd_domain *domain, const DOM_SID *sid,
const char *domain_name, const char *name,
enum SID_NAME_USE type)
@@ -1620,3 +2055,282 @@ void cache_sid2name(struct winbindd_domain *domain, const DOM_SID *sid,
wcache_save_sid_to_name(domain, NT_STATUS_OK, sid, domain_name,
name, type);
}
+
+/* delete all centries that don't have NT_STATUS_OK set */
+static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
+ TDB_DATA dbuf, void *state)
+{
+ struct cache_entry *centry;
+ char buf[1024];
+
+ if (!snprintf(buf, kbuf.dsize + 1, "%s", kbuf.dptr)) {
+ return 1;
+ }
+
+ centry = wcache_fetch_raw(buf);
+ if (!centry) {
+ return 0;
+ }
+
+ if (!NT_STATUS_IS_OK(centry->status)) {
+ DEBUG(10,("deleting centry %s\n", buf));
+ tdb_delete(the_tdb, kbuf);
+ }
+
+ centry_free(centry);
+ return 0;
+}
+
+/* flush the cache */
+void wcache_flush_cache(void)
+{
+ extern BOOL opt_nocache;
+
+ if (!wcache)
+ return;
+ if (wcache->tdb) {
+ tdb_close(wcache->tdb);
+ wcache->tdb = NULL;
+ }
+ if (opt_nocache)
+ return;
+
+ /* when working offline we must not clear the cache on restart */
+ wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
+ WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
+ TDB_DEFAULT /* TDB_CLEAR_IF_FIRST */, O_RDWR|O_CREAT, 0600);
+
+ if (!wcache->tdb) {
+ DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
+ }
+
+ tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
+
+ DEBUG(10,("wcache_flush_cache success\n"));
+}
+
+/* Count cached creds */
+
+static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ int *cred_count = (int*)state;
+
+ if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
+ (*cred_count)++;
+ }
+ return 0;
+}
+
+NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
+{
+ struct winbind_cache *cache = get_cache(domain);
+
+ *count = 0;
+
+ if (!cache->tdb) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
+
+ return NT_STATUS_OK;
+}
+
+struct cred_list {
+ struct cred_list *prev, *next;
+ TDB_DATA key;
+ fstring name;
+ time_t created;
+};
+static struct cred_list *wcache_cred_list;
+
+static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ struct cred_list *cred;
+
+ if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
+
+ cred = SMB_MALLOC_P(struct cred_list);
+ if (cred == NULL) {
+ DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
+ return -1;
+ }
+
+ ZERO_STRUCTP(cred);
+
+ /* save a copy of the key */
+
+ fstrcpy(cred->name, kbuf.dptr);
+ DLIST_ADD(wcache_cred_list, cred);
+ }
+
+ return 0;
+}
+
+NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ NTSTATUS status;
+ int ret;
+ struct cred_list *cred, *oldest = NULL;
+
+ if (!cache->tdb) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ /* we possibly already have an entry */
+ if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
+
+ fstring key_str;
+
+ DEBUG(11,("we already have an entry, deleting that\n"));
+
+ fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
+
+ tdb_delete(cache->tdb, string_tdb_data(key_str));
+
+ return NT_STATUS_OK;
+ }
+
+ ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
+ if (ret == 0) {
+ return NT_STATUS_OK;
+ } else if (ret == -1) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ ZERO_STRUCTP(oldest);
+
+ for (cred = wcache_cred_list; cred; cred = cred->next) {
+
+ TDB_DATA data;
+ time_t t;
+
+ data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
+ if (!data.dptr) {
+ DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
+ cred->name));
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ goto done;
+ }
+
+ t = IVAL(data.dptr, 0);
+ SAFE_FREE(data.dptr);
+
+ if (!oldest) {
+ oldest = SMB_MALLOC_P(struct cred_list);
+ if (oldest == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ fstrcpy(oldest->name, cred->name);
+ oldest->created = t;
+ continue;
+ }
+
+ if (t < oldest->created) {
+ fstrcpy(oldest->name, cred->name);
+ oldest->created = t;
+ }
+ }
+
+ if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
+ status = NT_STATUS_OK;
+ } else {
+ status = NT_STATUS_UNSUCCESSFUL;
+ }
+done:
+ SAFE_FREE(wcache_cred_list);
+ SAFE_FREE(oldest);
+
+ return status;
+}
+
+/* Change the global online/offline state. */
+BOOL set_global_winbindd_state_offline(void)
+{
+ TDB_DATA data;
+ int err;
+
+ DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
+
+ /* Only go offline if someone has created
+ the key "WINBINDD_OFFLINE" in the cache tdb. */
+
+ if (wcache == NULL || wcache->tdb == NULL) {
+ DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
+ return False;
+ }
+
+ if (!lp_winbind_offline_logon()) {
+ DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
+ return False;
+ }
+
+ if (global_winbindd_offline_state) {
+ /* Already offline. */
+ return True;
+ }
+
+ wcache->tdb->ecode = 0;
+
+ data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
+
+ /* As this is a key with no data we don't need to free, we
+ check for existence by looking at tdb_err. */
+
+ err = tdb_error(wcache->tdb);
+
+ if (err == TDB_ERR_NOEXIST) {
+ DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
+ return False;
+ } else {
+ DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
+ global_winbindd_offline_state = True;
+ return True;
+ }
+}
+
+void set_global_winbindd_state_online(void)
+{
+ DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
+
+ if (!lp_winbind_offline_logon()) {
+ DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
+ return;
+ }
+
+ if (!global_winbindd_offline_state) {
+ /* Already online. */
+ return;
+ }
+ global_winbindd_offline_state = False;
+
+ if (!wcache->tdb) {
+ return;
+ }
+
+ /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
+ tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
+}
+
+/* the cache backend methods are exposed via this structure */
+struct winbindd_methods cache_methods = {
+ True,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ query_user,
+ lookup_usergroups,
+ lookup_useraliases,
+ lookup_groupmem,
+ sequence_number,
+ lockout_policy,
+ password_policy,
+ trusted_domains
+};
diff --git a/source3/nsswitch/winbindd_cm.c b/source3/nsswitch/winbindd_cm.c
index 177ac54d3e..568078f86e 100644
--- a/source3/nsswitch/winbindd_cm.c
+++ b/source3/nsswitch/winbindd_cm.c
@@ -784,26 +784,32 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
- if ((strlen(domain->dcname) > 0)
- && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, domain->dcname))
- && (resolve_name(domain->dcname, &domain->dcaddr.sin_addr, 0x20)))
+ if ((strlen(domain->dcname) > 0)
+ && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, domain->dcname))
+ && (resolve_name(domain->dcname, &domain->dcaddr.sin_addr, 0x20)))
{
struct sockaddr_in *addrs = NULL;
int num_addrs = 0;
int dummy = 0;
-
add_sockaddr_to_array(mem_ctx, domain->dcaddr.sin_addr, 445, &addrs, &num_addrs);
add_sockaddr_to_array(mem_ctx, domain->dcaddr.sin_addr, 139, &addrs, &num_addrs);
if (!open_any_socket_out(addrs, num_addrs, 10000, &dummy, &fd)) {
+ domain->online = False;
fd = -1;
}
}
if ((fd == -1)
- && !find_new_dc(mem_ctx, domain, domain->dcname, &domain->dcaddr, &fd))
+ && !find_new_dc(mem_ctx, domain, domain->dcname, &domain->dcaddr, &fd))
{
+ /* This is the one place where we will
+ set the global winbindd offline state
+ to true, if a "WINBINDD_OFFLINE" entry
+ is found in the winbindd cache. */
+ set_global_winbindd_state_offline();
+ domain->online = False;
break;
}
@@ -816,11 +822,19 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
break;
}
+ if (NT_STATUS_IS_OK(result)) {
+ if (domain->online == False) {
+ /* We're changing state from offline to online. */
+ set_global_winbindd_state_online();
+ }
+ domain->online = True;
+ }
+
talloc_destroy(mem_ctx);
return result;
}
-/* Return true if a connection is still alive */
+/* Close down all open pipes on a connection. */
void invalidate_cm_connection(struct winbindd_cm_conn *conn)
{
diff --git a/source3/nsswitch/winbindd_cred_cache.c b/source3/nsswitch/winbindd_cred_cache.c
new file mode 100644
index 0000000000..a8aab04031
--- /dev/null
+++ b/source3/nsswitch/winbindd_cred_cache.c
@@ -0,0 +1,270 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - krb5 credential cache funcions
+
+ Copyright (C) Guenther Deschner 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+#define MAX_CCACHES 100
+
+static struct WINBINDD_CCACHE_ENTRY *ccache_list;
+
+static TALLOC_CTX *mem_ctx;
+
+const char *get_ccache_name_by_username(const char *username)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry;
+
+ for (entry = ccache_list; entry; entry = entry->next) {
+ if (strequal(entry->username, username)) {
+ return entry->ccname;
+ }
+ }
+ return NULL;
+}
+
+struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry;
+
+ for (entry = ccache_list; entry; entry = entry->next) {
+ if (strequal(entry->username, username)) {
+ return entry;
+ }
+ }
+ return NULL;
+}
+
+static int ccache_entry_count(void)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry;
+ int i = 0;
+
+ for (entry = ccache_list; entry; entry = entry->next) {
+ i++;
+ }
+ return i;
+}
+
+NTSTATUS remove_ccache_by_ccname(const char *ccname)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry;
+
+ for (entry = ccache_list; entry; entry = entry->next) {
+ if (strequal(entry->ccname, ccname)) {
+ DLIST_REMOVE(ccache_list, entry);
+ talloc_free(entry->event); /* unregisters events */
+ return talloc_free(entry) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+static void krb5_ticket_refresh_handler(struct timed_event *te,
+ const struct timeval *now,
+ void *private_data)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry =
+ talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
+ int ret;
+ time_t new_start;
+ struct timeval t;
+
+
+ DEBUG(10,("krb5_ticket_refresh_handler called\n"));
+ DEBUGADD(10,("event called for: %s, %s\n", entry->ccname, entry->username));
+
+ talloc_free(entry->event);
+
+#ifdef HAVE_KRB5
+
+ /* Kinit again if we have the user password and we can't renew the old
+ * tgt anymore */
+
+ if ((entry->renew_until < time(NULL)) && (entry->pass != NULL)) {
+
+ seteuid(entry->uid);
+
+ ret = kerberos_kinit_password(entry->principal_name,
+ entry->pass,
+ 0, /* hm, can we do time correction here ? */
+ &entry->refresh_time,
+ &entry->renew_until,
+ entry->ccname,
+ False, /* no PAC required anymore */
+ WINBINDD_PAM_AUTH_KRB5_RENEW_TIME);
+ seteuid(0);
+
+ if (ret) {
+ DEBUG(3,("could not re-kinit: %s\n", error_message(ret)));
+ talloc_free(entry->event);
+ return;
+ }
+
+ DEBUG(10,("successful re-kinit for: %s in ccache: %s\n",
+ entry->principal_name, entry->ccname));
+
+ new_start = entry->refresh_time;
+
+ goto done;
+ }
+
+ seteuid(entry->uid);
+
+ ret = smb_krb5_renew_ticket(entry->ccname,
+ entry->principal_name,
+ entry->service,
+ &new_start);
+ seteuid(0);
+
+ if (ret) {
+ DEBUG(3,("could not renew tickets: %s\n", error_message(ret)));
+ /* maybe we are beyond the renewing window */
+ return;
+ }
+
+done:
+
+ t = timeval_set(new_start, 0);
+
+ entry->event = add_timed_event(mem_ctx,
+ t,
+ "krb5_ticket_refresh_handler",
+ krb5_ticket_refresh_handler,
+ entry);
+
+#endif
+}
+
+NTSTATUS add_ccache_to_list(const char *princ_name,
+ const char *ccname,
+ const char *service,
+ const char *username,
+ const char *sid_string,
+ const char *pass,
+ uid_t uid,
+ time_t create_time,
+ time_t ticket_end,
+ time_t renew_until,
+ BOOL schedule_refresh_event)
+{
+ struct WINBINDD_CCACHE_ENTRY *new_entry = NULL;
+ NTSTATUS status;
+
+ if ((username == NULL && sid_string == NULL && princ_name == NULL) ||
+ ccname == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = init_ccache_list();
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (mem_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ccache_entry_count() + 1 > MAX_CCACHES) {
+ DEBUG(10,("add_ccache_to_list: max number of ccaches reached\n"));
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ new_entry = TALLOC_P(mem_ctx, struct WINBINDD_CCACHE_ENTRY);
+ if (new_entry == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(new_entry);
+
+ if (username) {
+ new_entry->username = talloc_strdup(mem_ctx, username);
+ NT_STATUS_HAVE_NO_MEMORY(new_entry->username);
+ }
+ if (sid_string) {
+ new_entry->sid_string = talloc_strdup(mem_ctx, sid_string);
+ NT_STATUS_HAVE_NO_MEMORY(new_entry->sid_string);
+ }
+ if (princ_name) {
+ new_entry->principal_name = talloc_strdup(mem_ctx, princ_name);
+ NT_STATUS_HAVE_NO_MEMORY(new_entry->principal_name);
+ }
+ if (service) {
+ new_entry->service = talloc_strdup(mem_ctx, service);
+ NT_STATUS_HAVE_NO_MEMORY(new_entry->service);
+ }
+ if (pass) {
+ new_entry->pass = talloc_strdup(mem_ctx, pass);
+ NT_STATUS_HAVE_NO_MEMORY(new_entry->pass);
+ }
+
+ new_entry->create_time = create_time;
+ new_entry->renew_until = renew_until;
+ new_entry->ccname = talloc_strdup(mem_ctx, ccname);
+ if (new_entry->ccname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ new_entry->uid = uid;
+
+
+#ifndef WITH_KCM /* no point in doing the refresh in KCM and by ourself */
+
+ if (schedule_refresh_event && renew_until > 0) {
+
+ struct timeval t = timeval_set((ticket_end -1 ), 0);
+
+ new_entry->event = add_timed_event(mem_ctx,
+ t,
+ "krb5_ticket_refresh_handler",
+ krb5_ticket_refresh_handler,
+ new_entry);
+ }
+#endif /* WITH_KCM */
+
+ DLIST_ADD(ccache_list, new_entry);
+
+ DEBUG(10,("add_ccache_to_list: added ccache [%s] for user [%s] to the list\n", ccname, username));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS destroy_ccache_list(void)
+{
+ return talloc_destroy(mem_ctx) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS init_ccache_list(void)
+{
+ if (ccache_list) {
+ return NT_STATUS_OK;
+ }
+
+ mem_ctx = talloc_init("winbindd_ccache_krb5_handling");
+ if (mem_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(ccache_list);
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/nsswitch/winbindd_creds.c b/source3/nsswitch/winbindd_creds.c
new file mode 100644
index 0000000000..d37e9019db
--- /dev/null
+++ b/source3/nsswitch/winbindd_creds.c
@@ -0,0 +1,162 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - cached credentials funcions
+
+ Copyright (C) Guenther Deschner 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+#define MAX_CACHED_LOGINS 10
+
+NTSTATUS winbindd_get_creds(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ NET_USER_INFO_3 **info3,
+ const uint8 *cached_nt_pass[NT_HASH_LEN])
+{
+ NET_USER_INFO_3 *info;
+ NTSTATUS status;
+
+ status = wcache_get_creds(domain, mem_ctx, sid, cached_nt_pass);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ info = netsamlogon_cache_get(mem_ctx, sid);
+ if (info == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ *info3 = info;
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS winbindd_store_creds(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *user,
+ const char *pass,
+ NET_USER_INFO_3 *info3,
+ const DOM_SID *user_sid)
+{
+ NTSTATUS status;
+ uchar nt_pass[NT_HASH_LEN];
+ DOM_SID cred_sid;
+
+ if (info3 != NULL) {
+
+ DOM_SID sid;
+ sid_copy(&sid, &(info3->dom_sid.sid));
+ sid_append_rid(&sid, info3->user_rid);
+ sid_copy(&cred_sid, &sid);
+ info3->user_flgs |= LOGON_CACHED_ACCOUNT;
+
+ } else if (user_sid != NULL) {
+
+ sid_copy(&cred_sid, user_sid);
+
+ } else if (user != NULL) {
+
+ /* do lookup ourself */
+
+ enum SID_NAME_USE type;
+
+ if (!lookup_cached_name(mem_ctx,
+ domain->name,
+ user,
+ &cred_sid,
+ &type)) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ } else {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (pass) {
+
+ int count = 0;
+
+ status = wcache_count_cached_creds(domain, &count);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(11,("we have %d cached creds\n", count));
+
+ if (count + 1 > MAX_CACHED_LOGINS) {
+
+ DEBUG(10,("need to delete the oldest cached login\n"));
+
+ status = wcache_remove_oldest_cached_creds(domain, &cred_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("failed to remove oldest cached cred: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ E_md4hash(pass, nt_pass);
+
+ dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
+
+ status = wcache_save_creds(domain, mem_ctx, &cred_sid, nt_pass);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ if (info3 != NULL && user != NULL) {
+ if (!netsamlogon_cache_store(user, info3)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS winbindd_update_creds_by_info3(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *user,
+ const char *pass,
+ NET_USER_INFO_3 *info3)
+{
+ return winbindd_store_creds(domain, mem_ctx, user, pass, info3, NULL);
+}
+
+NTSTATUS winbindd_update_creds_by_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ const char *pass)
+{
+ return winbindd_store_creds(domain, mem_ctx, NULL, pass, NULL, sid);
+}
+
+NTSTATUS winbindd_update_creds_by_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *user,
+ const char *pass)
+{
+ return winbindd_store_creds(domain, mem_ctx, user, pass, NULL, NULL);
+}
+
+
diff --git a/source3/nsswitch/winbindd_dual.c b/source3/nsswitch/winbindd_dual.c
index 38030ee9ac..1988f36b51 100644
--- a/source3/nsswitch/winbindd_dual.c
+++ b/source3/nsswitch/winbindd_dual.c
@@ -197,6 +197,8 @@ static void async_reply_recv(void *private_data, BOOL success)
SMB_ASSERT(cache_retrieve_response(child->pid,
state->response));
+ cache_cleanup_response(child->pid);
+
DLIST_REMOVE(child->requests, state);
schedule_async_request(child);
@@ -233,6 +235,8 @@ static void schedule_async_request(struct winbindd_child *child)
setup_async_write(&child->event, request->request,
sizeof(*request->request),
async_main_request_sent, request);
+
+ talloc_destroy(child->mem_ctx);
return;
}
@@ -347,6 +351,7 @@ static struct winbindd_child_dispatch_table child_dispatch_table[] = {
{ WINBINDD_SHOW_SEQUENCE, winbindd_dual_show_sequence, "SHOW_SEQUENCE" },
{ WINBINDD_PAM_AUTH, winbindd_dual_pam_auth, "PAM_AUTH" },
{ WINBINDD_PAM_AUTH_CRAP, winbindd_dual_pam_auth_crap, "AUTH_CRAP" },
+ { WINBINDD_PAM_LOGOFF, winbindd_dual_pam_logoff, "PAM_LOGOFF" },
{ WINBINDD_CHECK_MACHACC, winbindd_dual_check_machine_acct, "CHECK_MACHACC" },
{ WINBINDD_DUAL_SID2UID, winbindd_dual_sid2uid, "DUAL_SID2UID" },
{ WINBINDD_DUAL_SID2GID, winbindd_dual_sid2gid, "DUAL_SID2GID" },
@@ -356,8 +361,8 @@ static struct winbindd_child_dispatch_table child_dispatch_table[] = {
{ WINBINDD_DUAL_NAME2GID, winbindd_dual_name2gid, "DUAL_NAME2GID" },
{ WINBINDD_DUAL_IDMAPSET, winbindd_dual_idmapset, "DUAL_IDMAPSET" },
{ WINBINDD_DUAL_USERINFO, winbindd_dual_userinfo, "DUAL_USERINFO" },
- { WINBINDD_ALLOCATE_RID, winbindd_dual_allocate_rid, "ALLOCATE_RID" },
- { WINBINDD_ALLOCATE_RID_AND_GID, winbindd_dual_allocate_rid_and_gid, "ALLOCATE_RID_AND_GID" },
+ { WINBINDD_ALLOCATE_UID, winbindd_dual_allocate_uid, "ALLOCATE_UID" },
+ { WINBINDD_ALLOCATE_GID, winbindd_dual_allocate_gid, "ALLOCATE_GID" },
{ WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups, "GETUSERDOMGROUPS" },
{ WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases, "GETSIDALIASES" },
/* End of list */
@@ -444,6 +449,137 @@ void winbind_child_died(pid_t pid)
schedule_async_request(child);
}
+/* Forward the online/offline messages to our children. */
+void winbind_msg_offline(int msg_type, struct process_id src, void *buf, size_t len)
+{
+ struct winbindd_child *child;
+
+ DEBUG(10,("winbind_msg_offline: got offline message.\n"));
+
+ if (!lp_winbind_offline_logon()) {
+ DEBUG(10,("winbind_msg_offline: rejecting offline message.\n"));
+ return;
+ }
+
+ /* Set our global state as offline. */
+ if (!set_global_winbindd_state_offline()) {
+ DEBUG(10,("winbind_msg_offline: offline request failed.\n"));
+ return;
+ }
+
+ for (child = children; child != NULL; child = child->next) {
+ DEBUG(10,("winbind_msg_offline: sending message to pid %u.\n",
+ (unsigned int)child->pid ));
+ message_send_pid(pid_to_procid(child->pid), MSG_WINBIND_OFFLINE, NULL, 0, False);
+ }
+}
+
+/* Forward the online/offline messages to our children. */
+void winbind_msg_online(int msg_type, struct process_id src, void *buf, size_t len)
+{
+ struct winbindd_child *child;
+
+ DEBUG(10,("winbind_msg_online: got online message.\n"));
+
+ if (!lp_winbind_offline_logon()) {
+ DEBUG(10,("winbind_msg_online: rejecting online message.\n"));
+ return;
+ }
+
+ /* Set our global state as online. */
+ set_global_winbindd_state_online();
+
+ for (child = children; child != NULL; child = child->next) {
+ DEBUG(10,("winbind_msg_online: sending message to pid %u.\n",
+ (unsigned int)child->pid ));
+ message_send_pid(pid_to_procid(child->pid), MSG_WINBIND_ONLINE, NULL, 0, False);
+ }
+}
+
+static void account_lockout_policy_handler(struct timed_event *te,
+ const struct timeval *now,
+ void *private_data)
+{
+ struct winbindd_child *child = private_data;
+
+ struct winbindd_methods *methods;
+ SAM_UNK_INFO_12 lockout_policy;
+ NTSTATUS result;
+
+ DEBUG(10,("account_lockout_policy_handler called\n"));
+
+ if (child->timed_event) {
+ talloc_free(child->timed_event);
+ }
+
+ methods = child->domain->methods;
+
+ result = methods->lockout_policy(child->domain, child->mem_ctx, &lockout_policy);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("account_lockout_policy_handler: failed to call lockout_policy\n"));
+ return;
+ }
+
+ child->timed_event = add_timed_event(child->mem_ctx,
+ timeval_current_ofs(3600, 0),
+ "account_lockout_policy_handler",
+ account_lockout_policy_handler,
+ child);
+}
+
+/* Deal with a request to go offline. */
+
+static void child_msg_offline(int msg_type, struct process_id src, void *buf, size_t len)
+{
+ struct winbindd_domain *domain;
+
+ DEBUG(5,("child_msg_offline received.\n"));
+
+ if (!lp_winbind_offline_logon()) {
+ DEBUG(10,("child_msg_offline: rejecting offline message.\n"));
+ return;
+ }
+
+ /* Set our global state as offline. */
+ if (!set_global_winbindd_state_offline()) {
+ DEBUG(10,("child_msg_offline: offline request failed.\n"));
+ return;
+ }
+
+ /* Mark all our domains as offline. */
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ DEBUG(5,("child_msg_offline: marking %s offline.\n", domain->name));
+ domain->online = False;
+ }
+}
+
+/* Deal with a request to go online. */
+
+static void child_msg_online(int msg_type, struct process_id src, void *buf, size_t len)
+{
+ struct winbindd_domain *domain;
+
+ DEBUG(5,("child_msg_online received.\n"));
+
+ if (!lp_winbind_offline_logon()) {
+ DEBUG(10,("child_msg_online: rejecting online message.\n"));
+ return;
+ }
+
+ /* Set our global state as online. */
+ set_global_winbindd_state_online();
+
+ /* Mark everything online - delete any negative cache entries
+ to force an immediate reconnect. */
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ DEBUG(5,("child_msg_online: marking %s online.\n", domain->name));
+ domain->online = True;
+ check_negative_conn_cache_timeout(domain->name, domain->dcname, 0);
+ }
+}
+
static BOOL fork_domain_child(struct winbindd_child *child)
{
int fdpair[2];
@@ -459,10 +595,15 @@ static BOOL fork_domain_child(struct winbindd_child *child)
ZERO_STRUCT(state);
state.pid = getpid();
+ /* Ensure we don't process messages whilst we're
+ changing the disposition for the child. */
+ message_block();
+
child->pid = sys_fork();
if (child->pid == -1) {
DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
+ message_unblock();
return False;
}
@@ -475,6 +616,8 @@ static BOOL fork_domain_child(struct winbindd_child *child)
child->event.flags = 0;
child->requests = NULL;
add_fd_event(&child->event);
+ /* We're ok with online/offline messages now. */
+ message_unblock();
return True;
}
@@ -495,12 +638,80 @@ static BOOL fork_domain_child(struct winbindd_child *child)
lp_set_logfile(child->logfilename);
reopen_logs();
}
-
+
+ /* Don't handle the same messages as our parent. */
+ message_deregister(MSG_SMB_CONF_UPDATED);
+ message_deregister(MSG_SHUTDOWN);
+ message_deregister(MSG_WINBIND_OFFLINE);
+ message_deregister(MSG_WINBIND_ONLINE);
+
+ /* The child is ok with online/offline messages now. */
+ message_unblock();
+
+ child->mem_ctx = talloc_init("child_mem_ctx");
+ if (child->mem_ctx == NULL) {
+ return False;
+ }
+
+ if (child->domain != NULL) {
+ /* We might be in the idmap child...*/
+ child->timed_event = add_timed_event(
+ child->mem_ctx, timeval_zero(),
+ "account_lockout_policy_handler",
+ account_lockout_policy_handler,
+ child);
+ }
+
+ /* Handle online/offline messages. */
+ message_register(MSG_WINBIND_OFFLINE,child_msg_offline);
+ message_register(MSG_WINBIND_ONLINE,child_msg_online);
+
while (1) {
+
+ int ret;
+ fd_set read_fds;
+ struct timeval t;
+ struct timeval *tp;
+ struct timeval now;
+
/* free up any talloc memory */
lp_talloc_free();
main_loop_talloc_free();
+ run_events();
+
+ GetTimeOfDay(&now);
+
+ tp = get_timed_events_timeout(&t, (time_t)-1);
+ if (tp) {
+ DEBUG(11,("select will use timeout of %d seconds\n", (int)tp->tv_sec));
+ }
+
+ /* Handle messages */
+
+ message_dispatch();
+
+ FD_ZERO(&read_fds);
+ FD_SET(state.sock, &read_fds);
+
+ ret = sys_select(state.sock + 1, &read_fds, NULL, NULL, tp);
+
+ if (ret == 0) {
+ DEBUG(10,("nothing is ready yet, continue\n"));
+ continue;
+ }
+
+ if (ret == -1 && errno == EINTR) {
+ /* We got a signal - continue. */
+ continue;
+ }
+
+ if (ret == -1 && errno != EINTR) {
+ DEBUG(0,("select error occured\n"));
+ perror("select");
+ return False;
+ }
+
/* fetch a request from the main daemon */
child_read_request(&state);
diff --git a/source3/nsswitch/winbindd_group.c b/source3/nsswitch/winbindd_group.c
index ff2d19f5fc..a328cebac4 100644
--- a/source3/nsswitch/winbindd_group.c
+++ b/source3/nsswitch/winbindd_group.c
@@ -140,7 +140,7 @@ static BOOL fill_grent_mem(struct winbindd_domain *domain,
/* make sure to allow machine accounts */
if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
- DEBUG(3, ("name %s isn't a domain user\n", the_name));
+ DEBUG(3, ("name %s isn't a domain user (%s)\n", the_name, sid_type_lookup(name_types[i])));
continue;
}
@@ -208,6 +208,8 @@ void winbindd_getgrnam(struct winbindd_cli_state *state)
char *tmp, *gr_mem;
size_t gr_mem_len;
gid_t gid;
+ union unid_t id;
+ NTSTATUS status;
/* Ensure null termination */
state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
@@ -241,8 +243,8 @@ void winbindd_getgrnam(struct winbindd_cli_state *state)
/* should we deal with users for our domain? */
if ( lp_winbind_trusted_domains_only() && domain->primary) {
- DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
- name_domain, name_group));
+ DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
+ "getgrnam() for %s\\%s.\n", name_domain, name_group));
request_error(state);
return;
}
@@ -262,18 +264,35 @@ void winbindd_getgrnam(struct winbindd_cli_state *state)
((name_type==SID_NAME_ALIAS) && domain->internal) ||
((name_type==SID_NAME_WKN_GRP) && domain->internal)) )
{
- DEBUG(1, ("name '%s' is not a local, domain or builtin group: %d\n",
- name_group, name_type));
+ DEBUG(1, ("name '%s' is not a local, domain or builtin "
+ "group: %d\n", name_group, name_type));
request_error(state);
return;
}
- if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
- DEBUG(1, ("error converting unix gid to sid\n"));
- request_error(state);
- return;
+ /* Try to get the GID */
+
+ status = idmap_sid_to_gid(&group_sid, &gid, 0);
+
+ if (NT_STATUS_IS_OK(status)) {
+ goto got_gid;
+ }
+
+ /* Maybe it's one of our aliases in passdb */
+
+ if (pdb_sid_to_id(&group_sid, &id, &name_type) &&
+ ((name_type == SID_NAME_ALIAS) ||
+ (name_type == SID_NAME_WKN_GRP))) {
+ gid = id.gid;
+ goto got_gid;
}
+ DEBUG(1, ("error converting unix gid to sid\n"));
+ request_error(state);
+ return;
+
+ got_gid:
+
if (!fill_grent(&state->response.data.gr, name_domain,
name_group, gid) ||
!fill_grent_mem(domain, &group_sid, name_type,
@@ -303,6 +322,7 @@ void winbindd_getgrgid(struct winbindd_cli_state *state)
fstring group_name;
size_t gr_mem_len;
char *gr_mem;
+ NTSTATUS status;
DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
(unsigned long)state->request.data.gid));
@@ -315,14 +335,29 @@ void winbindd_getgrgid(struct winbindd_cli_state *state)
return;
}
- /* Get rid from gid */
- if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid, 0))) {
- DEBUG(1, ("could not convert gid %lu to rid\n",
- (unsigned long)state->request.data.gid));
- request_error(state);
- return;
+ /* Get sid from gid */
+
+ status = idmap_gid_to_sid(&group_sid, state->request.data.gid, 0);
+ if (NT_STATUS_IS_OK(status)) {
+ /* This is a remote one */
+ goto got_sid;
}
+ /* Ok, this might be "ours", i.e. an alias */
+
+ if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
+ lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
+ (name_type == SID_NAME_ALIAS)) {
+ /* Hey, got an alias */
+ goto got_sid;
+ }
+
+ DEBUG(1, ("could not convert gid %lu to sid\n",
+ (unsigned long)state->request.data.gid));
+ request_error(state);
+ return;
+
+ got_sid:
/* Get name from sid */
if (!winbindd_lookup_name_by_sid(state->mem_ctx, &group_sid, dom_name,
@@ -665,13 +700,32 @@ void winbindd_getgrent(struct winbindd_cli_state *state)
sid_copy(&group_sid, &domain->sid);
sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
- if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
-
- DEBUG(1, ("could not look up gid for group %s\n",
- name_list[ent->sam_entry_index].acct_name));
-
- ent->sam_entry_index++;
- goto tryagain;
+ if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid,
+ &group_gid, 0))) {
+ union unid_t id;
+ enum SID_NAME_USE type;
+
+ DEBUG(10, ("SID %s not in idmap\n",
+ sid_string_static(&group_sid)));
+
+ if (!pdb_sid_to_id(&group_sid, &id, &type)) {
+ DEBUG(1, ("could not look up gid for group "
+ "%s\n",
+ name_list[ent->sam_entry_index].acct_name));
+ ent->sam_entry_index++;
+ goto tryagain;
+ }
+
+ if ((type != SID_NAME_DOM_GRP) &&
+ (type != SID_NAME_ALIAS) &&
+ (type != SID_NAME_WKN_GRP)) {
+ DEBUG(1, ("Group %s is a %s, not a group\n",
+ sid_type_lookup(type),
+ name_list[ent->sam_entry_index].acct_name));
+ ent->sam_entry_index++;
+ goto tryagain;
+ }
+ group_gid = id.gid;
}
DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
@@ -1187,4 +1241,3 @@ enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *doma
return WINBINDD_OK;
}
-
diff --git a/source3/nsswitch/winbindd_misc.c b/source3/nsswitch/winbindd_misc.c
index 1fbf4b33df..b6aecae393 100644
--- a/source3/nsswitch/winbindd_misc.c
+++ b/source3/nsswitch/winbindd_misc.c
@@ -115,6 +115,7 @@ enum winbindd_result winbindd_dual_list_trusted_domains(struct winbindd_domain *
int extra_data_len = 0;
char *extra_data;
NTSTATUS result;
+ BOOL have_own_domain = False;
DEBUG(3, ("[%5lu]: list trusted domains\n",
(unsigned long)state->pid));
@@ -137,6 +138,22 @@ enum winbindd_result winbindd_dual_list_trusted_domains(struct winbindd_domain *
names[i],
alt_names[i] ? alt_names[i] : names[i],
sid_string_static(&sids[i]));
+ /* add our primary domain */
+
+ for (i=0; i<num_domains; i++) {
+ if (strequal(names[i], domain->name)) {
+ have_own_domain = True;
+ break;
+ }
+ }
+
+ if (state->request.data.list_all_domains && !have_own_domain) {
+ extra_data = talloc_asprintf(state->mem_ctx, "%s\n%s\\%s\\%s",
+ extra_data,
+ domain->name,
+ domain->alt_name ? domain->alt_name : domain->name,
+ sid_string_static(&domain->sid));
+ }
/* This is a bit excessive, but the extra data sooner or later will be
talloc'ed */
diff --git a/source3/nsswitch/winbindd_nss.h b/source3/nsswitch/winbindd_nss.h
index eda68ae5c7..033e51d794 100644
--- a/source3/nsswitch/winbindd_nss.h
+++ b/source3/nsswitch/winbindd_nss.h
@@ -34,7 +34,7 @@
/* Update this when you change the interface. */
-#define WINBIND_INTERFACE_VERSION 11
+#define WINBIND_INTERFACE_VERSION 14
/* Socket commands */
@@ -64,6 +64,7 @@ enum winbindd_cmd {
WINBINDD_PAM_AUTH,
WINBINDD_PAM_AUTH_CRAP,
WINBINDD_PAM_CHAUTHTOK,
+ WINBINDD_PAM_LOGOFF,
/* List various things */
@@ -82,8 +83,9 @@ enum winbindd_cmd {
WINBINDD_SID_TO_GID,
WINBINDD_UID_TO_SID,
WINBINDD_GID_TO_SID,
- WINBINDD_ALLOCATE_RID,
- WINBINDD_ALLOCATE_RID_AND_GID,
+
+ WINBINDD_ALLOCATE_UID,
+ WINBINDD_ALLOCATE_GID,
/* Miscellaneous other stuff */
@@ -114,7 +116,7 @@ enum winbindd_cmd {
/* return a list of group sids for a user sid */
WINBINDD_GETUSERSIDS,
- /* Return the domain groups a user is in */
+ /* Various group queries */
WINBINDD_GETUSERDOMGROUPS,
/* Initialize connection in a child */
@@ -165,7 +167,6 @@ typedef struct winbindd_gr {
#define WBFLAG_PAM_LMKEY 0x0008
#define WBFLAG_PAM_CONTACT_TRUSTDOM 0x0010
#define WBFLAG_QUERY_ONLY 0x0020
-#define WBFLAG_ALLOCATE_RID 0x0040
#define WBFLAG_PAM_UNIX_NAME 0x0080
#define WBFLAG_PAM_AFS_TOKEN 0x0100
#define WBFLAG_PAM_NT_STATUS_SQUASH 0x0200
@@ -175,6 +176,10 @@ typedef struct winbindd_gr {
/* Flag to say this is a winbindd internal send - don't recurse. */
#define WBFLAG_RECURSE 0x0800
+#define WBFLAG_PAM_KRB5 0x1000
+#define WBFLAG_PAM_FALLBACK_AFTER_KRB5 0x2000
+#define WBFLAG_PAM_CACHED_LOGIN 0x4000
+
#define WINBINDD_MAX_EXTRA_DATA (128*1024)
/* Winbind request structure */
@@ -199,6 +204,8 @@ struct winbindd_request {
fstring user;
fstring pass;
fstring require_membership_of_sid;
+ fstring krb5_cc_type;
+ uid_t uid;
} auth; /* pam_winbind auth module */
struct {
unsigned char chal[8];
@@ -217,6 +224,11 @@ struct winbindd_request {
fstring oldpass;
fstring newpass;
} chauthtok; /* pam_winbind passwd module */
+ struct {
+ fstring user;
+ fstring krb5ccname;
+ uid_t uid;
+ } logoff; /* pam_winbind session module */
fstring sid; /* lookupsid, sid_to_[ug]id */
struct {
fstring dom_name; /* lookupname */
@@ -242,6 +254,7 @@ struct winbindd_request {
gid_t gid;
fstring sid;
} dual_idmapset;
+ BOOL list_all_domains;
} data;
char *extra_data;
size_t extra_len;
@@ -307,12 +320,41 @@ struct winbindd_response {
int pam_error;
char user_session_key[16];
char first_8_lm_hash[8];
+ fstring krb5ccname;
+ struct policy_settings {
+ uint16 min_length_password;
+ uint16 password_history;
+ uint32 password_properties;
+ time_t expire;
+ time_t min_passwordage;
+ } policy;
+ uint32 reject_reason;
+ struct info3_text {
+ time_t logon_time;
+ time_t logoff_time;
+ time_t kickoff_time;
+ time_t pass_last_set_time;
+ time_t pass_can_change_time;
+ time_t pass_must_change_time;
+ uint16 logon_count;
+ uint16 bad_pw_count;
+ fstring user_sid;
+ fstring group_sid;
+ fstring dom_sid;
+ uint32 num_groups;
+ uint32 user_flgs;
+ uint32 acct_flags;
+ uint32 num_other_sids;
+ fstring user_name;
+ fstring full_name;
+ fstring logon_script;
+ fstring profile_path;
+ fstring home_dir;
+ fstring dir_drive;
+ fstring logon_srv;
+ fstring logon_dom;
+ } info3;
} auth;
- uint32 rid; /* create user or group or allocate rid */
- struct {
- uint32 rid;
- gid_t gid;
- } rid_and_gid;
struct {
fstring name;
fstring alt_name;
@@ -336,4 +378,20 @@ struct winbindd_response {
void *extra_data; /* getgrnam, getgrgid, getgrent */
};
+struct WINBINDD_CCACHE_ENTRY {
+ const char *principal_name;
+ const char *ccname;
+ const char *service;
+ const char *username;
+ const char *sid_string;
+ const char *pass;
+ uid_t uid;
+ time_t create_time;
+ time_t renew_until;
+ BOOL refresh_tgt;
+ time_t refresh_time;
+ struct timed_event *event;
+ struct WINBINDD_CCACHE_ENTRY *next, *prev;
+};
+
#endif
diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c
index 890007ae38..ab20102f79 100644
--- a/source3/nsswitch/winbindd_pam.c
+++ b/source3/nsswitch/winbindd_pam.c
@@ -6,6 +6,7 @@
Copyright (C) Andrew Tridgell 2000
Copyright (C) Tim Potter 2001
Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Guenther Deschner 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
@@ -27,6 +28,70 @@
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_WINBIND
+static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
+ struct winbindd_cli_state *state,
+ NET_USER_INFO_3 *info3)
+{
+ DOM_SID user_sid, group_sid;
+ fstring str_sid;
+
+ state->response.data.auth.info3.logon_time =
+ nt_time_to_unix(&(info3->logon_time));
+ state->response.data.auth.info3.logoff_time =
+ nt_time_to_unix(&(info3->logoff_time));
+ state->response.data.auth.info3.kickoff_time =
+ nt_time_to_unix(&(info3->kickoff_time));
+ state->response.data.auth.info3.pass_last_set_time =
+ nt_time_to_unix(&(info3->pass_last_set_time));
+ state->response.data.auth.info3.pass_can_change_time =
+ nt_time_to_unix(&(info3->pass_can_change_time));
+ state->response.data.auth.info3.pass_must_change_time =
+ nt_time_to_unix(&(info3->pass_must_change_time));
+
+ state->response.data.auth.info3.logon_count = info3->logon_count;
+ state->response.data.auth.info3.bad_pw_count = info3->bad_pw_count;
+
+ sid_copy(&user_sid, &(info3->dom_sid.sid));
+ sid_append_rid(&user_sid, info3->user_rid);
+
+ sid_to_string(str_sid, &user_sid);
+ fstrcpy(state->response.data.auth.info3.user_sid, str_sid);
+
+ sid_copy(&group_sid, &(info3->dom_sid.sid));
+ sid_append_rid(&group_sid, info3->group_rid);
+
+ sid_to_string(str_sid, &group_sid);
+ fstrcpy(state->response.data.auth.info3.group_sid, str_sid);
+
+ sid_to_string(str_sid, &(info3->dom_sid.sid));
+ fstrcpy(state->response.data.auth.info3.dom_sid, str_sid);
+
+ state->response.data.auth.info3.num_groups = info3->num_groups;
+ state->response.data.auth.info3.user_flgs = info3->user_flgs;
+
+ state->response.data.auth.info3.acct_flags = info3->acct_flags;
+ state->response.data.auth.info3.num_other_sids = info3->num_other_sids;
+
+ unistr2_to_ascii(state->response.data.auth.info3.user_name,
+ &info3->uni_user_name, -1);
+ unistr2_to_ascii(state->response.data.auth.info3.full_name,
+ &info3->uni_full_name, -1);
+ unistr2_to_ascii(state->response.data.auth.info3.logon_script,
+ &info3->uni_logon_script, -1);
+ unistr2_to_ascii(state->response.data.auth.info3.profile_path,
+ &info3->uni_profile_path, -1);
+ unistr2_to_ascii(state->response.data.auth.info3.home_dir,
+ &info3->uni_home_dir, -1);
+ unistr2_to_ascii(state->response.data.auth.info3.dir_drive,
+ &info3->uni_dir_drive, -1);
+
+ unistr2_to_ascii(state->response.data.auth.info3.logon_srv,
+ &info3->uni_logon_srv, -1);
+ unistr2_to_ascii(state->response.data.auth.info3.logon_dom,
+ &info3->uni_logon_dom, -1);
+
+ return NT_STATUS_OK;
+}
static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
struct winbindd_cli_state *state,
@@ -145,14 +210,15 @@ static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
return NT_STATUS_LOGON_FAILURE;
}
-static struct winbindd_domain *find_auth_domain(const char *domain_name)
+static struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
+ const char *domain_name)
{
struct winbindd_domain *domain;
if (IS_DC) {
domain = find_domain_from_name_noinit(domain_name);
if (domain == NULL) {
- DEBUG(3, ("Authentication for domain [%s] "
+ DEBUG(3, ("Authentication for domain [%s] refused"
"as it is not a trusted domain\n",
domain_name));
}
@@ -166,6 +232,18 @@ static struct winbindd_domain *find_auth_domain(const char *domain_name)
return NULL;
}
+ /* we can auth against trusted domains */
+ if (state->request.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
+ domain = find_domain_from_name_noinit(domain_name);
+ if (domain == NULL) {
+ DEBUG(3, ("Authentication for domain [%s] skipped "
+ "as it is not a trusted domain\n",
+ domain_name));
+ } else {
+ return domain;
+ }
+ }
+
return find_our_domain();
}
@@ -181,9 +259,372 @@ static void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
resp->data.auth.pam_error = nt_status_to_pam(result);
}
+static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state)
+{
+ struct winbindd_methods *methods;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ SAM_UNK_INFO_1 password_policy;
+
+ methods = domain->methods;
+
+ status = methods->password_policy(domain, state->mem_ctx, &password_policy);
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+
+ state->response.data.auth.policy.min_length_password =
+ password_policy.min_length_password;
+ state->response.data.auth.policy.password_history =
+ password_policy.password_history;
+ state->response.data.auth.policy.password_properties =
+ password_policy.password_properties;
+ state->response.data.auth.policy.expire =
+ nt_time_to_unix_abs(&(password_policy.expire));
+ state->response.data.auth.policy.min_passwordage =
+ nt_time_to_unix_abs(&(password_policy.min_passwordage));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint16 *max_allowed_bad_attempts)
+{
+ struct winbindd_methods *methods;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ SAM_UNK_INFO_12 lockout_policy;
+
+ *max_allowed_bad_attempts = 0;
+
+ methods = domain->methods;
+
+ status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+
+ *max_allowed_bad_attempts = lockout_policy.bad_attempt_lockout;
+
+ return NT_STATUS_OK;
+}
+
+
+static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
+ const char *type,
+ uid_t uid,
+ BOOL *internal_ccache)
+{
+ /* accept KCM, FILE and WRFILE as krb5_cc_type from the client and then
+ * build the full ccname string based on the user's uid here -
+ * Guenther*/
+
+ const char *gen_cc = NULL;
+
+ *internal_ccache = True;
+
+ if (uid == -1) {
+ goto memory_ccache;
+ }
+
+ if (!type || type[0] == '\0') {
+ goto memory_ccache;
+ }
+
+ if (strequal(type, "FILE")) {
+ gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
+ } else if (strequal(type, "WRFILE")) {
+ gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
+#ifdef WITH_KCM
+ } else if (strequal(type, "KCM")) {
+ gen_cc = talloc_asprintf(mem_ctx, "KCM:%d", uid);
+#endif
+ } else {
+ DEBUG(10,("we don't allow to set a %s type ccache\n", type));
+ goto memory_ccache;
+ }
+
+ *internal_ccache = False;
+ goto done;
+
+ memory_ccache:
+ gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbind_cache");
+
+ done:
+ if (gen_cc == NULL) {
+ DEBUG(0,("out of memory\n"));
+ return NULL;
+ }
+
+ DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
+
+ return gen_cc;
+}
+
+static uid_t get_uid_from_state(struct winbindd_cli_state *state)
+{
+ uid_t uid = -1;
+
+ uid = state->request.data.auth.uid;
+
+ if (uid < 0) {
+ DEBUG(1,("invalid uid: '%d'\n", uid));
+ return -1;
+ }
+ return uid;
+}
+
+static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
+{
+ const char *type = state->request.data.auth.krb5_cc_type;
+
+ state->response.data.auth.krb5ccname[0] = '\0';
+
+ if (type[0] == '\0') {
+ return;
+ }
+
+ if (!strequal(type, "FILE") &&
+#ifdef WITH_KCM
+ !strequal(type, "KCM") &&
+#endif
+ !strequal(type, "WRFILE")) {
+ DEBUG(10,("won't return krbccname for a %s type ccache\n",
+ type));
+ return;
+ }
+
+ fstrcpy(state->response.data.auth.krb5ccname, cc);
+}
+
/**********************************************************************
- Authenticate a user with a clear text password
-**********************************************************************/
+ Authenticate a user with a clear text password using Kerberos and fill up
+ ccache if required
+ **********************************************************************/
+static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state,
+ NET_USER_INFO_3 **info3)
+{
+#ifdef HAVE_KRB5
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ krb5_error_code krb5_ret;
+ DATA_BLOB tkt, session_key_krb5;
+ DATA_BLOB ap_rep, session_key;
+ PAC_DATA *pac_data = NULL;
+ PAC_LOGON_INFO *logon_info = NULL;
+ char *client_princ = NULL;
+ char *client_princ_out = NULL;
+ char *local_service = NULL;
+ const char *cc = NULL;
+ const char *principal_s = NULL;
+ const char *service = NULL;
+ char *realm = NULL;
+ fstring name_domain, name_user;
+ time_t ticket_lifetime = 0;
+ time_t renewal_until = 0;
+ uid_t uid = -1;
+ ADS_STRUCT *ads;
+ time_t time_offset = 0;
+ BOOL internal_ccache = True;
+
+ ZERO_STRUCT(session_key);
+ ZERO_STRUCT(session_key_krb5);
+ ZERO_STRUCT(tkt);
+ ZERO_STRUCT(ap_rep);
+
+ ZERO_STRUCTP(info3);
+
+ *info3 = NULL;
+
+ /* 1st step:
+ * prepare a krb5_cc_cache string for the user */
+
+ uid = get_uid_from_state(state);
+ if (uid == -1) {
+ DEBUG(0,("no valid uid\n"));
+ }
+
+ cc = generate_krb5_ccache(state->mem_ctx,
+ state->request.data.auth.krb5_cc_type,
+ state->request.data.auth.uid,
+ &internal_ccache);
+ if (cc == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+
+ /* 2nd step:
+ * get kerberos properties */
+
+ if (domain->private_data) {
+ ads = (ADS_STRUCT *)domain->private_data;
+ time_offset = ads->auth.time_offset;
+ }
+
+
+ /* 3rd step:
+ * do kerberos auth and setup ccache as the user */
+
+ parse_domain_user(state->request.data.auth.user, name_domain, name_user);
+
+ realm = domain->alt_name;
+ strupper_m(realm);
+
+ principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
+ if (principal_s == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ service = talloc_asprintf(state->mem_ctx, "krbtgt/%s@%s", realm, realm);
+ if (service == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* if this is a user ccache, we need to act as the user to let the krb5
+ * library handle the chown, etc. */
+
+ /************************ NON-ROOT **********************/
+
+ if (!internal_ccache) {
+
+ seteuid(uid);
+ DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
+ }
+
+ krb5_ret = kerberos_kinit_password(principal_s,
+ state->request.data.auth.pass,
+ time_offset,
+ &ticket_lifetime,
+ &renewal_until,
+ cc,
+ True,
+ WINBINDD_PAM_AUTH_KRB5_RENEW_TIME);
+
+ if (krb5_ret) {
+ DEBUG(1,("winbindd_raw_kerberos_login: kinit failed for '%s' with: %s (%d)\n",
+ principal_s, error_message(krb5_ret), krb5_ret));
+ result = krb5_to_nt_status(krb5_ret);
+ goto done;
+ }
+
+ /* does http_timestring use heimdals libroken strftime?? - Guenther */
+ DEBUG(10,("got TGT for %s in %s (valid until: %s (%d), renewable till: %s (%d))\n",
+ principal_s, cc,
+ http_timestring(ticket_lifetime), (int)ticket_lifetime,
+ http_timestring(renewal_until), (int)renewal_until));
+
+ client_princ = talloc_strdup(state->mem_ctx, global_myname());
+ if (client_princ == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ strlower_m(client_princ);
+
+ local_service = talloc_asprintf(state->mem_ctx, "HOST/%s@%s", client_princ, lp_realm());
+ if (local_service == NULL) {
+ DEBUG(0,("winbindd_raw_kerberos_login: out of memory\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ krb5_ret = cli_krb5_get_ticket(local_service,
+ time_offset,
+ &tkt,
+ &session_key_krb5,
+ 0,
+ cc);
+ if (krb5_ret) {
+ DEBUG(1,("winbindd_raw_kerberos_login: failed to get ticket for: %s\n",
+ local_service));
+ result = krb5_to_nt_status(krb5_ret);
+ goto done;
+ }
+
+ if (!internal_ccache) {
+ seteuid(0);
+ }
+
+ /************************ NON-ROOT **********************/
+
+ result = ads_verify_ticket(state->mem_ctx,
+ lp_realm(),
+ &tkt,
+ &client_princ_out,
+ &pac_data,
+ &ap_rep,
+ &session_key);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("winbindd_raw_kerberos_login: ads_verify_ticket failed: %s\n",
+ nt_errstr(result)));
+ goto done;
+ }
+
+ DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
+ client_princ));
+
+ if (!pac_data) {
+ DEBUG(3,("winbindd_raw_kerberos_login: no pac data\n"));
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ logon_info = get_logon_info_from_pac(pac_data);
+ if (logon_info == NULL) {
+ DEBUG(1,("winbindd_raw_kerberos_login: no logon info\n"));
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+
+ /* last step:
+ * put results together */
+
+ *info3 = &logon_info->info3;
+
+ /* if we had a user's ccache then return that string for the pam
+ * environment */
+
+ if (!internal_ccache) {
+
+ setup_return_cc_name(state, cc);
+
+ result = add_ccache_to_list(principal_s,
+ cc,
+ service,
+ state->request.data.auth.user,
+ NULL,
+ state->request.data.auth.pass,
+ uid,
+ time(NULL),
+ ticket_lifetime,
+ renewal_until,
+ lp_winbind_refresh_tickets());
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
+ nt_errstr(result)));
+ }
+ }
+
+ result = NT_STATUS_OK;
+
+done:
+ data_blob_free(&session_key);
+ data_blob_free(&session_key_krb5);
+ data_blob_free(&ap_rep);
+ data_blob_free(&tkt);
+
+ SAFE_FREE(client_princ_out);
+
+ if (!internal_ccache) {
+ seteuid(0);
+ }
+
+ return result;
+#else
+ return NT_STATUS_NOT_SUPPORTED;
+#endif /* HAVE_KRB5 */
+}
void winbindd_pam_auth(struct winbindd_cli_state *state)
{
@@ -206,7 +647,7 @@ void winbindd_pam_auth(struct winbindd_cli_state *state)
parse_domain_user(state->request.data.auth.user,
name_domain, name_user);
- domain = find_auth_domain(name_domain);
+ domain = find_auth_domain(state, name_domain);
if (domain == NULL) {
set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
@@ -222,12 +663,221 @@ void winbindd_pam_auth(struct winbindd_cli_state *state)
sendto_domain(state, domain);
}
-enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
- struct winbindd_cli_state *state)
+NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state,
+ NET_USER_INFO_3 **info3)
{
- NTSTATUS result;
+ NTSTATUS result = NT_STATUS_LOGON_FAILURE;
+ uint16 max_allowed_bad_attempts;
fstring name_domain, name_user;
- NET_USER_INFO_3 info3;
+ DOM_SID sid;
+ enum SID_NAME_USE type;
+ uchar new_nt_pass[NT_HASH_LEN];
+ const uint8 *cached_nt_pass;
+ NET_USER_INFO_3 *my_info3;
+ time_t kickoff_time, must_change_time;
+
+ *info3 = NULL;
+
+ ZERO_STRUCTP(info3);
+
+ DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
+
+ /* Parse domain and username */
+
+ parse_domain_user(state->request.data.auth.user, name_domain, name_user);
+
+
+ if (!lookup_cached_name(state->mem_ctx,
+ name_domain,
+ name_user,
+ &sid,
+ &type)) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (type != SID_NAME_USER) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ result = winbindd_get_creds(domain,
+ state->mem_ctx,
+ &sid,
+ &my_info3,
+ &cached_nt_pass);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
+ return result;
+ }
+
+ *info3 = my_info3;
+
+ E_md4hash(state->request.data.auth.pass, new_nt_pass);
+
+ dump_data(100, (const char *)new_nt_pass, NT_HASH_LEN);
+ dump_data(100, (const char *)cached_nt_pass, NT_HASH_LEN);
+
+ if (!memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN)) {
+
+ /* User *DOES* know the password, update logon_time and reset
+ * bad_pw_count */
+
+ my_info3->user_flgs |= LOGON_CACHED_ACCOUNT;
+
+ if (my_info3->acct_flags & ACB_AUTOLOCK) {
+ return NT_STATUS_ACCOUNT_LOCKED_OUT;
+ }
+
+ if (my_info3->acct_flags & ACB_DISABLED) {
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ if (my_info3->acct_flags & ACB_WSTRUST) {
+ return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
+ }
+
+ if (my_info3->acct_flags & ACB_SVRTRUST) {
+ return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
+ }
+
+ if (my_info3->acct_flags & ACB_DOMTRUST) {
+ return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
+ }
+
+ if (!(my_info3->acct_flags & ACB_NORMAL)) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
+ my_info3->acct_flags));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ kickoff_time = nt_time_to_unix(&my_info3->kickoff_time);
+ if (kickoff_time != 0 && time(NULL) > kickoff_time) {
+ return NT_STATUS_ACCOUNT_EXPIRED;
+ }
+
+ must_change_time = nt_time_to_unix(&my_info3->pass_must_change_time);
+ if (must_change_time != 0 && must_change_time < time(NULL)) {
+ return NT_STATUS_PASSWORD_EXPIRED;
+ }
+
+ /* FIXME: we possibly should handle logon hours as well (does xp when
+ * offline?) see auth/auth_sam.c:sam_account_ok for details */
+
+ unix_to_nt_time(&my_info3->logon_time, time(NULL));
+ my_info3->bad_pw_count = 0;
+
+ result = winbindd_update_creds_by_info3(domain,
+ state->mem_ctx,
+ state->request.data.auth.user,
+ state->request.data.auth.pass,
+ my_info3);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(1,("failed to update creds: %s\n", nt_errstr(result)));
+ return result;
+ }
+
+ return NT_STATUS_OK;
+
+ }
+
+ /* User does *NOT* know the correct password, modify info3 accordingly */
+
+ /* failure of this is not critical */
+ result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
+ "Won't be able to honour account lockout policies\n"));
+ }
+
+ if (max_allowed_bad_attempts == 0) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* increase counter */
+ if (my_info3->bad_pw_count < max_allowed_bad_attempts) {
+
+ my_info3->bad_pw_count++;
+ }
+
+ /* lockout user */
+ if (my_info3->bad_pw_count >= max_allowed_bad_attempts) {
+
+ my_info3->acct_flags |= ACB_AUTOLOCK;
+ }
+
+ result = winbindd_update_creds_by_info3(domain,
+ state->mem_ctx,
+ state->request.data.auth.user,
+ NULL,
+ my_info3);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
+ nt_errstr(result)));
+ }
+
+ return NT_STATUS_LOGON_FAILURE;
+}
+
+NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state,
+ NET_USER_INFO_3 **info3)
+{
+ struct winbindd_domain *contact_domain;
+ fstring name_domain, name_user;
+ NTSTATUS result;
+
+ DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
+
+ /* Parse domain and username */
+
+ parse_domain_user(state->request.data.auth.user, name_domain, name_user);
+
+ /* what domain should we contact? */
+
+ if ( IS_DC ) {
+ if (!(contact_domain = find_domain_from_name(name_domain))) {
+ DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
+ state->request.data.auth.user, name_domain, name_user, name_domain));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ } else {
+ if (is_myname(name_domain)) {
+ DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ contact_domain = find_domain_from_name(name_domain);
+ if (contact_domain == NULL) {
+ DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
+ state->request.data.auth.user, name_domain, name_user, name_domain));
+
+ contact_domain = find_our_domain();
+ }
+ }
+
+ set_dc_type_and_flags(contact_domain);
+
+ if (!contact_domain->active_directory) {
+ DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
+ return NT_STATUS_INVALID_LOGON_TYPE;
+ }
+
+ result = winbindd_raw_kerberos_login(contact_domain, state, info3);
+done:
+ return result;
+}
+
+NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state,
+ NET_USER_INFO_3 **info3)
+{
+
struct rpc_pipe_client *netlogon_pipe;
uchar chal[8];
DATA_BLOB lm_resp;
@@ -236,17 +886,23 @@ enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
unsigned char local_lm_response[24];
unsigned char local_nt_response[24];
struct winbindd_domain *contact_domain;
+ fstring name_domain, name_user;
BOOL retry;
+ NTSTATUS result;
+ NET_USER_INFO_3 *my_info3;
- /* Ensure null termination */
- state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
+ ZERO_STRUCTP(info3);
- /* Ensure null termination */
- state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
+ *info3 = NULL;
- DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
- state->request.data.auth.user));
+ my_info3 = TALLOC_ZERO_P(state->mem_ctx, NET_USER_INFO_3);
+ if (my_info3 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
+
/* Parse domain and username */
parse_domain_user(state->request.data.auth.user, name_domain, name_user);
@@ -332,7 +988,7 @@ enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
do {
- ZERO_STRUCT(info3);
+ ZERO_STRUCTP(my_info3);
retry = False;
result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
@@ -352,7 +1008,7 @@ enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
chal,
lm_resp,
nt_resp,
- &info3);
+ my_info3);
attempts += 1;
/* We have to try a second time as cm_connect_netlogon
@@ -381,25 +1037,154 @@ enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
} while ( (attempts < 2) && retry );
+ *info3 = my_info3;
+done:
+ return result;
+}
+
+enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state)
+{
+ NTSTATUS result = NT_STATUS_LOGON_FAILURE;
+ fstring name_domain, name_user;
+ NET_USER_INFO_3 *info3;
+
+ /* Ensure null termination */
+ state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
+
+ /* Ensure null termination */
+ state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
+ state->request.data.auth.user));
+
+ /* Parse domain and username */
+
+ parse_domain_user(state->request.data.auth.user, name_domain, name_user);
+
+ DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
+
+ /* Check for Kerberos authentication */
+ if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
+
+ result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
+
+ if (NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
+ goto process_result;
+ } else {
+ DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
+ }
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS)) {
+ DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
+ domain->online = False;
+ }
+
+ if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
+ DEBUG(3,("falling back to samlogon\n"));
+ goto sam_logon;
+ } else {
+ goto cached_logon;
+ }
+ }
+
+sam_logon:
+ /* Check for Samlogon authentication */
+ if (domain->online) {
+ result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
+
+ if (NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
+ goto process_result;
+ } else {
+ DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n", nt_errstr(result)));
+ if (domain->online) {
+ /* We're still online - fail. */
+ goto done;
+ }
+ /* Else drop through and see if we can check offline.... */
+ }
+ }
+
+cached_logon:
+ /* Check for Cached logons */
+ if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
+ lp_winbind_offline_logon()) {
+
+ result = winbindd_dual_pam_auth_cached(domain, state, &info3);
+
+ if (NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
+ goto process_result;
+ } else {
+ DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
+ goto done;
+ }
+ }
+
+process_result:
+
if (NT_STATUS_IS_OK(result)) {
- netsamlogon_cache_store(name_user, &info3);
- wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3);
+
+ netsamlogon_cache_store(name_user, info3);
+ wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
/* Check if the user is in the right group */
- if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3,
+ if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
state->request.data.auth.require_membership_of_sid))) {
DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
state->request.data.auth.user,
state->request.data.auth.require_membership_of_sid));
+ goto done;
}
- }
-done:
+ if (state->request.flags & WBFLAG_PAM_INFO3_NDR) {
+ result = append_info3_as_ndr(state->mem_ctx, state, info3);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("Failed to append INFO3 (NDR): %s\n", nt_errstr(result)));
+ goto done;
+ }
+ }
+
+ if (state->request.flags & WBFLAG_PAM_INFO3_TEXT) {
+ result = append_info3_as_txt(state->mem_ctx, state, info3);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("Failed to append INFO3 (TXT): %s\n", nt_errstr(result)));
+ goto done;
+ }
+
+ }
+ if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
+ lp_winbind_offline_logon()) {
+
+ result = winbindd_store_creds(domain,
+ state->mem_ctx,
+ state->request.data.auth.user,
+ state->request.data.auth.pass,
+ info3, NULL);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
+ goto done;
+ }
+
+ }
+
+ result = fillup_password_policy(domain, state);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result)));
+ goto done;
+ }
+
+ }
+
+done:
/* give us a more useful (more correct?) error code */
if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
- (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
+ (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
result = NT_STATUS_NO_LOGON_SERVERS;
}
@@ -439,8 +1224,8 @@ done:
DOM_SID user_sid;
fstring sidstr;
- sid_copy(&user_sid, &info3.dom_sid.sid);
- sid_append_rid(&user_sid, info3.user_rid);
+ sid_copy(&user_sid, &info3->dom_sid.sid);
+ sid_append_rid(&user_sid, info3->user_rid);
sid_to_string(sidstr, &user_sid);
afsname = talloc_string_sub(state->mem_ctx, afsname,
"%s", sidstr);
@@ -474,10 +1259,11 @@ done:
no_token:
talloc_free(afsname);
}
-
+
return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
}
+
/**********************************************************************
Challenge Response Authentication Protocol
**********************************************************************/
@@ -525,7 +1311,7 @@ void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
}
if (domain_name != NULL)
- domain = find_auth_domain(domain_name);
+ domain = find_auth_domain(state, domain_name);
if (domain != NULL) {
sendto_domain(state, domain);
@@ -675,6 +1461,7 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
} while ( (attempts < 2) && retry );
if (NT_STATUS_IS_OK(result)) {
+
netsamlogon_cache_store(name_user, &info3);
wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3);
@@ -732,7 +1519,7 @@ done:
/* give us a more useful (more correct?) error code */
if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
- (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
+ (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
result = NT_STATUS_NO_LOGON_SERVERS;
}
@@ -763,12 +1550,16 @@ done:
void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
{
- NTSTATUS result;
- char *oldpass, *newpass;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ char *oldpass;
+ char *newpass = NULL;
fstring domain, user;
POLICY_HND dom_pol;
struct winbindd_domain *contact_domain;
struct rpc_pipe_client *cli;
+ BOOL got_info = False;
+ SAM_UNK_INFO_1 *info;
+ SAMR_CHANGE_REJECT *reject;
DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
state->request.data.chauthtok.user));
@@ -798,10 +1589,70 @@ void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
goto done;
}
- result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass,
- oldpass);
+ result = rpccli_samr_chgpasswd3(cli, state->mem_ctx, user, newpass, oldpass, &info, &reject);
+
+ /* FIXME: need to check for other error codes ? */
+ if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION)) {
+
+ state->response.data.auth.policy.min_length_password =
+ info->min_length_password;
+ state->response.data.auth.policy.password_history =
+ info->password_history;
+ state->response.data.auth.policy.password_properties =
+ info->password_properties;
+ state->response.data.auth.policy.expire =
+ nt_time_to_unix_abs(&info->expire);
+ state->response.data.auth.policy.min_passwordage =
+ nt_time_to_unix_abs(&info->min_passwordage);
+
+ state->response.data.auth.reject_reason =
+ reject->reject_reason;
+
+ got_info = True;
+
+ } else if (!NT_STATUS_IS_OK(result)) {
+
+ DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n",
+ nt_errstr(result)));
+
+ state->response.data.auth.reject_reason = 0;
+
+ result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, oldpass);
+ }
+
+done:
+ if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
+ lp_winbind_offline_logon()) {
+
+ NTSTATUS cred_ret;
+
+ cred_ret = winbindd_update_creds_by_name(contact_domain,
+ state->mem_ctx, user,
+ newpass);
+ if (!NT_STATUS_IS_OK(cred_ret)) {
+ DEBUG(10,("Failed to store creds: %s\n", nt_errstr(cred_ret)));
+ goto process_result; /* FIXME: hm, risking inconsistant cache ? */
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(result) && !got_info) {
+
+ NTSTATUS policy_ret;
+
+ policy_ret = fillup_password_policy(contact_domain, state);
+
+ /* failure of this is non critical, it will just provide no
+ * additional information to the client why the change has
+ * failed - Guenther */
+
+ if (!NT_STATUS_IS_OK(policy_ret)) {
+ DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
+ goto process_result;
+ }
+ }
+
+process_result:
-done:
state->response.data.auth.nt_status = NT_STATUS_V(result);
fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
@@ -819,3 +1670,112 @@ done:
else
request_error(state);
}
+
+void winbindd_pam_logoff(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+ fstring name_domain, user;
+
+ DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
+ state->request.data.logoff.user));
+
+ /* Ensure null termination */
+ state->request.data.logoff.user
+ [sizeof(state->request.data.logoff.user)-1]='\0';
+
+ state->request.data.logoff.krb5ccname
+ [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
+
+ parse_domain_user(state->request.data.logoff.user, name_domain, user);
+
+ domain = find_auth_domain(state, name_domain);
+
+ if (domain == NULL) {
+ set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
+ DEBUG(5, ("Pam Logoff for %s returned %s "
+ "(PAM: %d)\n",
+ state->request.data.auth.user,
+ state->response.data.auth.nt_status_string,
+ state->response.data.auth.pam_error));
+ request_error(state);
+ return;
+ }
+
+ sendto_domain(state, domain);
+}
+
+enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state)
+{
+ NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
+ struct WINBINDD_CCACHE_ENTRY *entry;
+ int ret;
+
+ DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
+ state->request.data.logoff.user));
+
+ if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
+ result = NT_STATUS_OK;
+ goto process_result;
+ }
+
+#ifdef HAVE_KRB5
+
+ /* what we need here is to find the corresponding krb5 ccache name *we*
+ * created for a given username and destroy it (as the user who created it) */
+
+ entry = get_ccache_by_username(state->request.data.logoff.user);
+ if (entry == NULL) {
+ DEBUG(10,("winbindd_pam_logoff: could not get ccname for user %s\n",
+ state->request.data.logoff.user));
+ goto process_result;
+ }
+
+ DEBUG(10,("winbindd_pam_logoff: found ccache [%s]\n", entry->ccname));
+
+ if (entry->uid < 0 || state->request.data.logoff.uid < 0) {
+ DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
+ goto process_result;
+ }
+
+ if (entry->uid != state->request.data.logoff.uid) {
+ DEBUG(0,("winbindd_pam_logoff: uid's differ: %d != %d\n",
+ entry->uid, state->request.data.logoff.uid));
+ goto process_result;
+ }
+
+ if (!strcsequal(entry->ccname, state->request.data.logoff.krb5ccname)) {
+ DEBUG(0,("winbindd_pam_logoff: krb5ccnames differ: (daemon) %s != (client) %s\n",
+ entry->ccname, state->request.data.logoff.krb5ccname));
+ goto process_result;
+ }
+
+ seteuid(entry->uid);
+
+ ret = ads_kdestroy(entry->ccname);
+
+ seteuid(0);
+
+ if (ret) {
+ DEBUG(0,("winbindd_pam_logoff: failed to destroy user ccache %s with: %s\n",
+ entry->ccname, error_message(ret)));
+ } else {
+ DEBUG(10,("winbindd_pam_logoff: successfully destroyed ccache %s for user %s\n",
+ entry->ccname, state->request.data.logoff.user));
+ remove_ccache_by_ccname(entry->ccname);
+ }
+
+ result = krb5_to_nt_status(ret);
+#else
+ result = NT_STATUS_NOT_SUPPORTED;
+#endif
+
+process_result:
+ state->response.data.auth.nt_status = NT_STATUS_V(result);
+ fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
+ fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
+ state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
diff --git a/source3/nsswitch/winbindd_passdb.c b/source3/nsswitch/winbindd_passdb.c
index c32aa01a38..96a85a4f3a 100644
--- a/source3/nsswitch/winbindd_passdb.c
+++ b/source3/nsswitch/winbindd_passdb.c
@@ -151,7 +151,8 @@ BOOL fill_passdb_alias_grmem(struct winbindd_domain *domain,
*gr_mem = NULL;
*gr_mem_len = 0;
- if (!pdb_enum_aliasmem(group_sid, &members, &num_members))
+ if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
+ &num_members)))
return True;
for (i=0; i<num_members; i++) {
@@ -265,19 +266,24 @@ static NTSTATUS sid_to_name(struct winbindd_domain *domain,
char **name,
enum SID_NAME_USE *type)
{
- struct acct_info info;
+ const char *dom, *nam;
DEBUG(10, ("Converting SID %s\n", sid_string_static(sid)));
- if (!pdb_get_aliasinfo(sid, &info))
+ /* Paranoia check */
+ if (!sid_check_is_in_builtin(sid) &&
+ !sid_check_is_in_our_domain(sid)) {
+ DEBUG(0, ("Possible deadlock: Trying to lookup SID %s with "
+ "passdb backend\n", sid_string_static(sid)));
return NT_STATUS_NONE_MAPPED;
+ }
- *domain_name = talloc_strdup(mem_ctx, domain->name);
- *name = talloc_strdup(mem_ctx, info.acct_name);
- if (sid_check_is_in_builtin(sid))
- *type = SID_NAME_WKN_GRP;
- else
- *type = SID_NAME_ALIAS;
+ if (!lookup_sid(mem_ctx, sid, &dom, &nam, type)) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ *domain_name = talloc_strdup(mem_ctx, dom);
+ *name = talloc_strdup(mem_ctx, nam);
return NT_STATUS_OK;
}
@@ -305,14 +311,14 @@ static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
uint32 num_sids, const DOM_SID *sids,
uint32 *p_num_aliases, uint32 **rids)
{
- BOOL result;
+ NTSTATUS result;
size_t num_aliases = 0;
result = pdb_enum_alias_memberships(mem_ctx, &domain->sid,
sids, num_sids, rids, &num_aliases);
*p_num_aliases = num_aliases;
- return result ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+ return result;
}
/* Lookup group membership given a rid. */
@@ -322,16 +328,106 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
DOM_SID **sid_mem, char ***names,
uint32 **name_types)
{
+ size_t i, num_members, num_mapped;
+ uint32 *rids;
+ NTSTATUS result;
+ const DOM_SID **sids;
+ struct lsa_dom_info *lsa_domains;
+ struct lsa_name_info *lsa_names;
+
+ if (!sid_check_is_in_our_domain(group_sid)) {
+ /* There's no groups, only aliases in BUILTIN */
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ result = pdb_enum_group_members(mem_ctx, group_sid, &rids,
+ &num_members);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ if (num_members == 0) {
+ *num_names = 0;
+ *sid_mem = NULL;
+ *names = NULL;
+ *name_types = NULL;
+ return NT_STATUS_OK;
+ }
+
+ *sid_mem = TALLOC_ARRAY(mem_ctx, DOM_SID, num_members);
+ *names = TALLOC_ARRAY(mem_ctx, char *, num_members);
+ *name_types = TALLOC_ARRAY(mem_ctx, uint32, num_members);
+ sids = TALLOC_ARRAY(mem_ctx, const DOM_SID *, num_members);
+
+ if (((*sid_mem) == NULL) || ((*names) == NULL) ||
+ ((*name_types) == NULL) || (sids == NULL)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_members; i++) {
+ DOM_SID *sid = &((*sid_mem)[i]);
+ sid_copy(sid, &domain->sid);
+ sid_append_rid(sid, rids[i]);
+ sids[i] = sid;
+ }
+
+ result = lookup_sids(mem_ctx, num_members, sids, 1,
+ &lsa_domains, &lsa_names);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ num_mapped = 0;
+ for (i=0; i<num_members; i++) {
+ if (lsa_names[i].type != SID_NAME_USER) {
+ DEBUG(2, ("Got %s as group member -- ignoring\n",
+ sid_type_lookup(lsa_names[i].type)));
+ continue;
+ }
+ (*names)[i] = talloc_steal((*names),
+ lsa_names[i].name);
+ (*name_types)[i] = lsa_names[i].type;
+
+ num_mapped += 1;
+ }
+
+ *num_names = num_mapped;
+
return NT_STATUS_OK;
}
/* find the sequence number for a domain */
static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
{
- *seq = 1;
+ BOOL result;
+ time_t seq_num;
+
+ result = pdb_get_seq_num(&seq_num);
+ if (!result) {
+ *seq = 1;
+ }
+
+ *seq = (int) seq_num;
+ /* *seq = 1; */
return NT_STATUS_OK;
}
+static NTSTATUS lockout_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ SAM_UNK_INFO_12 *lockout_policy)
+{
+ /* actually we have that */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS password_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ SAM_UNK_INFO_1 *password_policy)
+{
+ /* actually we have that */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
/* get a list of trusted domains */
static NTSTATUS trusted_domains(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
@@ -341,41 +437,35 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain,
DOM_SID **dom_sids)
{
NTSTATUS nt_status;
- int enum_ctx = 0;
- int num_sec_domains;
- TRUSTDOM **domains;
+ struct trustdom_info **domains;
+ int i;
+
*num_domains = 0;
*names = NULL;
*alt_names = NULL;
*dom_sids = NULL;
- do {
- int i;
- nt_status = secrets_get_trusted_domains(mem_ctx, &enum_ctx, 1,
- &num_sec_domains,
- &domains);
- *names = TALLOC_REALLOC_ARRAY(mem_ctx, *names, char *,
- num_sec_domains + *num_domains);
- *alt_names = TALLOC_REALLOC_ARRAY(mem_ctx, *alt_names, char *,
- num_sec_domains + *num_domains);
- *dom_sids = TALLOC_REALLOC_ARRAY(mem_ctx, *dom_sids, DOM_SID,
- num_sec_domains + *num_domains);
-
- for (i=0; i< num_sec_domains; i++) {
- if (pull_ucs2_talloc(mem_ctx, &(*names)[*num_domains],
- domains[i]->name) == -1) {
- return NT_STATUS_NO_MEMORY;
- }
- (*alt_names)[*num_domains] = NULL;
- (*dom_sids)[*num_domains] = domains[i]->sid;
- (*num_domains)++;
- }
- } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+ nt_status = secrets_trusted_domains(mem_ctx, num_domains,
+ &domains);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
- if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MORE_ENTRIES)) {
- return NT_STATUS_OK;
+ *names = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
+ *alt_names = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
+ *dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
+
+ if ((*alt_names == NULL) || (*names == NULL) || (*dom_sids == NULL)) {
+ return NT_STATUS_NO_MEMORY;
}
- return nt_status;
+
+ for (i=0; i<*num_domains; i++) {
+ (*alt_names)[i] = NULL;
+ (*names)[i] = talloc_steal((*names), domains[i]->name);
+ sid_copy(&(*dom_sids)[i], &domains[i]->sid);
+ }
+
+ return NT_STATUS_OK;
}
/* the rpc backend methods are exposed via this structure */
@@ -391,5 +481,7 @@ struct winbindd_methods passdb_methods = {
lookup_useraliases,
lookup_groupmem,
sequence_number,
+ lockout_policy,
+ password_policy,
trusted_domains,
};
diff --git a/source3/nsswitch/winbindd_reconnect.c b/source3/nsswitch/winbindd_reconnect.c
index 77df9c1513..e37bfcad97 100644
--- a/source3/nsswitch/winbindd_reconnect.c
+++ b/source3/nsswitch/winbindd_reconnect.c
@@ -220,6 +220,36 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
return result;
}
+/* find the lockout policy of a domain */
+static NTSTATUS lockout_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ SAM_UNK_INFO_12 *lockout_policy)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.lockout_policy(domain, mem_ctx, lockout_policy);
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))
+ result = msrpc_methods.lockout_policy(domain, mem_ctx, lockout_policy);
+
+ return result;
+}
+
+/* find the password policy of a domain */
+static NTSTATUS password_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ SAM_UNK_INFO_1 *password_policy)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.password_policy(domain, mem_ctx, password_policy);
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))
+ result = msrpc_methods.password_policy(domain, mem_ctx, password_policy);
+
+ return result;
+}
+
/* get a list of trusted domains */
static NTSTATUS trusted_domains(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
@@ -255,5 +285,7 @@ struct winbindd_methods reconnect_methods = {
lookup_useraliases,
lookup_groupmem,
sequence_number,
+ lockout_policy,
+ password_policy,
trusted_domains,
};
diff --git a/source3/nsswitch/winbindd_rpc.c b/source3/nsswitch/winbindd_rpc.c
index 6179189e30..4aaedad4a2 100644
--- a/source3/nsswitch/winbindd_rpc.c
+++ b/source3/nsswitch/winbindd_rpc.c
@@ -269,7 +269,7 @@ NTSTATUS msrpc_name_to_sid(struct winbindd_domain *domain,
return result;
result = rpccli_lsa_lookup_names(cli, mem_ctx, &lsa_policy, 1,
- &full_name, &sids, &types);
+ &full_name, NULL, &sids, &types);
if (!NT_STATUS_IS_OK(result))
return result;
@@ -883,6 +883,71 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain,
return result;
}
+/* find the lockout policy for a domain */
+NTSTATUS msrpc_lockout_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ SAM_UNK_INFO_12 *lockout_policy)
+{
+ NTSTATUS result;
+ struct rpc_pipe_client *cli;
+ POLICY_HND dom_pol;
+ SAM_UNK_CTR ctr;
+
+ DEBUG(10,("rpc: fetch lockout policy for %s\n", domain->name));
+
+ result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_samr_query_dom_info(cli, mem_ctx, &dom_pol, 12, &ctr);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ *lockout_policy = ctr.info.inf12;
+
+ DEBUG(10,("msrpc_lockout_policy: bad_attempt_lockout %d\n",
+ ctr.info.inf12.bad_attempt_lockout));
+
+ done:
+
+ return result;
+}
+
+/* find the password policy for a domain */
+NTSTATUS msrpc_password_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ SAM_UNK_INFO_1 *password_policy)
+{
+ NTSTATUS result;
+ struct rpc_pipe_client *cli;
+ POLICY_HND dom_pol;
+ SAM_UNK_CTR ctr;
+
+ DEBUG(10,("rpc: fetch password policy for %s\n", domain->name));
+
+ result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_samr_query_dom_info(cli, mem_ctx, &dom_pol, 1, &ctr);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ *password_policy = ctr.info.inf1;
+
+ DEBUG(10,("msrpc_password_policy: min_length_password %d\n",
+ ctr.info.inf1.min_length_password));
+
+ done:
+
+ return result;
+}
+
+
/* the rpc backend methods are exposed via this structure */
struct winbindd_methods msrpc_methods = {
False,
@@ -896,5 +961,7 @@ struct winbindd_methods msrpc_methods = {
msrpc_lookup_useraliases,
lookup_groupmem,
sequence_number,
+ msrpc_lockout_policy,
+ msrpc_password_policy,
trusted_domains,
};
diff --git a/source3/nsswitch/winbindd_sid.c b/source3/nsswitch/winbindd_sid.c
index fc878cb5cc..a4cd8f7604 100644
--- a/source3/nsswitch/winbindd_sid.c
+++ b/source3/nsswitch/winbindd_sid.c
@@ -506,10 +506,10 @@ static void gid2sid_idmap_set_mapping_recv(void *private_data, BOOL success)
request_ok(state->cli_state);
}
-void winbindd_allocate_rid(struct winbindd_cli_state *state)
+void winbindd_allocate_uid(struct winbindd_cli_state *state)
{
if ( !state->privileged ) {
- DEBUG(2, ("winbindd_allocate_rid: non-privileged access "
+ DEBUG(2, ("winbindd_allocate_uid: non-privileged access "
"denied!\n"));
request_error(state);
return;
@@ -518,25 +518,22 @@ void winbindd_allocate_rid(struct winbindd_cli_state *state)
sendto_child(state, idmap_child());
}
-enum winbindd_result winbindd_dual_allocate_rid(struct winbindd_domain *domain,
+enum winbindd_result winbindd_dual_allocate_uid(struct winbindd_domain *domain,
struct winbindd_cli_state *state)
{
- /* We tell idmap to always allocate a user RID. There might be a good
- * reason to keep RID allocation for users to even and groups to
- * odd. This needs discussion I think. For now only allocate user
- * rids. */
+ union unid_t id;
- if (!NT_STATUS_IS_OK(idmap_allocate_rid(&state->response.data.rid,
- USER_RID_TYPE)))
+ if (!NT_STATUS_IS_OK(idmap_allocate_id(&id, ID_USERID))) {
return WINBINDD_ERROR;
-
+ }
+ state->response.data.uid = id.uid;
return WINBINDD_OK;
}
-void winbindd_allocate_rid_and_gid(struct winbindd_cli_state *state)
+void winbindd_allocate_gid(struct winbindd_cli_state *state)
{
if ( !state->privileged ) {
- DEBUG(2, ("winbindd_allocate_rid: non-privileged access "
+ DEBUG(2, ("winbindd_allocate_gid: non-privileged access "
"denied!\n"));
request_error(state);
return;
@@ -545,30 +542,15 @@ void winbindd_allocate_rid_and_gid(struct winbindd_cli_state *state)
sendto_child(state, idmap_child());
}
-enum winbindd_result winbindd_dual_allocate_rid_and_gid(struct winbindd_domain *domain,
- struct winbindd_cli_state *state)
+enum winbindd_result winbindd_dual_allocate_gid(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state)
{
- NTSTATUS result;
- DOM_SID sid;
-
- /* We tell idmap to always allocate a user RID. This is really
- * historic and needs to be fixed. I *think* this has to do with the
- * way winbind determines its free RID space. */
-
- result = idmap_allocate_rid(&state->response.data.rid_and_gid.rid,
- USER_RID_TYPE);
+ union unid_t id;
- if (!NT_STATUS_IS_OK(result))
+ if (!NT_STATUS_IS_OK(idmap_allocate_id(&id, ID_GROUPID))) {
return WINBINDD_ERROR;
-
- sid_copy(&sid, get_global_sam_sid());
- sid_append_rid(&sid, state->response.data.rid_and_gid.rid);
-
- result = idmap_sid_to_gid(&sid, &state->response.data.rid_and_gid.gid,
- 0);
-
- if (!NT_STATUS_IS_OK(result))
- return WINBINDD_ERROR;
-
+ }
+ state->response.data.gid = id.gid;
return WINBINDD_OK;
}
+
diff --git a/source3/nsswitch/winbindd_user.c b/source3/nsswitch/winbindd_user.c
index 0b88d5eee5..9670bf534c 100644
--- a/source3/nsswitch/winbindd_user.c
+++ b/source3/nsswitch/winbindd_user.c
@@ -122,10 +122,10 @@ static BOOL winbindd_fill_pwent(char *dom_name, char *user_name,
pw->pw_uid, pw->pw_gid, shell, pw->pw_shell))
return False;
- /* Password - set to "x" as we can't generate anything useful here.
+ /* Password - set to "*" as we can't generate anything useful here.
Authentication can be done using the pam_winbind module. */
- safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
+ safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
return True;
}
@@ -307,10 +307,10 @@ static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
goto failed;
}
- /* Password - set to "x" as we can't generate anything useful here.
+ /* Password - set to "*" as we can't generate anything useful here.
Authentication can be done using the pam_winbind module. */
- safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
+ safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
request_ok(s->state);
return;
diff --git a/source3/nsswitch/winbindd_util.c b/source3/nsswitch/winbindd_util.c
index 4c3306a8ac..b92ee0de82 100644
--- a/source3/nsswitch/winbindd_util.c
+++ b/source3/nsswitch/winbindd_util.c
@@ -161,6 +161,7 @@ static struct winbindd_domain *add_trusted_domain(const char *domain_name, const
domain->sequence_number = DOM_SEQUENCE_NONE;
domain->last_seq_check = 0;
domain->initialized = False;
+ domain->online = False;
if (sid) {
sid_copy(&domain->sid, sid);
}
@@ -334,6 +335,7 @@ enum winbindd_result init_child_connection(struct winbindd_domain *domain,
struct winbindd_request *request;
struct winbindd_response *response;
struct init_child_state *state;
+ struct winbindd_domain *request_domain;
mem_ctx = talloc_init("init_child_connection");
if (mem_ctx == NULL) {
@@ -366,7 +368,6 @@ enum winbindd_result init_child_connection(struct winbindd_domain *domain,
fstrcpy(request->domain_name, domain->name);
request->data.init_conn.is_primary = True;
fstrcpy(request->data.init_conn.dcname, "");
-
async_request(mem_ctx, &domain->child, request, response,
init_child_recv, state);
return WINBINDD_PENDING;
@@ -378,7 +379,11 @@ enum winbindd_result init_child_connection(struct winbindd_domain *domain,
request->cmd = WINBINDD_GETDCNAME;
fstrcpy(request->domain_name, domain->name);
- async_domain_request(mem_ctx, find_our_domain(), request, response,
+ /* save online flag */
+ request_domain = find_our_domain();
+ request_domain->online = domain->online;
+
+ async_domain_request(mem_ctx, request_domain, request, response,
init_child_getdc_recv, state);
return WINBINDD_PENDING;
}
@@ -1079,10 +1084,6 @@ static int convert_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data, void *state
#define HWM_GROUP "GROUP HWM"
#define HWM_USER "USER HWM"
-/* idmap version determines auto-conversion */
-#define IDMAP_VERSION 2
-
-
/*****************************************************************************
Convert the idmap database from an older version.
*****************************************************************************/