+++ /dev/null
-/* Unix NT password database implementation, version 0.6.
- *
- * 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 "config.h"
-#include "includes.h"
-#include "general.h"
-
-#include "support.h"
-#include "secrets.h"
-
-#include "../libcli/auth/libcli_auth.h"
-#if defined(HAVE_SECURITY_PAM_EXT_H)
-#include <security/pam_ext.h>
-#elif defined(HAVE_PAM_PAM_EXT_H)
-#include <pam/pam_ext.h>
-#endif
-
-#if defined(HAVE_SECURITY__PAM_MACROS_H)
-#include <security/_pam_macros.h>
-#elif defined(HAVE_PAM__PAM_MACROS_H)
-#include <pam/_pam_macros.h>
-#endif
-
-#ifdef HAVE_SYSLOG_H
-#include <syslog.h>
-#endif
-
-#define _pam_overwrite(x) \
-do { \
- register char *__xx__; \
- if ((__xx__=(x))) \
- while (*__xx__) \
- *__xx__++ = '\0'; \
-} while (0)
-
-/*
- * Don't just free it, forget it too.
- */
-
-#define _pam_drop(X) \
-do { \
- if (X) { \
- free(X); \
- X=NULL; \
- } \
-} while (0)
-
-#define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
-do { \
- int reply_i; \
- \
- for (reply_i=0; reply_i<replies; ++reply_i) { \
- if (reply[reply_i].resp) { \
- _pam_overwrite(reply[reply_i].resp); \
- free(reply[reply_i].resp); \
- } \
- } \
- if (reply) \
- free(reply); \
-} while (0)
-
-
-int converse(pam_handle_t *, int, int, struct pam_message **,
- struct pam_response **);
-int make_remark(pam_handle_t *, unsigned int, int, const char *);
-void _cleanup(pam_handle_t *, void *, int);
-char *_pam_delete(register char *);
-
-/* syslogging function for errors and other information */
-#ifdef HAVE_PAM_VSYSLOG
-void _log_err( pam_handle_t *pamh, int err, const char *format, ... )
-{
- va_list args;
-
- va_start(args, format);
- pam_vsyslog(pamh, err, format, args);
- va_end(args);
-}
-#else
-void _log_err( pam_handle_t *pamh, int err, const char *format, ... )
-{
- va_list args;
- char *mod_format;
-
- if (asprintf(&mod_format, "(pam_smbpass) %s", format) == -1) {
- /*
- * try really, really hard to log something, since
- * this may have been a message about a malloc()
- * failure...
- */
- va_start(args, format);
- vsyslog(err | LOG_AUTH, format, args);
- va_end(args);
- return;
- }
-
- va_start(args, format);
- vsyslog(err | LOG_AUTH, mod_format, args);
- va_end(args);
-
- free(mod_format);
-}
-#endif
-
-/* this is a front-end for module-application conversations */
-
-int converse( pam_handle_t * pamh, int ctrl, int nargs
- , struct pam_message **message
- , struct pam_response **response )
-{
- int retval;
- struct pam_conv *conv = NULL;
-
- retval = _pam_get_item(pamh, PAM_CONV, &conv);
- if (retval == PAM_SUCCESS) {
-
- retval = conv->conv(nargs, (const struct pam_message **) message
- ,response, conv->appdata_ptr);
-
- if (retval != PAM_SUCCESS && on(SMB_DEBUG, ctrl)) {
- _log_err(pamh, LOG_DEBUG, "conversation failure [%s]"
- ,pam_strerror(pamh, retval));
- }
- } else {
- _log_err(pamh, LOG_ERR, "couldn't obtain coversation function [%s]"
- ,pam_strerror(pamh, retval));
- }
-
- return retval; /* propagate error status */
-}
-
-int make_remark( pam_handle_t * pamh, unsigned int ctrl
- , int type, const char *text )
-{
- if (off(SMB__QUIET, ctrl)) {
- struct pam_message *pmsg[1], msg[1];
- struct pam_response *resp;
-
- pmsg[0] = &msg[0];
- msg[0].msg = discard_const_p(char, text);
- msg[0].msg_style = type;
- resp = NULL;
-
- return converse(pamh, ctrl, 1, pmsg, &resp);
- }
- return PAM_SUCCESS;
-}
-
-
-/* set the control flags for the SMB module. */
-
-unsigned int set_ctrl(pam_handle_t *pamh,
- int flags,
- int argc,
- const char **argv)
-{
- int i = 0;
- const char *service_file = NULL;
- unsigned int ctrl;
- bool ok;
-
- ctrl = SMB_DEFAULTS; /* the default selection of options */
-
- /* set some flags manually */
-
- /* A good, sane default (matches Samba's behavior). */
- set( SMB__NONULL, ctrl );
-
- /* initialize service file location */
- service_file=get_dyn_CONFIGFILE();
-
- if (flags & PAM_SILENT) {
- set( SMB__QUIET, ctrl );
- }
-
- /* Run through the arguments once, looking for an alternate smb config
- file location */
- while (i < argc) {
- int j;
-
- for (j = 0; j < SMB_CTRLS_; ++j) {
- if (smb_args[j].token
- && !strncmp(argv[i], smb_args[j].token, strlen(smb_args[j].token)))
- {
- break;
- }
- }
-
- if (j == SMB_CONF_FILE) {
- service_file = argv[i] + 8;
- }
- i++;
- }
-
- /* Read some options from the Samba config. Can be overridden by
- the PAM config. */
- if(lp_load_client(service_file) == false) {
- _log_err(pamh, LOG_ERR, "Error loading service file %s", service_file);
- }
-
- ok = secrets_init();
- if (!ok) {
- _log_err(pamh, LOG_ERR, "Error loading secrets database");
- }
-
- if (lp_null_passwords()) {
- set( SMB__NULLOK, ctrl );
- }
-
- /* now parse the rest of the arguments to this module */
-
- while (argc-- > 0) {
- int j;
-
- for (j = 0; j < SMB_CTRLS_; ++j) {
- if (smb_args[j].token
- && !strncmp(*argv, smb_args[j].token, strlen(smb_args[j].token)))
- {
- break;
- }
- }
-
- if (j >= SMB_CTRLS_) {
- _log_err(pamh, LOG_ERR, "unrecognized option [%s]", *argv);
- } else {
- ctrl &= smb_args[j].mask; /* for turning things off */
- ctrl |= smb_args[j].flag; /* for turning things on */
- }
-
- ++argv; /* step to next argument */
- }
-
- /* auditing is a more sensitive version of debug */
-
- if (on( SMB_AUDIT, ctrl )) {
- set( SMB_DEBUG, ctrl );
- }
- /* return the set of flags */
-
- return ctrl;
-}
-
-/* use this to free strings. ESPECIALLY password strings */
-
-char * _pam_delete( register char *xx )
-{
- _pam_overwrite( xx );
- _pam_drop( xx );
- return NULL;
-}
-
-void _cleanup( pam_handle_t * pamh, void *x, int error_status )
-{
- x = _pam_delete( (char *) x );
-}
-
-/* JHT
- *
- * Safe duplication of character strings. "Paranoid"; don't leave
- * evidence of old token around for later stack analysis.
- *
- */
-char * smbpXstrDup( pam_handle_t *pamh, const char *x )
-{
- register char *newstr = NULL;
-
- if (x != NULL) {
- register int i;
-
- for (i = 0; x[i]; ++i); /* length of string */
- if ((newstr = SMB_MALLOC_ARRAY(char, ++i)) == NULL) {
- i = 0;
- _log_err(pamh, LOG_CRIT, "out of memory in smbpXstrDup");
- } else {
- while (i-- > 0) {
- newstr[i] = x[i];
- }
- }
- x = NULL;
- }
- return newstr; /* return the duplicate or NULL on error */
-}
-
-/* ************************************************************** *
- * Useful non-trivial functions *
- * ************************************************************** */
-
-void _cleanup_failures( pam_handle_t * pamh, void *fl, int err )
-{
- int quiet;
- const char *service = NULL;
- struct _pam_failed_auth *failure;
-
-#ifdef PAM_DATA_SILENT
- quiet = err & PAM_DATA_SILENT; /* should we log something? */
-#else
- quiet = 0;
-#endif
-#ifdef PAM_DATA_REPLACE
- err &= PAM_DATA_REPLACE; /* are we just replacing data? */
-#endif
- failure = (struct _pam_failed_auth *) fl;
-
- if (failure != NULL) {
-
-#ifdef PAM_DATA_SILENT
- if (!quiet && !err) { /* under advisement from Sun,may go away */
-#else
- if (!quiet) { /* under advisement from Sun,may go away */
-#endif
-
- /* log the number of authentication failures */
- if (failure->count != 0) {
- _pam_get_item( pamh, PAM_SERVICE, &service );
- _log_err(pamh, LOG_NOTICE
- , "%d authentication %s "
- "from %s for service %s as %s(%d)"
- , failure->count
- , failure->count == 1 ? "failure" : "failures"
- , failure->agent
- , service == NULL ? "**unknown**" : service
- , failure->user, failure->id );
- if (failure->count > SMB_MAX_RETRIES) {
- _log_err(pamh, LOG_ALERT
- , "service(%s) ignoring max retries; %d > %d"
- , service == NULL ? "**unknown**" : service
- , failure->count
- , SMB_MAX_RETRIES );
- }
- }
- }
- _pam_delete( failure->agent ); /* tidy up */
- _pam_delete( failure->user ); /* tidy up */
- SAFE_FREE( failure );
- }
-}
-
-int _smb_verify_password( pam_handle_t * pamh, struct samu *sampass,
- const char *p, unsigned int ctrl )
-{
- uchar lm_pw[16];
- uchar nt_pw[16];
- int retval = PAM_AUTH_ERR;
- char *data_name;
- const char *name;
-
- if (!sampass)
- return PAM_ABORT;
-
- name = pdb_get_username(sampass);
-
-#ifdef HAVE_PAM_FAIL_DELAY
- if (off( SMB_NODELAY, ctrl )) {
- (void) pam_fail_delay( pamh, 1000000 ); /* 1 sec delay for on failure */
- }
-#endif
-
- if (!pdb_get_nt_passwd(sampass))
- {
- _log_err(pamh, LOG_DEBUG, "user %s has null SMB password", name);
-
- if (off( SMB__NONULL, ctrl )
- && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ))
- { /* this means we've succeeded */
- return PAM_SUCCESS;
- } else {
- const char *service = NULL;
-
- _pam_get_item( pamh, PAM_SERVICE, &service );
- _log_err(pamh, LOG_NOTICE, "failed auth request by %s for service %s as %s",
- uidtoname(getuid()), service ? service : "**unknown**", name);
- return PAM_AUTH_ERR;
- }
- }
-
- if (asprintf(&data_name, "-SMB-FAIL- %s", name) == -1) {
- _log_err(pamh, LOG_CRIT, "no memory for data-name" );
- return PAM_AUTH_ERR;
- }
-
- /*
- * The password we were given wasn't an encrypted password, or it
- * didn't match the one we have. We encrypt the password now and try
- * again.
- */
-
- nt_lm_owf_gen(p, nt_pw, lm_pw);
-
- /* the moment of truth -- do we agree with the password? */
-
- if (!memcmp( nt_pw, pdb_get_nt_passwd(sampass), 16 )) {
-
- retval = PAM_SUCCESS;
- if (data_name) { /* reset failures */
- pam_set_data(pamh, data_name, NULL, _cleanup_failures);
- }
- } else {
-
- const char *service = NULL;
-
- retval = PAM_AUTH_ERR;
-
- _pam_get_item( pamh, PAM_SERVICE, &service );
-
- if (data_name != NULL) {
- struct _pam_failed_auth *newauth = NULL;
- const struct _pam_failed_auth *old = NULL;
-
- /* get a failure recorder */
-
- newauth = SMB_MALLOC_P( struct _pam_failed_auth );
-
- if (newauth != NULL) {
-
- /* any previous failures for this user ? */
- _pam_get_data(pamh, data_name, &old);
-
- if (old != NULL) {
- newauth->count = old->count + 1;
- if (newauth->count >= SMB_MAX_RETRIES) {
- retval = PAM_MAXTRIES;
- }
- } else {
- _log_err(pamh, LOG_NOTICE,
- "failed auth request by %s for service %s as %s",
- uidtoname(getuid()),
- service ? service : "**unknown**", name);
- newauth->count = 1;
- }
- if (!sid_to_uid(pdb_get_user_sid(sampass), &(newauth->id))) {
- _log_err(pamh, LOG_NOTICE,
- "failed auth request by %s for service %s as %s",
- uidtoname(getuid()),
- service ? service : "**unknown**", name);
- }
- newauth->user = smbpXstrDup( pamh, name );
- newauth->agent = smbpXstrDup( pamh, uidtoname( getuid() ) );
- pam_set_data( pamh, data_name, newauth, _cleanup_failures );
-
- } else {
- _log_err(pamh, LOG_CRIT, "no memory for failure recorder" );
- _log_err(pamh, LOG_NOTICE,
- "failed auth request by %s for service %s as %s(%d)",
- uidtoname(getuid()),
- service ? service : "**unknown**", name);
- }
- }
- _log_err(pamh, LOG_NOTICE,
- "failed auth request by %s for service %s as %s(%d)",
- uidtoname(getuid()),
- service ? service : "**unknown**", name);
- }
-
- _pam_delete( data_name );
-
- return retval;
-}
-
-
-/*
- * _smb_blankpasswd() is a quick check for a blank password
- *
- * returns TRUE if user does not have a password
- * - to avoid prompting for one in such cases (CG)
- */
-
-int _smb_blankpasswd( unsigned int ctrl, struct samu *sampass )
-{
- int retval;
-
- /*
- * This function does not have to be too smart if something goes
- * wrong, return FALSE and let this case to be treated somewhere
- * else (CG)
- */
-
- if (on( SMB__NONULL, ctrl ))
- return 0; /* will fail but don't let on yet */
-
- if (!(pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ))
- return 0;
-
- if (pdb_get_nt_passwd(sampass) == NULL)
- retval = 1;
- else
- retval = 0;
-
- return retval;
-}
-
-/*
- * obtain a password from the user
- */
-
-int _smb_read_password( pam_handle_t * pamh, unsigned int ctrl,
- const char *comment, const char *prompt1,
- const char *prompt2, const char *data_name, char **pass )
-{
- int authtok_flag;
- int retval;
- char *item = NULL;
- char *token;
-
- struct pam_message msg[3], *pmsg[3];
- struct pam_response *resp;
- int i, expect;
-
-
- /* make sure nothing inappropriate gets returned */
-
- *pass = token = NULL;
-
- /* which authentication token are we getting? */
-
- authtok_flag = on(SMB__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
-
- /* should we obtain the password from a PAM item ? */
-
- if (on(SMB_TRY_FIRST_PASS, ctrl) || on(SMB_USE_FIRST_PASS, ctrl)) {
- retval = _pam_get_item( pamh, authtok_flag, &item );
- if (retval != PAM_SUCCESS) {
- /* very strange. */
- _log_err(pamh, LOG_ALERT,
- "pam_get_item returned error to smb_read_password");
- return retval;
- } else if (item != NULL) { /* we have a password! */
- *pass = item;
- item = NULL;
- return PAM_SUCCESS;
- } else if (on( SMB_USE_FIRST_PASS, ctrl )) {
- return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
- } else if (on( SMB_USE_AUTHTOK, ctrl )
- && off( SMB__OLD_PASSWD, ctrl ))
- {
- return PAM_AUTHTOK_RECOVER_ERR;
- }
- }
-
- /*
- * getting here implies we will have to get the password from the
- * user directly.
- */
-
- /* prepare to converse */
- if (comment != NULL && off(SMB__QUIET, ctrl)) {
- pmsg[0] = &msg[0];
- msg[0].msg_style = PAM_TEXT_INFO;
- msg[0].msg = discard_const_p(char, comment);
- i = 1;
- } else {
- i = 0;
- }
-
- pmsg[i] = &msg[i];
- msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
- msg[i++].msg = discard_const_p(char, prompt1);
-
- if (prompt2 != NULL) {
- pmsg[i] = &msg[i];
- msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
- msg[i++].msg = discard_const_p(char, prompt2);
- expect = 2;
- } else
- expect = 1;
-
- resp = NULL;
-
- retval = converse( pamh, ctrl, i, pmsg, &resp );
-
- if (resp != NULL) {
- int j = comment ? 1 : 0;
- /* interpret the response */
-
- if (retval == PAM_SUCCESS) { /* a good conversation */
-
- token = smbpXstrDup(pamh, resp[j++].resp);
- if (token != NULL) {
- if (expect == 2) {
- /* verify that password entered correctly */
- if (!resp[j].resp || strcmp( token, resp[j].resp )) {
- _pam_delete( token );
- retval = PAM_AUTHTOK_RECOVER_ERR;
- make_remark( pamh, ctrl, PAM_ERROR_MSG
- , MISTYPED_PASS );
- }
- }
- } else {
- _log_err(pamh, LOG_NOTICE,
- "could not recover authentication token");
- }
- }
-
- /* tidy up */
- _pam_drop_reply( resp, expect );
-
- } else {
- retval = (retval == PAM_SUCCESS) ? PAM_AUTHTOK_RECOVER_ERR : retval;
- }
-
- if (retval != PAM_SUCCESS) {
- if (on( SMB_DEBUG, ctrl ))
- _log_err(pamh, LOG_DEBUG, "unable to obtain a password");
- return retval;
- }
- /* 'token' is the entered password */
-
- if (off( SMB_NOT_SET_PASS, ctrl )) {
-
- /* we store this password as an item */
-
- retval = pam_set_item( pamh, authtok_flag, (const void *)token );
- _pam_delete( token ); /* clean it up */
- if (retval != PAM_SUCCESS
- || (retval = _pam_get_item( pamh, authtok_flag
- ,&item )) != PAM_SUCCESS)
- {
- _log_err(pamh, LOG_CRIT, "error manipulating password");
- return retval;
- }
- } else {
- /*
- * then store it as data specific to this module. pam_end()
- * will arrange to clean it up.
- */
-
- retval = pam_set_data( pamh, data_name, (void *) token, _cleanup );
- if (retval != PAM_SUCCESS
- || (retval = _pam_get_data( pamh, data_name, &item ))
- != PAM_SUCCESS)
- {
- _log_err(pamh, LOG_CRIT, "error manipulating password data [%s]",
- pam_strerror( pamh, retval ));
- _pam_delete( token );
- item = NULL;
- return retval;
- }
- token = NULL; /* break link to password */
- }
-
- *pass = item;
- item = NULL; /* break link to password */
-
- return PAM_SUCCESS;
-}
-
-int _pam_smb_approve_pass(pam_handle_t * pamh,
- unsigned int ctrl,
- const char *pass_old,
- const char *pass_new )
-{
-
- /* Further checks should be handled through module stacking. -SRL */
- if (pass_new == NULL || (pass_old && !strcmp( pass_old, pass_new )))
- {
- if (on(SMB_DEBUG, ctrl)) {
- _log_err(pamh, LOG_DEBUG,
- "passwd: bad authentication token (null or unchanged)");
- }
- make_remark( pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
- "No password supplied" : "Password unchanged" );
- return PAM_AUTHTOK_ERR;
- }
-
- return PAM_SUCCESS;
-}
-
-/*
- * Work around the pam API that has functions with void ** as parameters
- * These lead to strict aliasing warnings with gcc.
- */
-int _pam_get_item(const pam_handle_t *pamh,
- int item_type,
- const void *_item)
-{
- const void **item = (const void **)_item;
- return pam_get_item(pamh, item_type, item);
-}
-
-int _pam_get_data(const pam_handle_t *pamh,
- const char *module_data_name,
- const void *_data)
-{
- const void **data = (const void **)_data;
- return pam_get_data(pamh, module_data_name, data);
-}