diff options
Diffstat (limited to 'source4/auth/ntlm/auth_unix.c')
-rw-r--r-- | source4/auth/ntlm/auth_unix.c | 844 |
1 files changed, 844 insertions, 0 deletions
diff --git a/source4/auth/ntlm/auth_unix.c b/source4/auth/ntlm/auth_unix.c new file mode 100644 index 0000000000..1717b9d0e1 --- /dev/null +++ b/source4/auth/ntlm/auth_unix.c @@ -0,0 +1,844 @@ +/* + Unix SMB/CIFS implementation. + Password and authentication handling + Copyright (C) Andrew Bartlett 2001 + Copyright (C) Jeremy Allison 2001 + Copyright (C) Simo Sorce 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "auth/auth.h" +#include "auth/ntlm/auth_proto.h" +#include "system/passwd.h" /* needed by some systems for struct passwd */ +#include "lib/socket/socket.h" +#include "auth/ntlm/pam_errors.h" +#include "param/param.h" + +/* TODO: look at how to best fill in parms retrieveing a struct passwd info + * except in case USER_INFO_DONT_CHECK_UNIX_ACCOUNT is set + */ +static NTSTATUS authunix_make_server_info(TALLOC_CTX *mem_ctx, + const char *netbios_name, + const struct auth_usersupplied_info *user_info, + struct passwd *pwd, + struct auth_serversupplied_info **_server_info) +{ + struct auth_serversupplied_info *server_info; + NTSTATUS status; + + /* This is a real, real hack */ + if (pwd->pw_uid == 0) { + status = auth_system_server_info(mem_ctx, netbios_name, &server_info); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + server_info->account_name = talloc_steal(server_info, pwd->pw_name); + NT_STATUS_HAVE_NO_MEMORY(server_info->account_name); + + server_info->domain_name = talloc_strdup(server_info, "unix"); + NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name); + } else { + server_info = talloc(mem_ctx, struct auth_serversupplied_info); + NT_STATUS_HAVE_NO_MEMORY(server_info); + + server_info->authenticated = true; + + server_info->account_name = talloc_steal(server_info, pwd->pw_name); + NT_STATUS_HAVE_NO_MEMORY(server_info->account_name); + + server_info->domain_name = talloc_strdup(server_info, "unix"); + NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name); + + /* This isn't in any way correct.. */ + server_info->account_sid = NULL; + server_info->primary_group_sid = NULL; + server_info->n_domain_groups = 0; + server_info->domain_groups = NULL; + } + server_info->user_session_key = data_blob(NULL,0); + server_info->lm_session_key = data_blob(NULL,0); + + server_info->full_name = talloc_steal(server_info, pwd->pw_gecos); + NT_STATUS_HAVE_NO_MEMORY(server_info->full_name); + server_info->logon_script = talloc_strdup(server_info, ""); + NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script); + server_info->profile_path = talloc_strdup(server_info, ""); + NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path); + server_info->home_directory = talloc_strdup(server_info, ""); + NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory); + server_info->home_drive = talloc_strdup(server_info, ""); + NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive); + + server_info->last_logon = 0; + server_info->last_logoff = 0; + server_info->acct_expiry = 0; + server_info->last_password_change = 0; + server_info->allow_password_change = 0; + server_info->force_password_change = 0; + server_info->logon_count = 0; + server_info->bad_password_count = 0; + server_info->acct_flags = 0; + + *_server_info = server_info; + + return NT_STATUS_OK; +} + +static NTSTATUS talloc_getpwnam(TALLOC_CTX *ctx, const char *username, struct passwd **pws) +{ + struct passwd *ret; + struct passwd *from; + + *pws = NULL; + + ret = talloc(ctx, struct passwd); + NT_STATUS_HAVE_NO_MEMORY(ret); + + from = getpwnam(username); + if (!from) { + return NT_STATUS_NO_SUCH_USER; + } + + ret->pw_name = talloc_strdup(ctx, from->pw_name); + NT_STATUS_HAVE_NO_MEMORY(ret->pw_name); + + ret->pw_passwd = talloc_strdup(ctx, from->pw_passwd); + NT_STATUS_HAVE_NO_MEMORY(ret->pw_passwd); + + ret->pw_uid = from->pw_uid; + ret->pw_gid = from->pw_gid; + ret->pw_gecos = talloc_strdup(ctx, from->pw_gecos); + NT_STATUS_HAVE_NO_MEMORY(ret->pw_gecos); + + ret->pw_dir = talloc_strdup(ctx, from->pw_dir); + NT_STATUS_HAVE_NO_MEMORY(ret->pw_dir); + + ret->pw_shell = talloc_strdup(ctx, from->pw_shell); + NT_STATUS_HAVE_NO_MEMORY(ret->pw_shell); + + *pws = ret; + + return NT_STATUS_OK; +} + + +#ifdef HAVE_SECURITY_PAM_APPL_H +#include <security/pam_appl.h> + +struct smb_pam_user_info { + const char *account_name; + const char *plaintext_password; +}; + +#define COPY_STRING(s) (s) ? strdup(s) : NULL + +/* + * Check user password + * Currently it uses PAM only and fails on systems without PAM + * Samba3 code located in pass_check.c is to ugly to be used directly it will + * need major rework that's why pass_check.c is still there. +*/ + +static int smb_pam_conv(int num_msg, const struct pam_message **msg, + struct pam_response **reply, void *appdata_ptr) +{ + struct smb_pam_user_info *info = (struct smb_pam_user_info *)appdata_ptr; + int num; + + if (num_msg <= 0) { + *reply = NULL; + return PAM_CONV_ERR; + } + + /* + * Apparantly HPUX has a buggy PAM that doesn't support the + * data pointer. Fail if this is the case. JRA. + */ + + if (info == NULL) { + *reply = NULL; + return PAM_CONV_ERR; + } + + /* + * PAM frees memory in reply messages by itself + * so use malloc instead of talloc here. + */ + *reply = malloc_array_p(struct pam_response, num_msg); + if (*reply == NULL) { + return PAM_CONV_ERR; + } + + for (num = 0; num < num_msg; num++) { + switch (msg[num]->msg_style) { + case PAM_PROMPT_ECHO_ON: + (*reply)[num].resp_retcode = PAM_SUCCESS; + (*reply)[num].resp = COPY_STRING(info->account_name); + break; + + case PAM_PROMPT_ECHO_OFF: + (*reply)[num].resp_retcode = PAM_SUCCESS; + (*reply)[num].resp = COPY_STRING(info->plaintext_password); + break; + + case PAM_TEXT_INFO: + (*reply)[num].resp_retcode = PAM_SUCCESS; + (*reply)[num].resp = NULL; + DEBUG(4,("PAM Info message in conversation function: %s\n", (msg[num]->msg))); + break; + + case PAM_ERROR_MSG: + (*reply)[num].resp_retcode = PAM_SUCCESS; + (*reply)[num].resp = NULL; + DEBUG(4,("PAM Error message in conversation function: %s\n", (msg[num]->msg))); + break; + + default: + while (num > 0) { + SAFE_FREE((*reply)[num-1].resp); + num--; + } + SAFE_FREE(*reply); + *reply = NULL; + DEBUG(1,("Error: PAM subsystme sent an UNKNOWN message type to the conversation function!\n")); + return PAM_CONV_ERR; + } + } + + return PAM_SUCCESS; +} + +/* + * Start PAM authentication for specified account + */ + +static NTSTATUS smb_pam_start(pam_handle_t **pamh, const char *account_name, const char *remote_host, struct pam_conv *pconv) +{ + int pam_error; + + if (account_name == NULL || remote_host == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", account_name)); + + pam_error = pam_start("samba", account_name, pconv, pamh); + if (pam_error != PAM_SUCCESS) { + /* no valid pamh here, can we reliably call pam_strerror ? */ + DEBUG(4,("smb_pam_start: pam_start failed!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + +#ifdef PAM_RHOST + DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", remote_host)); + pam_error = pam_set_item(*pamh, PAM_RHOST, remote_host); + if (pam_error != PAM_SUCCESS) { + NTSTATUS nt_status; + + DEBUG(4,("smb_pam_start: setting rhost failed with error: %s\n", + pam_strerror(*pamh, pam_error))); + nt_status = pam_to_nt_status(pam_error); + + pam_error = pam_end(*pamh, 0); + if (pam_error != PAM_SUCCESS) { + /* no vaild pamh here, can we reliably call pam_strerror ? */ + DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n", + pam_error)); + return pam_to_nt_status(pam_error); + } + return nt_status; + } +#endif +#ifdef PAM_TTY + DEBUG(4,("smb_pam_start: PAM: setting tty\n")); + pam_error = pam_set_item(*pamh, PAM_TTY, "samba"); + if (pam_error != PAM_SUCCESS) { + NTSTATUS nt_status; + + DEBUG(4,("smb_pam_start: setting tty failed with error: %s\n", + pam_strerror(*pamh, pam_error))); + nt_status = pam_to_nt_status(pam_error); + + pam_error = pam_end(*pamh, 0); + if (pam_error != PAM_SUCCESS) { + /* no vaild pamh here, can we reliably call pam_strerror ? */ + DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n", + pam_error)); + return pam_to_nt_status(pam_error); + } + return nt_status; + } +#endif + DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", account_name)); + + return NT_STATUS_OK; +} + +static NTSTATUS smb_pam_end(pam_handle_t *pamh) +{ + int pam_error; + + if (pamh != NULL) { + pam_error = pam_end(pamh, 0); + if (pam_error != PAM_SUCCESS) { + /* no vaild pamh here, can we reliably call pam_strerror ? */ + DEBUG(4,("smb_pam_end: clean up failed, pam_end gave error %d.\n", + pam_error)); + return pam_to_nt_status(pam_error); + } + return NT_STATUS_OK; + } + + DEBUG(2,("smb_pam_end: pamh is NULL, PAM not initialized ?\n")); + return NT_STATUS_UNSUCCESSFUL; +} + +/* + * PAM Authentication Handler + */ +static NTSTATUS smb_pam_auth(pam_handle_t *pamh, bool allow_null_passwords, const char *user) +{ + int pam_error; + + /* + * To enable debugging set in /etc/pam.d/samba: + * auth required /lib/security/pam_pwdb.so nullok shadow audit + */ + + DEBUG(4,("smb_pam_auth: PAM: Authenticate User: %s\n", user)); + + pam_error = pam_authenticate(pamh, PAM_SILENT | allow_null_passwords ? 0 : PAM_DISALLOW_NULL_AUTHTOK); + switch( pam_error ){ + case PAM_AUTH_ERR: + DEBUG(2, ("smb_pam_auth: PAM: Authentication Error for user %s\n", user)); + break; + case PAM_CRED_INSUFFICIENT: + DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user)); + break; + case PAM_AUTHINFO_UNAVAIL: + DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user)); + break; + case PAM_USER_UNKNOWN: + DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user)); + break; + case PAM_MAXTRIES: + DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user)); + break; + case PAM_ABORT: + DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user)); + break; + case PAM_SUCCESS: + DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user)); + break; + default: + DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user)); + break; + } + + return pam_to_nt_status(pam_error); +} + +/* + * PAM Account Handler + */ +static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user) +{ + int pam_error; + + DEBUG(4,("smb_pam_account: PAM: Account Management for User: %s\n", user)); + + pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */ + switch( pam_error ) { + case PAM_AUTHTOK_EXPIRED: + DEBUG(2, ("smb_pam_account: PAM: User %s is valid but password is expired\n", user)); + break; + case PAM_ACCT_EXPIRED: + DEBUG(2, ("smb_pam_account: PAM: User %s no longer permitted to access system\n", user)); + break; + case PAM_AUTH_ERR: + DEBUG(2, ("smb_pam_account: PAM: There was an authentication error for user %s\n", user)); + break; + case PAM_PERM_DENIED: + DEBUG(0, ("smb_pam_account: PAM: User %s is NOT permitted to access system at this time\n", user)); + break; + case PAM_USER_UNKNOWN: + DEBUG(0, ("smb_pam_account: PAM: User \"%s\" is NOT known to account management\n", user)); + break; + case PAM_SUCCESS: + DEBUG(4, ("smb_pam_account: PAM: Account OK for User: %s\n", user)); + break; + default: + DEBUG(0, ("smb_pam_account: PAM: UNKNOWN PAM ERROR (%d) during Account Management for User: %s\n", pam_error, user)); + break; + } + + return pam_to_nt_status(pam_error); +} + +/* + * PAM Credential Setting + */ + +static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, const char * user) +{ + int pam_error; + + /* + * This will allow samba to aquire a kerberos token. And, when + * exporting an AFS cell, be able to /write/ to this cell. + */ + + DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user)); + + pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT)); + switch( pam_error ) { + case PAM_CRED_UNAVAIL: + DEBUG(0, ("smb_pam_setcred: PAM: Credentials not found for user:%s\n", user )); + break; + case PAM_CRED_EXPIRED: + DEBUG(0, ("smb_pam_setcred: PAM: Credentials for user: \"%s\" EXPIRED!\n", user )); + break; + case PAM_USER_UNKNOWN: + DEBUG(0, ("smb_pam_setcred: PAM: User: \"%s\" is NOT known so can not set credentials!\n", user )); + break; + case PAM_CRED_ERR: + DEBUG(0, ("smb_pam_setcred: PAM: Unknown setcredentials error - unable to set credentials for %s\n", user )); + break; + case PAM_SUCCESS: + DEBUG(4, ("smb_pam_setcred: PAM: SetCredentials OK for User: %s\n", user)); + break; + default: + DEBUG(0, ("smb_pam_setcred: PAM: UNKNOWN PAM ERROR (%d) during SetCredentials for User: %s\n", pam_error, user)); + break; + } + + return pam_to_nt_status(pam_error); +} + +static NTSTATUS check_unix_password(TALLOC_CTX *ctx, struct loadparm_context *lp_ctx, + const struct auth_usersupplied_info *user_info, struct passwd **pws) +{ + struct smb_pam_user_info *info; + struct pam_conv *pamconv; + pam_handle_t *pamh; + NTSTATUS nt_status; + + info = talloc(ctx, struct smb_pam_user_info); + if (info == NULL) { + return NT_STATUS_NO_MEMORY; + } + + info->account_name = user_info->mapped.account_name; + info->plaintext_password = user_info->password.plaintext; + + pamconv = talloc(ctx, struct pam_conv); + if (pamconv == NULL) { + return NT_STATUS_NO_MEMORY; + } + + pamconv->conv = smb_pam_conv; + pamconv->appdata_ptr = (void *)info; + + /* TODO: + * check for user_info->flags & USER_INFO_CASE_INSENSITIVE_USERNAME + * if true set up a crack name routine. + */ + + nt_status = smb_pam_start(&pamh, user_info->mapped.account_name, user_info->remote_host ? user_info->remote_host->addr : NULL, pamconv); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + nt_status = smb_pam_auth(pamh, lp_null_passwords(lp_ctx), user_info->mapped.account_name); + if (!NT_STATUS_IS_OK(nt_status)) { + smb_pam_end(pamh); + return nt_status; + } + + if ( ! (user_info->flags & USER_INFO_DONT_CHECK_UNIX_ACCOUNT)) { + + nt_status = smb_pam_account(pamh, user_info->mapped.account_name); + if (!NT_STATUS_IS_OK(nt_status)) { + smb_pam_end(pamh); + return nt_status; + } + + nt_status = smb_pam_setcred(pamh, user_info->mapped.account_name); + if (!NT_STATUS_IS_OK(nt_status)) { + smb_pam_end(pamh); + return nt_status; + } + } + + smb_pam_end(pamh); + + nt_status = talloc_getpwnam(ctx, user_info->mapped.account_name, pws); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + return NT_STATUS_OK; +} + +#else + +/**************************************************************************** +core of password checking routine +****************************************************************************/ +static NTSTATUS password_check(const char *username, const char *password, + const char *crypted, const char *salt) +{ + bool ret; + +#ifdef WITH_AFS + if (afs_auth(username, password)) + return NT_STATUS_OK; +#endif /* WITH_AFS */ + +#ifdef WITH_DFS + if (dfs_auth(username, password)) + return NT_STATUS_OK; +#endif /* WITH_DFS */ + +#ifdef OSF1_ENH_SEC + + ret = (strcmp(osf1_bigcrypt(password, salt), crypted) == 0); + + if (!ret) { + DEBUG(2, + ("OSF1_ENH_SEC failed. Trying normal crypt.\n")); + ret = (strcmp((char *)crypt(password, salt), crypted) == 0); + } + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } + +#endif /* OSF1_ENH_SEC */ + +#ifdef ULTRIX_AUTH + ret = (strcmp((char *)crypt16(password, salt), crypted) == 0); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } + +#endif /* ULTRIX_AUTH */ + +#ifdef LINUX_BIGCRYPT + ret = (linux_bigcrypt(password, salt, crypted)); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } +#endif /* LINUX_BIGCRYPT */ + +#if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS) + + /* + * Some systems have bigcrypt in the C library but might not + * actually use it for the password hashes (HPUX 10.20) is + * a noteable example. So we try bigcrypt first, followed + * by crypt. + */ + + if (strcmp(bigcrypt(password, salt), crypted) == 0) + return NT_STATUS_OK; + else + ret = (strcmp((char *)crypt(password, salt), crypted) == 0); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } +#else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */ + +#ifdef HAVE_BIGCRYPT + ret = (strcmp(bigcrypt(password, salt), crypted) == 0); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } +#endif /* HAVE_BIGCRYPT */ + +#ifndef HAVE_CRYPT + DEBUG(1, ("Warning - no crypt available\n")); + return NT_STATUS_LOGON_FAILURE; +#else /* HAVE_CRYPT */ + ret = (strcmp((char *)crypt(password, salt), crypted) == 0); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } +#endif /* HAVE_CRYPT */ +#endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */ +} + +static NTSTATUS check_unix_password(TALLOC_CTX *ctx, struct loadparm_context *lp_ctx, + const struct auth_usersupplied_info *user_info, struct passwd **ret_passwd) +{ + char *username; + char *password; + char *pwcopy; + char *salt; + char *crypted; + struct passwd *pws; + NTSTATUS nt_status; + int level = lp_passwordlevel(lp_ctx); + + *ret_passwd = NULL; + + username = talloc_strdup(ctx, user_info->mapped.account_name); + password = talloc_strdup(ctx, user_info->password.plaintext); + + nt_status = talloc_getpwnam(ctx, username, &pws); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + crypted = pws->pw_passwd; + salt = pws->pw_passwd; + +#ifdef HAVE_GETSPNAM + { + struct spwd *spass; + + /* many shadow systems require you to be root to get + the password, in most cases this should already be + the case when this function is called, except + perhaps for IPC password changing requests */ + + spass = getspnam(pws->pw_name); + if (spass && spass->sp_pwdp) { + crypted = talloc_strdup(ctx, spass->sp_pwdp); + NT_STATUS_HAVE_NO_MEMORY(crypted); + salt = talloc_strdup(ctx, spass->sp_pwdp); + NT_STATUS_HAVE_NO_MEMORY(salt); + } + } +#elif defined(IA_UINFO) + { + char *ia_password; + /* Need to get password with SVR4.2's ia_ functions + instead of get{sp,pw}ent functions. Required by + UnixWare 2.x, tested on version + 2.1. (tangent@cyberport.com) */ + uinfo_t uinfo; + if (ia_openinfo(pws->pw_name, &uinfo) != -1) { + ia_get_logpwd(uinfo, &ia_password); + crypted = talloc_strdup(ctx, ia_password); + NT_STATUS_HAVE_NO_MEMORY(crypted); + } + } +#endif + +#ifdef HAVE_GETPRPWNAM + { + struct pr_passwd *pr_pw = getprpwnam(pws->pw_name); + if (pr_pw && pr_pw->ufld.fd_encrypt) { + crypted = talloc_strdup(ctx, pr_pw->ufld.fd_encrypt); + NT_STATUS_HAVE_NO_MEMORY(crypted); + } + } +#endif + +#ifdef HAVE_GETPWANAM + { + struct passwd_adjunct *pwret; + pwret = getpwanam(s); + if (pwret && pwret->pwa_passwd) { + crypted = talloc_strdup(ctx, pwret->pwa_passwd); + NT_STATUS_HAVE_NO_MEMORY(crypted); + } + } +#endif + +#ifdef OSF1_ENH_SEC + { + struct pr_passwd *mypasswd; + DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n", username)); + mypasswd = getprpwnam(username); + if (mypasswd) { + username = talloc_strdup(ctx, mypasswd->ufld.fd_name); + NT_STATUS_HAVE_NO_MEMORY(username); + crypted = talloc_strdup(ctx, mypasswd->ufld.fd_encrypt); + NT_STATUS_HAVE_NO_MEMORY(crypted); + } else { + DEBUG(5,("OSF1_ENH_SEC: No entry for user %s in protected database !\n", username)); + } + } +#endif + +#ifdef ULTRIX_AUTH + { + AUTHORIZATION *ap = getauthuid(pws->pw_uid); + if (ap) { + crypted = talloc_strdup(ctx, ap->a_password); + endauthent(); + NT_STATUS_HAVE_NO_MEMORY(crypted); + } + } +#endif + +#if defined(HAVE_TRUNCATED_SALT) + /* crypt on some platforms (HPUX in particular) + won't work with more than 2 salt characters. */ + salt[2] = 0; +#endif + + if (crypted[0] == '\0') { + if (!lp_null_passwords(lp_ctx)) { + DEBUG(2, ("Disallowing %s with null password\n", username)); + return NT_STATUS_LOGON_FAILURE; + } + if (password == NULL) { + DEBUG(3, ("Allowing access to %s with null password\n", username)); + *ret_passwd = pws; + return NT_STATUS_OK; + } + } + + /* try it as it came to us */ + nt_status = password_check(username, password, crypted, salt); + if (NT_STATUS_IS_OK(nt_status)) { + *ret_passwd = pws; + return nt_status; + } + else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) { + /* No point continuing if its not the password thats to blame (ie PAM disabled). */ + return nt_status; + } + + if ( user_info->flags | USER_INFO_CASE_INSENSITIVE_PASSWORD) { + return nt_status; + } + + /* if the password was given to us with mixed case then we don't + * need to proceed as we know it hasn't been case modified by the + * client */ + if (strhasupper(password) && strhaslower(password)) { + return nt_status; + } + + /* make a copy of it */ + pwcopy = talloc_strdup(ctx, password); + if (!pwcopy) + return NT_STATUS_NO_MEMORY; + + /* try all lowercase if it's currently all uppercase */ + if (strhasupper(pwcopy)) { + strlower(pwcopy); + nt_status = password_check(username, pwcopy, crypted, salt); + if NT_STATUS_IS_OK(nt_status) { + *ret_passwd = pws; + return nt_status; + } + } + + /* give up? */ + if (level < 1) { + return NT_STATUS_WRONG_PASSWORD; + } + + /* last chance - all combinations of up to level chars upper! */ + strlower(pwcopy); + +#if 0 + if (NT_STATUS_IS_OK(nt_status = string_combinations(pwcopy, password_check, level))) { + *ret_passwd = pws; + return nt_status; + } +#endif + return NT_STATUS_WRONG_PASSWORD; +} + +#endif + +/** Check a plaintext username/password + * + **/ + +static NTSTATUS authunix_want_check(struct auth_method_context *ctx, + TALLOC_CTX *mem_ctx, + const struct auth_usersupplied_info *user_info) +{ + if (!user_info->mapped.account_name || !*user_info->mapped.account_name) { + return NT_STATUS_NOT_IMPLEMENTED; + } + + return NT_STATUS_OK; +} + +static NTSTATUS authunix_check_password(struct auth_method_context *ctx, + TALLOC_CTX *mem_ctx, + const struct auth_usersupplied_info *user_info, + struct auth_serversupplied_info **server_info) +{ + TALLOC_CTX *check_ctx; + NTSTATUS nt_status; + struct passwd *pwd; + + if (user_info->password_state != AUTH_PASSWORD_PLAIN) { + return NT_STATUS_INVALID_PARAMETER; + } + + check_ctx = talloc_named_const(mem_ctx, 0, "check_unix_password"); + if (check_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + nt_status = check_unix_password(check_ctx, ctx->auth_ctx->lp_ctx, user_info, &pwd); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(check_ctx); + return nt_status; + } + + nt_status = authunix_make_server_info(mem_ctx, lp_netbios_name(ctx->auth_ctx->lp_ctx), + user_info, pwd, server_info); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(check_ctx); + return nt_status; + } + + talloc_free(check_ctx); + return NT_STATUS_OK; +} + +static const struct auth_operations unix_ops = { + .name = "unix", + .get_challenge = auth_get_challenge_not_implemented, + .want_check = authunix_want_check, + .check_password = authunix_check_password +}; + +_PUBLIC_ NTSTATUS auth_unix_init(void) +{ + NTSTATUS ret; + + ret = auth_register(&unix_ops); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register unix auth backend!\n")); + return ret; + } + + return ret; +} |