From d870c3481eb8142b52072759028eaa793a188821 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Fri, 3 Jun 2005 14:06:23 +0000 Subject: r7232: add some more auth stuff enables us to authenticate against system users (only PAM support right now) (This used to be commit 0c894de58b7850a2905b73eb17c42d7e87cb9f87) --- source4/auth/auth_unix.c | 499 +++++++++++++++++++++++++++++++++++++++-------- source4/auth/config.m4 | 11 ++ source4/auth/config.mk | 12 ++ 3 files changed, 442 insertions(+), 80 deletions(-) create mode 100644 source4/auth/config.m4 (limited to 'source4') diff --git a/source4/auth/auth_unix.c b/source4/auth/auth_unix.c index 01868dd949..61698671ac 100644 --- a/source4/auth/auth_unix.c +++ b/source4/auth/auth_unix.c @@ -1,7 +1,9 @@ /* Unix SMB/CIFS implementation. Password and authentication handling - Copyright (C) Andrew Bartlett 2001 + 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 @@ -19,128 +21,465 @@ */ #include "includes.h" +#include "auth/auth.h" +#include "system/passwd.h" -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_AUTH -/** - * update the encrypted smbpasswd file from the plaintext username and password - * - * this ugly hack needs to die, but not quite yet, I think people still use it... - **/ -static BOOL update_smbpassword_file(const char *user, const char *password) +/* 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 struct auth_usersupplied_info *user_info, + struct auth_serversupplied_info **_server_info) { - SAM_ACCOUNT *sampass = NULL; - BOOL ret; - - pdb_init_sam(&sampass); + struct auth_serversupplied_info *server_info; + + 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_strdup(server_info, talloc_strdup(mem_ctx, user_info->account_name)); + NT_STATUS_HAVE_NO_MEMORY(server_info->account_name); + + server_info->domain_name = talloc_strdup(server_info, talloc_strdup(mem_ctx, "unix")); + NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name); + + + /* is this 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_strdup(server_info, ""); + 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; +} + +#ifdef HAVE_SECURITY_PAM_APPL_H +#include + +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; + } - become_root(); - ret = pdb_getsampwnam(sampass, user); - unbecome_root(); + /* + * Apparantly HPUX has a buggy PAM that doesn't support the + * data pointer. Fail if this is the case. JRA. + */ - if(ret == False) { - DEBUG(0,("pdb_getsampwnam returned NULL\n")); - pdb_free_sam(&sampass); - return False; + if (info == NULL) { + *reply = NULL; + return PAM_CONV_ERR; } /* - * Remove the account disabled flag - we are updating the - * users password from a login. + * PAM frees memory in reply messages by itself + * so use malloc instead of talloc here. */ - if (!pdb_set_acct_ctrl(sampass, pdb_get_acct_ctrl(sampass) & ~ACB_DISABLED, PDB_CHANGED)) { - pdb_free_sam(&sampass); - return False; + *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))); + + 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: + SAFE_FREE(*reply); + *reply = NULL; + DEBUG(1,("Error: PAM subsystme sent an UNKNOWN message type to the conversation function!\n")); + return PAM_CONV_ERR; + } } - if (!pdb_set_plaintext_passwd (sampass, password)) { - pdb_free_sam(&sampass); - return False; + 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) +{ + NTSTATUS nt_status; + int pam_error; + + if (account_name == NULL || remote_host == NULL) { + return NT_STATUS_INVALID_PARAMETER; } - /* Now write it into the file. */ - become_root(); + DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", account_name)); - ret = pdb_update_sam_account (sampass); + pam_error = pam_start("samba", account_name, pconv, pamh); + if (pam_error != PAM_SUCCESS) { + /* no vaild pamh here, can we reliably call pam_strerror ? */ + DEBUG(4,("smb_pam_start: pam_start failed!\n")); + return NT_STATUS_UNSUCCESSFUL; + } - unbecome_root(); +#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) { + 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); - if (ret) { - DEBUG(3,("pdb_update_sam_account returned %d\n",ret)); + 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) { + 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); - pdb_free_sam(&sampass); - return ret; + 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; -/** Check a plaintext username/password - * - * Cannot deal with an encrupted password in any manner whatsoever, - * unless the account has a null password. - **/ + 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; + } -static NTSTATUS check_unix_security(const struct auth_context *auth_context, - void *my_private_data, - TALLOC_CTX *mem_ctx, - const auth_usersupplied_info *user_info, - auth_serversupplied_info **server_info) + 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, 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 | lp_null_passwords() ? 0 : PAM_DISALLOW_NULL_AUTHTOK); + switch( pam_error ){ + case PAM_AUTH_ERR: + DEBUG(2, ("smb_pam_auth: PAM: Athentication 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, const struct auth_usersupplied_info *user_info) +{ + struct smb_pam_user_info *info; + struct pam_conv *pamconv; + pam_handle_t *pamh; NTSTATUS nt_status; - struct passwd *pass = NULL; - become_root(); - pass = Get_Pwnam(user_info->internal_username.str); + info = talloc(ctx, struct smb_pam_user_info); + if (info == NULL) { + return NT_STATUS_NO_MEMORY; + } - - /** @todo This call assumes a ASCII password, no charset transformation is - done. We may need to revisit this **/ - nt_status = pass_check(pass, - pass ? pass->pw_name : user_info->internal_username.str, - (char *)user_info->plaintext_password.data, - user_info->plaintext_password.length-1, - lp_update_encrypted() ? - update_smbpassword_file : NULL, - True); - - unbecome_root(); - - if (NT_STATUS_IS_OK(nt_status)) { - if (pass) { - make_server_info_pw(auth_context, server_info, pass); - } else { - /* we need to do somthing more useful here */ - nt_status = NT_STATUS_NO_SUCH_USER; + info->account_name = user_info->account_name; + info->plaintext_password = (char *)(user_info->plaintext_password.data); + + 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->account_name, user_info->remote_host, pamconv); + if (!NT_STATUS_IS_OK(nt_status)) { + smb_pam_end(pamh); + return nt_status; + } + + nt_status = smb_pam_auth(pamh, user_info->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->account_name); + if (!NT_STATUS_IS_OK(nt_status)) { + smb_pam_end(pamh); + return nt_status; + } + + nt_status = smb_pam_setcred(pamh, user_info->account_name); + if (!NT_STATUS_IS_OK(nt_status)) { + smb_pam_end(pamh); + return nt_status; } } - return nt_status; + smb_pam_end(pamh); + return NT_STATUS_OK; } -/* module initialisation */ -static NTSTATUS auth_init_unix(struct auth_context *auth_context, const char* param, auth_methods **auth_method) +#else + +static NTSTATUS check_unix_password(TALLOC_CTX *ctx, const struct auth_usersupplied_info *user_info) { - if (!make_auth_methods(auth_context, auth_method)) { + return NT_STATUS_UNIMPLEMENTED; +} + +#endif /*(HAVE_SECURITY_PAM_APPL_H)*/ + +/** Check a plaintext username/password + * + **/ + +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; + + if (! user_info->account_name && ! *user_info->account_name) { + 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; } - (*auth_method)->name = "unix"; - (*auth_method)->auth = check_unix_security; + nt_status = check_unix_password(mem_ctx, user_info); + if ( ! NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + nt_status = authunix_make_server_info(mem_ctx, user_info, server_info); + if ( ! NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + return NT_STATUS_OK; } +static const struct auth_operations unix_ops = { + .name = "unix", + .get_challenge = auth_get_challenge_not_implemented, + .check_password = authunix_check_password +}; + NTSTATUS auth_unix_init(void) { NTSTATUS ret; - struct auth_operations ops; - ops.name = "unix"; - ops.init = auth_init_unix; - ret = register_backend("auth", &ops); + ret = auth_register(&unix_ops); if (!NT_STATUS_IS_OK(ret)) { - DEBUG(0,("Failed to register '%s' auth backend!\n", - ops.name)); + DEBUG(0,("Failed to register unix auth backend!\n")); return ret; } diff --git a/source4/auth/config.m4 b/source4/auth/config.m4 new file mode 100644 index 0000000000..5e069ce57b --- /dev/null +++ b/source4/auth/config.m4 @@ -0,0 +1,11 @@ +############################### +# start SMB_EXT_LIB_PAM +# check for security/pam_appl.h and -lpam +AC_CHECK_HEADERS(security/pam_appl.h) +AC_CHECK_LIB_EXT(pam, PAM_LIBS, pam_start) +if test x"$ac_cv_header_security_pam_appl_h" = x"yes" -a x"$ac_cv_lib_ext_pam_pam_start" = x"yes";then + SMB_EXT_LIB_ENABLE(PAM,YES) +fi +SMB_EXT_LIB(PAM, $PAM_LIBS) +# end SMB_EXT_LIB_PAM +############################### diff --git a/source4/auth/config.mk b/source4/auth/config.mk index 99e590faa1..ccf310f07b 100644 --- a/source4/auth/config.mk +++ b/source4/auth/config.mk @@ -57,6 +57,18 @@ INIT_OBJ_FILES = \ # End MODULE auth_developer ####################### +####################### +# Start MODULE auth_developer +[MODULE::auth_unix] +INIT_FUNCTION = auth_unix_init +SUBSYSTEM = AUTH +INIT_OBJ_FILES = \ + auth/auth_unix.o +REQUIRED_SUBSYSTEMS = \ + EXT_LIB_PAM PAM_ERRORS +# End MODULE auth_developer +####################### + ####################### # Start SUBSYSTEM AUTH [SUBSYSTEM::AUTH] -- cgit