Added Steve Langasek <vorlon@netexpress.net> pam_smbpass PAM module code.
authorJohn Terpstra <jht@samba.org>
Tue, 24 Apr 2001 20:00:12 +0000 (20:00 +0000)
committerJohn Terpstra <jht@samba.org>
Tue, 24 Apr 2001 20:00:12 +0000 (20:00 +0000)
Note: Still have to add build stuff - not ready yet.
(This used to be commit 1de7022f98b64b15503aaf48c8d729789fc49781)

14 files changed:
source3/pam_smbpass/CHANGELOG [new file with mode: 0644]
source3/pam_smbpass/README [new file with mode: 0644]
source3/pam_smbpass/TODO [new file with mode: 0644]
source3/pam_smbpass/general.h [new file with mode: 0644]
source3/pam_smbpass/pam_smb_acct.c [new file with mode: 0644]
source3/pam_smbpass/pam_smb_auth.c [new file with mode: 0644]
source3/pam_smbpass/pam_smb_passwd.c [new file with mode: 0644]
source3/pam_smbpass/samples/README [new file with mode: 0644]
source3/pam_smbpass/samples/kdc-pdc [new file with mode: 0644]
source3/pam_smbpass/samples/password-mature [new file with mode: 0644]
source3/pam_smbpass/samples/password-migration [new file with mode: 0644]
source3/pam_smbpass/samples/password-sync [new file with mode: 0644]
source3/pam_smbpass/support.c [new file with mode: 0644]
source3/pam_smbpass/support.h [new file with mode: 0644]

diff --git a/source3/pam_smbpass/CHANGELOG b/source3/pam_smbpass/CHANGELOG
new file mode 100644 (file)
index 0000000..96ef784
--- /dev/null
@@ -0,0 +1,31 @@
+version 0.7.5  25 Mar 2001
+   - Use Samba 2.2.0 (alpha) as the target codebase, since it doesn't look
+     like Samba will be offering shared libraries in the near future.
+   - added a Makefile and support scripts to make the build process easier.
+   - imported some Solaris fixes that I've been sitting on.
+
+version 0.7.4  20 Jan 2000
+   - added a 'migrate' option to the authentication code which makes no
+     effort to authenticate the user, or even to ask for a password, but
+     it can be useful for filling in an SMB password db.
+
+version 0.7.3  19 Jan 2000
+   - updated to use the SAMBA_TNG Samba branch, allowing us to dynamically
+     link against Luke's new shared libs (libsamba, libsmb).
+
+version 0.7.2  20 Jul 1999
+   - miscellaneous bugfixes.  Cleanup of legacy pam_pwdb code.
+   - fixed return value of pam_sm_setcred function.
+   - fix to autoconf support
+   - clarified some of the messages being logged
+
+version 0.6,  15 Jul 1999
+   - updated to use the new Samba (2.0) password database API.
+   - added autoconf support. May now theoretically compile on more
+     platforms than PAM itself does.
+   - added support for account management functions (i.e., disabled
+     accounts)
+
+version 0.5,  4 Apr 1998
+   - added support for hashed passwords as input.  Now capable of serving
+     as an authentication agent for encrypted network transactions.
diff --git a/source3/pam_smbpass/README b/source3/pam_smbpass/README
new file mode 100644 (file)
index 0000000..6f50ce4
--- /dev/null
@@ -0,0 +1,66 @@
+25 Mar 2001
+
+pam_smbpass is a PAM module which can be used on conforming systems to
+keep the smbpasswd (Samba password) database in sync with the unix
+password file. PAM (Pluggable Authentication Modules) is an API supported
+under some Unices, such as Solaris, HPUX and Linux, that provides a
+generic interface to authentication mechanisms.
+
+For more information on PAM, see http://ftp.kernel.org/pub/linux/libs/pam/
+
+This module authenticates a local smbpasswd user database.  If you require
+support for authenticating against a remote SMB server, or if you're
+concerned about the presence of suid root binaries on your system, it is
+recommended that you use one of the other two following modules
+
+  pam_smb - http://www.csn.ul.ie/~airlied/pam_smb/
+    authenticates against any remote SMB server
+
+  pam_ntdom - ftp://ftp.samba.org/pub/samba/pam_ntdom/
+    authenticates against an NT or Samba domain controller
+
+Options recognized by this module are as follows:
+
+       debug           -       log more debugging info
+       audit           -       like debug, but also logs unknown usernames
+       use_first_pass  -       don't prompt the user for passwords;
+                               take them from PAM_ items instead
+       try_first_pass  -       try to get the password from a previous
+                               PAM module, fall back to prompting the user
+       use_authtok     -       like try_first_pass, but *fail* if the new
+                               PAM_AUTHTOK has not been previously set.
+                               (intended for stacking password modules only)
+       not_set_pass    -       don't make passwords used by this module
+                               available to other modules.
+       nodelay         -       don't insert ~1 second delays on authentication
+                               failure.
+       nullok          -       null passwords are allowed.
+       nonull          -       null passwords are not allowed. Used to
+                               override the Samba configuration.
+       migrate         -       only meaningful in an "auth" context;
+                               used to update smbpasswd file with a
+                               password used for successful authentication.
+       smbconf=<file>  -       specify an alternate path to the smb.conf
+                               file.
+
+See the samples/ directory for example PAM configurations using this
+module.
+
+Thanks go to the following people:
+
+* Andrew Morgan <morgan@transmeta.com>, for providing the Linux-PAM
+framework, without which none of this would have happened
+
+* Christian Gafton <gafton@redhat.com> and Andrew Morgan again, for the
+pam_pwdb module upon which pam_smbpass was originally based
+
+* Luke Leighton <lkcl@switchboard.net> for being receptive to the idea,
+and for the occasional good-natured complaint about the project's status
+that keep me working on it :)
+
+* and of course, all the other members of the Samba team 
+<samba-bugs@samba.org>, for creating a great product and for giving this
+project a purpose
+
+---------------------
+Stephen Langasek <vorlon@netexpress.net>
diff --git a/source3/pam_smbpass/TODO b/source3/pam_smbpass/TODO
new file mode 100644 (file)
index 0000000..20cf4fb
--- /dev/null
@@ -0,0 +1,7 @@
+This is a tentative TODO file which will probably get much longer before
+it gets much shorter.
+
+- Recognizing (and overriding) debug options in the smb.conf file
+- Support for 'name=value' parameters in the PAM config
+- Compliant handling of unrecognized PAM parameters (i.e., fail on error)
+- 
diff --git a/source3/pam_smbpass/general.h b/source3/pam_smbpass/general.h
new file mode 100644 (file)
index 0000000..0291146
--- /dev/null
@@ -0,0 +1,123 @@
+#ifndef LINUX
+/* This is only needed by modules in the Sun implementation. */
+#include <security/pam_appl.h>
+#endif  /* LINUX */
+
+#include <security/pam_modules.h>
+
+#ifndef PAM_AUTHTOK_RECOVER_ERR  
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+/* type definition for the control options */
+
+typedef struct {
+     const char *token;
+     unsigned int mask;            /* shall assume 32 bits of flags */
+     unsigned int flag;
+} SMB_Ctrls;
+
+#ifndef False
+#define False (0)
+#endif
+
+#ifndef True
+#define True (1)
+#endif
+
+/* macro to determine if a given flag is on */
+#define on(x,ctrl)  (smb_args[x].flag & ctrl)
+
+/* macro to determine that a given flag is NOT on */
+#define off(x,ctrl) (!on(x,ctrl))
+
+/* macro to turn on/off a ctrl flag manually */
+#define set(x,ctrl)   (ctrl = ((ctrl)&smb_args[x].mask)|smb_args[x].flag)
+#define unset(x,ctrl) (ctrl &= ~(smb_args[x].flag))
+
+#ifndef __linux__
+#define strncasecmp(s1,s2,n) StrnCaseCmp(s1,s2,n)
+#endif
+
+/* the generic mask */
+#define _ALL_ON_  (~0U)
+
+/* end of macro definitions definitions for the control flags */
+
+/*
+ * These are the options supported by the smb password module, very
+ * similar to the pwdb options
+ */
+
+#define SMB__OLD_PASSWD                 0      /* internal */
+#define SMB__VERIFY_PASSWD      1      /* internal */
+
+#define SMB_AUDIT               2      /* print more things than debug..
+                                          some information may be sensitive */
+#define SMB_USE_FIRST_PASS      3
+#define SMB_TRY_FIRST_PASS      4
+#define SMB_NOT_SET_PASS        5      /* don't set the AUTHTOK items */
+
+#define SMB__NONULL             6      /* internal */
+#define SMB__QUIET              7      /* internal */
+#define SMB_USE_AUTHTOK                 8      /* insist on reading PAM_AUTHTOK */
+#define SMB__NULLOK             9      /* Null token ok */
+#define SMB_DEBUG              10      /* send more info to syslog(3) */
+#define SMB_NODELAY            11      /* admin does not want a fail-delay */
+#define SMB_MIGRATE            12      /* Does no authentication, just
+                                          updates the smb database. */
+#define SMB_CONF_FILE          13      /* Alternate location of smb.conf */
+
+#define SMB_CTRLS_             14      /* number of ctrl arguments defined */
+
+static const SMB_Ctrls smb_args[SMB_CTRLS_] = {
+/* symbol                 token name          ctrl mask      ctrl       *
+ * ------------------     ------------------  -------------- ---------- */
+
+/* SMB__OLD_PASSWD */   {  NULL,            _ALL_ON_,              01 },
+/* SMB__VERIFY_PASSWD */ {  NULL,            _ALL_ON_,              02 },
+/* SMB_AUDIT */                 { "audit",          _ALL_ON_,              04 },
+/* SMB_USE_FIRST_PASS */ { "use_first_pass", _ALL_ON_^(030),       010 },
+/* SMB_TRY_FIRST_PASS */ { "try_first_pass", _ALL_ON_^(030),       020 },
+/* SMB_NOT_SET_PASS */  { "not_set_pass",   _ALL_ON_,             040 },
+/* SMB__NONULL */       {  "nonull",        _ALL_ON_,            0100 },
+/* SMB__QUIET */        {  NULL,            _ALL_ON_,            0200 },
+/* SMB_USE_AUTHTOK */   { "use_authtok",    _ALL_ON_,            0400 },
+/* SMB__NULLOK */       { "nullok",         _ALL_ON_^(0100),        0 },
+/* SMB_DEBUG */                 { "debug",          _ALL_ON_,           01000 },
+/* SMB_NODELAY */       { "nodelay",        _ALL_ON_,           02000 },
+/* SMB_MIGRATE */       { "migrate",        _ALL_ON_^(0100),    04000 },
+/* SMB_CONF_FILE */     { "smbconf=",       _ALL_ON_,               0 },
+};
+
+#define SMB_DEFAULTS  (smb_args[SMB__NONULL].flag)
+
+/*
+ * the following is used to keep track of the number of times a user fails
+ * to authenticate themself.
+ */
+
+#define FAIL_PREFIX                    "-SMB-FAIL-"
+#define SMB_MAX_RETRIES                        3
+
+struct _pam_failed_auth {
+    char *user;                 /* user that's failed to be authenticated */
+    int id;                     /* uid of requested user */
+    char *agent;                /* attempt from user with name */
+    int count;                  /* number of failures so far */
+};
diff --git a/source3/pam_smbpass/pam_smb_acct.c b/source3/pam_smbpass/pam_smb_acct.c
new file mode 100644 (file)
index 0000000..af0c21c
--- /dev/null
@@ -0,0 +1,113 @@
+/* Unix NT password database implementation, version 0.7.5.
+ *
+ * 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.
+*/
+
+/* indicate the following groups are defined */
+#define PAM_SM_ACCT
+
+#include "includes.h"
+
+#ifndef LINUX
+
+/* This is only used in the Sun implementation. */
+#include <security/pam_appl.h>
+
+#endif  /* LINUX */
+
+#include <security/pam_modules.h>
+
+#include "general.h"
+
+#include "support.h"
+
+/*
+ * pam_sm_acct_mgmt() verifies whether or not the account is disabled.
+ *
+ */
+
+int pam_sm_acct_mgmt( pam_handle_t *pamh, int flags,
+                      int argc, const char **argv )
+{
+    unsigned int ctrl;
+    int retval;
+
+    const char *name;
+    const char *p;
+    struct smb_passwd *smb_pwent = NULL;
+
+    extern BOOL in_client;
+
+    /* Samba initialization. */
+    setup_logging( "pam_smbpass", False );
+    charset_initialise();
+    in_client = True;
+
+    ctrl = set_ctrl( flags, argc, argv );
+
+    /* get the username */
+
+    retval = pam_get_user( pamh, &name, "Username: " );
+    if (retval != PAM_SUCCESS) {
+        if (on( SMB_DEBUG, ctrl )) {
+           _log_err( LOG_DEBUG, "acct: could not identify user" );
+        }
+        return retval;
+    }
+    if (on( SMB_DEBUG, ctrl )) {
+        _log_err( LOG_DEBUG, "acct: username [%s] obtained", name );
+    }
+
+    if (!initialize_password_db()) {
+        _log_err( LOG_ALERT, "Cannot access samba password database" );
+        return PAM_AUTHINFO_UNAVAIL;
+    }
+
+    /* Get the user's record. */
+    smb_pwent = getsmbpwnam( name );
+
+    if (!smb_pwent)
+        return PAM_USER_UNKNOWN;
+
+    if (smb_pwent->acct_ctrl & ACB_DISABLED) {
+        if (on( SMB_DEBUG, ctrl )) {
+            _log_err( LOG_DEBUG
+                      , "acct: account %s is administratively disabled", name );
+        }
+        make_remark( pamh, ctrl, PAM_ERROR_MSG
+                     , "Your account has been disabled; "
+                       "please see your system administrator." );
+
+        return PAM_ACCT_EXPIRED;
+    }
+
+    /* TODO: support for expired passwords. */
+
+    return PAM_SUCCESS;
+}
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_smbpass_acct_modstruct = {
+     "pam_smbpass",
+     NULL,
+     NULL,
+     pam_sm_acct_mgmt,
+     NULL,
+     NULL,
+     NULL
+};
+#endif
+
diff --git a/source3/pam_smbpass/pam_smb_auth.c b/source3/pam_smbpass/pam_smb_auth.c
new file mode 100644 (file)
index 0000000..0e95a84
--- /dev/null
@@ -0,0 +1,246 @@
+/* Unix NT password database implementation, version 0.7.5.
+ *
+ * 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.
+*/
+
+/* indicate the following groups are defined */
+#define PAM_SM_AUTH
+
+#include "includes.h"
+#include "debug.h"
+
+#ifndef LINUX
+
+/* This is only used in the Sun implementation. */
+#include <security/pam_appl.h>
+
+#endif  /* LINUX */
+
+#include <security/pam_modules.h>
+
+#include "general.h"
+
+#include "support.h"
+
+#define AUTH_RETURN                                            \
+do {                                                           \
+       if(ret_data) {                                          \
+               *ret_data = retval;                             \
+               pam_set_data( pamh, "smb_setcred_return"        \
+                             , (void *) ret_data, NULL );      \
+       }                                                       \
+       return retval;                                          \
+} while (0)
+
+static int _smb_add_user(pam_handle_t *pamh, unsigned int ctrl,
+                         const char *name, struct smb_passwd *smb_pwent);
+
+/*
+ * pam_sm_authenticate() authenticates users against the samba password file.
+ *
+ *     First, obtain the password from the user. Then use a
+ *      routine in 'support.c' to authenticate the user.
+ */
+
+#define _SMB_AUTHTOK  "-SMB-PASS"
+
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+                        int argc, const char **argv)
+{
+    unsigned int ctrl;
+    int retval, *ret_data = NULL;
+
+    const char *name;
+
+    /* Points to memory managed by the PAM library. Do not free. */
+    const char *p = NULL;
+
+    struct smb_passwd *smb_pwent = NULL;
+
+    extern BOOL in_client;
+
+    /* Samba initialization. */
+    setup_logging("pam_smbpass",False);
+    charset_initialise();
+    in_client = True;
+
+    ctrl = set_ctrl(flags, argc, argv);
+
+    /* Get a few bytes so we can pass our return value to
+       pam_sm_setcred(). */
+    ret_data = malloc(sizeof(int));
+
+    /* get the username */
+    retval = pam_get_user( pamh, &name, "Username: " );
+    if ( retval != PAM_SUCCESS ) {
+        if (on( SMB_DEBUG, ctrl )) {
+           _log_err(LOG_DEBUG, "auth: could not identify user");
+        }
+        AUTH_RETURN;
+    }
+    if (on( SMB_DEBUG, ctrl )) {
+        _log_err( LOG_DEBUG, "username [%s] obtained", name );
+    }
+
+    if (!initialize_password_db()) {
+        _log_err( LOG_ALERT, "Cannot access samba password database" );
+        retval = PAM_AUTHINFO_UNAVAIL;
+        AUTH_RETURN;
+    }
+
+    smb_pwent = getsmbpwnam( name );
+
+    if (on( SMB_MIGRATE, ctrl )) {
+       retval = _smb_add_user(pamh, ctrl, name, smb_pwent);
+       AUTH_RETURN;
+    }
+
+    if (smb_pwent == NULL) {
+        _log_err(LOG_ALERT, "Failed to find entry for user %s.", name);
+        retval = PAM_USER_UNKNOWN;
+        AUTH_RETURN;
+    }
+   
+    /* if this user does not have a password... */
+
+    if (_smb_blankpasswd( ctrl, smb_pwent )) {
+        smb_pwent = NULL;
+        retval = PAM_SUCCESS;
+        AUTH_RETURN;
+    }
+
+    /* get this user's authentication token */
+
+    retval = _smb_read_password(pamh, ctrl, NULL, "Password: ", NULL
+                                , _SMB_AUTHTOK, &p);
+    if (retval != PAM_SUCCESS ) {
+       _log_err(LOG_CRIT, "auth: no password provided for [%s]"
+                , name);
+        smb_pwent = NULL;
+        AUTH_RETURN;
+    }
+
+    /* verify the password of this user */
+
+    retval = _smb_verify_password( pamh, smb_pwent, p, ctrl );
+    smb_pwent = NULL;
+    p = NULL;
+    AUTH_RETURN;
+}
+
+/*
+ * This function is for setting samba credentials.  If anyone comes up
+ * with any credentials they think should be set, let me know.
+ */
+
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+                   int argc, const char **argv)
+{
+    int retval, *pretval = NULL;
+
+    retval = PAM_SUCCESS;
+
+    pam_get_data(pamh, "smb_setcred_return", (const void **) &pretval);
+    if(pretval) {
+       retval = *pretval;
+       free(pretval);
+    }
+    pam_set_data(pamh, "smb_setcred_return", NULL, NULL);
+
+    return retval;
+}
+
+
+/* Helper function for adding a user to the db. */
+static int _smb_add_user(pam_handle_t *pamh, unsigned int ctrl,
+                         const char *name, struct smb_passwd *smb_pwent)
+{
+    pstring err_str;
+    pstring msg_str;
+    const char *pass = NULL;
+    int retval;
+
+    err_str[0] = '\0';
+    msg_str[0] = '\0';
+
+    /* Get the authtok; if we don't have one, silently fail. */
+    retval = pam_get_item( pamh, PAM_AUTHTOK, (const void **) &pass );
+
+    if (retval != PAM_SUCCESS) {
+       _log_err( LOG_ALERT
+                 , "pam_get_item returned error to pam_sm_authenticate" );
+       return PAM_AUTHTOK_RECOVER_ERR;
+    } else if (pass == NULL) {
+       return PAM_AUTHTOK_RECOVER_ERR;
+    }
+
+    /* Add the user to the db if they aren't already there. */
+    if (smb_pwent == NULL) {
+       retval = local_password_change( name, LOCAL_ADD_USER,
+                                        pass, err_str,
+                                        sizeof(err_str),
+                                        msg_str, sizeof(msg_str) );
+       if (!retval && *err_str)
+       {
+           err_str[PSTRING_LEN-1] = '\0';
+           make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
+       }
+       else if (*msg_str)
+       {
+           msg_str[PSTRING_LEN-1] = '\0';
+           make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
+       }
+       pass = NULL;
+
+       return PAM_IGNORE;
+    }
+
+    /* Change the user's password IFF it's null. */
+    if (smb_pwent->smb_passwd == NULL && (smb_pwent->acct_ctrl & ACB_PWNOTREQ))
+    {
+       retval = local_password_change( name, 0,
+                                        pass, err_str,
+                                        sizeof(err_str),
+                                        msg_str, sizeof(msg_str) );
+       if (!retval && *err_str)
+       {
+           err_str[PSTRING_LEN-1] = '\0';
+           make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
+       }
+       else if (*msg_str)
+       {
+           msg_str[PSTRING_LEN-1] = '\0';
+           make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
+       }
+    }
+    pass = NULL;
+
+    return PAM_IGNORE;
+}
+
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_smbpass_auth_modstruct = {
+     "pam_smbpass",
+     pam_sm_authenticate,
+     pam_sm_setcred,
+     NULL,
+     NULL,
+     NULL,
+     NULL
+};
+#endif
+
diff --git a/source3/pam_smbpass/pam_smb_passwd.c b/source3/pam_smbpass/pam_smb_passwd.c
new file mode 100644 (file)
index 0000000..e1bb8f3
--- /dev/null
@@ -0,0 +1,314 @@
+/* Unix NT password database implementation, version 0.7.5.
+ *
+ * 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.
+*/
+
+/* indicate the following groups are defined */
+#define PAM_SM_PASSWORD
+
+#include "includes.h"
+
+#ifndef LINUX
+
+/* This is only used in the Sun implementation. */
+#include <security/pam_appl.h>
+
+#endif  /* LINUX */
+
+#include <security/pam_modules.h>
+
+#include "general.h" 
+
+#include "support.h"
+
+int smb_update_db( pam_handle_t *pamh, int ctrl, const char *user
+                   , const char *pass_new )
+{
+ char          c;
+ int           retval, i;
+ pstring       err_str;
+ pstring       msg_str;
+
+    err_str[0] = '\0';
+    msg_str[0] = '\0';
+
+    retval = local_password_change( user, 0, pass_new, err_str, sizeof(err_str),
+                                   msg_str, sizeof(msg_str) );
+
+    if (!retval) {
+        if (*err_str) {
+            err_str[PSTRING_LEN-1] = '\0';
+            make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
+        }
+
+        /* FIXME: what value is appropriate here? */
+        retval = PAM_AUTHTOK_ERR;
+    } else {
+        if (*msg_str) {
+            msg_str[PSTRING_LEN-1] = '\0';
+            make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
+        }
+        retval = PAM_SUCCESS;
+    }
+
+    return retval;      
+
+}
+
+
+/* data tokens */
+
+#define _SMB_OLD_AUTHTOK  "-SMB-OLD-PASS"
+#define _SMB_NEW_AUTHTOK  "-SMB-NEW-PASS"
+
+/*
+ * FUNCTION: pam_sm_chauthtok()
+ *
+ * This function is called twice by the PAM library, once with
+ * PAM_PRELIM_CHECK set, and then again with PAM_UPDATE_AUTHTOK set.  With
+ * Linux-PAM, these two passes generally involve first checking the old
+ * token and then changing the token.  This is what we do here.
+ *
+ * Having obtained a new password. The function updates the
+ * SMB_PASSWD_FILE file (normally, $(LIBDIR)/smbpasswd).
+ */
+
+int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+                     int argc, const char **argv)
+{
+    unsigned int ctrl;
+    int retval;
+
+    extern BOOL in_client;
+
+    struct smb_passwd *smb_pwent=NULL;
+    const char *user;
+    const char *pass_old, *pass_new;
+
+    /* Samba initialization. */
+    setup_logging( "pam_smbpass", False );
+    charset_initialise();
+    in_client = True;
+
+    ctrl = set_ctrl(flags, argc, argv);
+
+    /*
+     * First get the name of a user.  No need to do anything if we can't
+     * determine this.
+     */
+
+    retval = pam_get_user( pamh, &user, "Username: " );
+    if (retval != PAM_SUCCESS) {
+        if (on( SMB_DEBUG, ctrl )) {
+            _log_err( LOG_DEBUG, "password: could not identify user" );
+        }
+        return retval;
+    }
+    if (on( SMB_DEBUG, ctrl )) {
+        _log_err( LOG_DEBUG, "username [%s] obtained", user );
+    }
+
+    if (!initialize_password_db()) {
+        _log_err( LOG_ALERT, "Cannot access samba password database" );
+        return PAM_AUTHINFO_UNAVAIL;
+    }
+
+    /* obtain user record */
+    smb_pwent = getsmbpwnam(user);
+
+    if (smb_pwent == NULL) {
+        _log_err( LOG_ALERT, "Failed to find entry for user %s.", user );
+        return PAM_USER_UNKNOWN;
+    }
+
+    if (flags & PAM_PRELIM_CHECK) {
+        /*
+         * obtain and verify the current password (OLDAUTHTOK) for
+         * the user.
+         */
+
+        char *Announce;
+
+        if (_smb_blankpasswd( ctrl, smb_pwent )) {
+
+            return PAM_SUCCESS;
+
+        }
+
+       /* Password change by root, or for an expired token, doesn't
+           require authentication.  Is this a good choice? */
+        if (getuid() != 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
+
+            /* tell user what is happening */
+#define greeting "Changing password for "
+            Announce = (char *) malloc(sizeof(greeting)+strlen(user));
+            if (Announce == NULL) {
+                _log_err(LOG_CRIT, "password: out of memory");
+                return PAM_BUF_ERR;
+            }
+            strncpy( Announce, greeting, sizeof(greeting) );
+            strncpy( Announce+sizeof(greeting)-1, user, strlen(user)+1 );
+#undef greeting
+
+            set( SMB__OLD_PASSWD, ctrl );
+            retval = _smb_read_password( pamh, ctrl
+                                         , Announce
+                                         , "Current SMB password: "
+                                         , NULL
+                                         , _SMB_OLD_AUTHTOK
+                                         , &pass_old );
+            free( Announce );
+
+            if (retval != PAM_SUCCESS) {
+                _log_err( LOG_NOTICE
+                          , "password - (old) token not obtained" );
+                return retval;
+            }
+
+            /* verify that this is the password for this user */
+
+            retval = _smb_verify_password( pamh, smb_pwent, pass_old, ctrl );
+
+        } else {
+           pass_old = NULL;
+            retval = PAM_SUCCESS;           /* root doesn't have to */
+        }
+
+        pass_old = NULL;
+        return retval;
+
+    } else if (flags & PAM_UPDATE_AUTHTOK) {
+
+        if (flags & PAM_CHANGE_EXPIRED_AUTHTOK) {
+            /* NOTE: there is currently no support for password expiring
+               under Samba. Support will be added here when it becomes
+               available. */
+            return PAM_SUCCESS;
+        }
+        /*
+         * obtain the proposed password
+         */
+
+        /*
+         * get the old token back. NULL was ok only if root [at this
+         * point we assume that this has already been enforced on a
+         * previous call to this function].
+         */
+
+        if (off( SMB_NOT_SET_PASS, ctrl )) {
+            retval = pam_get_item( pamh, PAM_OLDAUTHTOK,
+                                   (const void **)&pass_old );
+        } else {
+            retval = pam_get_data( pamh, _SMB_OLD_AUTHTOK,
+                                   (const void **)&pass_old );
+            if (retval == PAM_NO_MODULE_DATA) {
+               pass_old = NULL;
+                retval = PAM_SUCCESS;
+            }
+        }
+
+        if (retval != PAM_SUCCESS) {
+            _log_err( LOG_NOTICE, "password: user not authenticated" );
+            return retval;
+        }
+
+        /*
+         * use_authtok is to force the use of a previously entered
+         * password -- needed for pluggable password strength checking
+        * or other module stacking
+         */
+
+        if (on( SMB_USE_AUTHTOK, ctrl )) {
+            set( SMB_USE_FIRST_PASS, ctrl );
+        }
+
+        retval = _smb_read_password( pamh, ctrl
+                                      , NULL
+                                      , "Enter new SMB password: "
+                                      , "Retype new SMB password: "
+                                      , _SMB_NEW_AUTHTOK
+                                      , &pass_new );
+
+        if (retval != PAM_SUCCESS) {
+            if (on( SMB_DEBUG, ctrl )) {
+                _log_err( LOG_ALERT
+                          , "password: new password not obtained" );
+            }
+            pass_old = NULL;                               /* tidy up */
+            return retval;
+        }
+
+        /*
+         * At this point we know who the user is and what they
+         * propose as their new password. Verify that the new
+         * password is acceptable.
+         */ 
+
+        if (pass_new[0] == '\0') {     /* "\0" password = NULL */
+            pass_new = NULL;
+        }
+
+        retval = _pam_smb_approve_pass(pamh, ctrl, pass_old, pass_new);
+
+        if (retval != PAM_SUCCESS) {
+            _log_err(LOG_NOTICE, "new password not acceptable");
+            pass_new = pass_old = NULL;               /* tidy up */
+            return retval;
+        }
+
+        /*
+         * By reaching here we have approved the passwords and must now
+         * rebuild the smb password file.
+         */
+
+        /* update the password database */
+
+        retval = smb_update_db(pamh, ctrl, user, pass_new);
+        if (retval == PAM_SUCCESS) {
+            /* password updated */
+            _log_err( LOG_NOTICE, "password for (%s/%d) changed by (%s/%d)"
+                      , user, smb_pwent->smb_userid, uidtoname( getuid() )
+                      , getuid() );
+        } else {
+            _log_err( LOG_ERR, "password change failed for user %s"
+                      , user );
+        }
+
+        pass_old = pass_new = NULL;
+        smb_pwent = NULL;
+
+    } else {            /* something has broken with the library */
+
+        _log_err( LOG_ALERT, "password received unknown request" );
+        retval = PAM_ABORT;
+
+    }
+
+    return retval;
+}
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_smbpass_passwd_modstruct = {
+     "pam_smbpass",
+     NULL,
+     NULL,
+     NULL,
+     NULL,
+     NULL,
+     pam_sm_chauthtok
+};
+#endif
+
diff --git a/source3/pam_smbpass/samples/README b/source3/pam_smbpass/samples/README
new file mode 100644 (file)
index 0000000..d776033
--- /dev/null
@@ -0,0 +1,3 @@
+This directory contains example configurations demonstrating various uses
+of pam_smbpass.  These examples use Linux-style /etc/pam.d syntax, and
+must be modified for use on Solaris systems.
diff --git a/source3/pam_smbpass/samples/kdc-pdc b/source3/pam_smbpass/samples/kdc-pdc
new file mode 100644 (file)
index 0000000..70f1998
--- /dev/null
@@ -0,0 +1,15 @@
+#%PAM-1.0
+# kdc-pdc
+# 
+# A sample PAM configuration that shows pam_smbpass used together with
+# pam_krb5.  This could be useful on a Samba PDC that is also a member of
+# a Kerberos realm.
+
+auth       requisite        pam_nologin.so
+auth       requisite        pam_krb5.so
+auth       optional         pam_smbpass.so migrate
+account    required         pam_krb5.so
+password   requisite        pam_cracklib.so retry=3
+password   optional         pam_smbpass.so nullok use_authtok try_first_pass
+password   required         pam_krb5.so use_authtok try_first_pass
+session    required         pam_krb5.so
diff --git a/source3/pam_smbpass/samples/password-mature b/source3/pam_smbpass/samples/password-mature
new file mode 100644 (file)
index 0000000..6d73e09
--- /dev/null
@@ -0,0 +1,14 @@
+#%PAM-1.0
+# password-mature
+#
+# A sample PAM configuration for a 'mature' smbpasswd installation.
+# private/smbpasswd is fully populated, and we consider it an error if
+# the smbpasswd doesn't exist or doesn't match the Unix password.
+
+auth       requisite        pam_nologin.so
+auth       required         pam_unix.so
+account    required         pam_unix.so
+password   requisite        pam_cracklib.so retry=3
+password   requisite        pam_unix.so shadow md5 use_authtok try_first_pass
+password   required         pam_smbpass.so use_authtok use_first_pass
+session    required         pam_unix.so
diff --git a/source3/pam_smbpass/samples/password-migration b/source3/pam_smbpass/samples/password-migration
new file mode 100644 (file)
index 0000000..305cb53
--- /dev/null
@@ -0,0 +1,18 @@
+#%PAM-1.0
+# password-migration
+#
+# A sample PAM configuration that shows the use of pam_smbpass to migrate
+# from plaintext to encrypted passwords for Samba.  Unlike other methods,
+# this can be used for users who have never connected to Samba shares:
+# password migration takes place when users ftp in, login using ssh, pop
+# their mail, etc.
+
+auth       requisite        pam_nologin.so
+# pam_smbpass is called IFF pam_unix succeeds.
+auth       requisite        pam_unix.so
+auth       optional         pam_smbpass.so migrate
+account    required         pam_unix.so
+password   requisite        pam_cracklib.so retry=3
+password   requisite        pam_unix.so shadow md5 use_authtok try_first_pass
+password   optional         pam_smbpass.so nullok use_authtok try_first_pass
+session    required         pam_unix.so
diff --git a/source3/pam_smbpass/samples/password-sync b/source3/pam_smbpass/samples/password-sync
new file mode 100644 (file)
index 0000000..0a950dd
--- /dev/null
@@ -0,0 +1,15 @@
+#%PAM-1.0
+# password-sync
+#
+# A sample PAM configuration that shows the use of pam_smbpass to make
+# sure private/smbpasswd is kept in sync when /etc/passwd (/etc/shadow)
+# is changed.  Useful when an expired password might be changed by an
+# application (such as ssh).
+
+auth       requisite        pam_nologin.so
+auth       required         pam_unix.so
+account    required         pam_unix.so
+password   requisite        pam_cracklib.so retry=3
+password   requisite        pam_unix.so shadow md5 use_authtok try_first_pass
+password   required         pam_smbpass.so nullok use_authtok try_first_pass
+session    required         pam_unix.so
diff --git a/source3/pam_smbpass/support.c b/source3/pam_smbpass/support.c
new file mode 100644 (file)
index 0000000..01f4aa3
--- /dev/null
@@ -0,0 +1,651 @@
+/* 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 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 "general.h"
+
+#include "support.h"
+
+
+#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 */
+
+void _log_err( int err, const char *format, ... )
+{
+    va_list args;
+
+    va_start( args, format );
+    openlog( "PAM_smbpass", LOG_CONS | LOG_PID, LOG_AUTH );
+    vsyslog( err, format, args );
+    va_end( args );
+    closelog();
+}
+
+/* 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;
+
+       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);
+
+               if (retval != PAM_SUCCESS && on(SMB_DEBUG, ctrl)) {
+                       _log_err(LOG_DEBUG, "conversation failure [%s]"
+                                        ,pam_strerror(pamh, retval));
+               }
+       } else {
+               _log_err(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 = 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. */
+
+int set_ctrl( int flags, int argc, const char **argv )
+{
+    int i = 0;
+    static pstring servicesf = CONFIGFILE;
+    const char *service_file = servicesf;
+    unsigned int ctrl;
+
+    ctrl = SMB_DEFAULTS;       /* the default selection of options */
+
+    /* set some flags manually */
+
+    /* A good, sane default (matches Samba's behavior). */
+    set( SMB__NONULL, ctrl );
+
+    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(service_file,True,False,False) == False) {
+       _log_err( LOG_ERR, "Error loading service file %s", service_file );
+    }
+
+    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( 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 );
+}
+
+/*
+ * Safe duplication of character strings. "Paranoid"; don't leave
+ * evidence of old token around for later stack analysis.
+ */
+
+char * xstrdup( const char *x )
+{
+    register char *new = NULL;
+
+    if (x != NULL) {
+        register int i;
+
+        for (i = 0; x[i]; ++i); /* length of string */
+        if ((new = malloc(++i)) == NULL) {
+            i = 0;
+            _log_err( LOG_CRIT, "out of memory in xstrdup" );
+        } else {
+            while (i-- > 0) {
+                new[i] = x[i];
+            }
+        }
+        x = NULL;
+    }
+    return new;                        /* 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, (const void **) &service );
+                _log_err( 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( 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 */
+       free( failure );
+    }
+}
+
+int _smb_verify_password( pam_handle_t * pamh
+                          , const struct smb_passwd *smb_pwent
+                          , const char *p, unsigned int ctrl )
+{
+    uchar hash_pass[16];
+    uchar lm_pw[16];
+    uchar nt_pw[16];
+    int retval;
+    char *data_name;
+    const char *name;
+
+    if (!smb_pwent)
+        return PAM_ABORT;
+
+    name = smb_pwent->smb_name;
+
+#ifdef HAVE_PAM_FAIL_DELAY
+    if (off( SMB_NODELAY, ctrl )) {
+        (void) pam_fail_delay( pamh, 1000000 );        /* 1 sec delay for on failure */
+    }
+#endif
+
+    if (!smb_pwent->smb_passwd)
+    {
+        _log_err( LOG_DEBUG, "user %s has null SMB password"
+                  , name );
+
+        if (off( SMB__NONULL, ctrl )
+            && (smb_pwent->acct_ctrl & ACB_PWNOTREQ))
+        { /* this means we've succeeded */
+            return PAM_SUCCESS;
+        } else {
+            const char *service;
+
+            pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
+            _log_err( LOG_NOTICE
+                      , "failed auth request by %s for service %s as %s(%d)"
+                      , uidtoname( getuid() )
+                      , service ? service : "**unknown**", name
+                      , smb_pwent->smb_userid );
+            return PAM_AUTH_ERR;
+        }
+    }
+
+    data_name = (char *) malloc( sizeof(FAIL_PREFIX) 
+                                 + strlen( name ));
+    if (data_name == NULL) {
+        _log_err( LOG_CRIT, "no memory for data-name" );
+    }
+    strncpy( data_name, FAIL_PREFIX, sizeof(FAIL_PREFIX) );
+    strncpy( data_name + sizeof(FAIL_PREFIX) - 1, name, strlen( name ) + 1 );
+
+    /* First we check whether we've been given the password in already
+       encrypted form. */
+    if (strlen( p ) == 16 || (strlen( p ) == 32
+         && pdb_gethexpwd( p, (char *) hash_pass ))) {
+
+        if (!memcmp( hash_pass, smb_pwent->smb_passwd, 16 )
+            || (smb_pwent->smb_nt_passwd
+                && !memcmp( hash_pass, smb_pwent->smb_nt_passwd, 16 )))
+        {
+            retval = PAM_SUCCESS;
+            if (data_name) {   /* reset failures */
+                pam_set_data( pamh, data_name, NULL, _cleanup_failures );
+            }
+            _pam_delete( data_name );
+            memset( hash_pass, '\0', 16 );
+            smb_pwent = NULL;
+            return retval;
+        }
+    }
+
+    /*
+     * 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, smb_pwent->smb_nt_passwd, 16 )) {
+
+        retval = PAM_SUCCESS;
+        if (data_name) {               /* reset failures */
+            pam_set_data(pamh, data_name, NULL, _cleanup_failures);
+        }
+    } else {
+
+        const char *service;
+
+        pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
+
+        if (data_name != NULL) {
+            struct _pam_failed_auth *new = NULL;
+            const struct _pam_failed_auth *old = NULL;
+
+            /* get a failure recorder */
+
+            new = (struct _pam_failed_auth *)
+                      malloc( sizeof(struct _pam_failed_auth) );
+
+            if (new != NULL) {
+
+                /* any previous failures for this user ? */
+                pam_get_data(pamh, data_name, (const void **) &old);
+
+                if (old != NULL) {
+                    new->count = old->count + 1;
+                    if (new->count >= SMB_MAX_RETRIES) {
+                        retval = PAM_MAXTRIES;
+                    }
+                } else {
+                    _log_err( LOG_NOTICE
+                      , "failed auth request by %s for service %s as %s(%d)"
+                      , uidtoname( getuid() )
+                      , service ? service : "**unknown**", name
+                      , smb_pwent->smb_userid );
+                    new->count = 1;
+                }
+                new->user = xstrdup( name );
+                new->id = smb_pwent->smb_userid;
+                new->agent = xstrdup( uidtoname( getuid() ) );
+                pam_set_data( pamh, data_name, new, _cleanup_failures );
+
+            } else {
+                _log_err( LOG_CRIT, "no memory for failure recorder" );
+                _log_err( LOG_NOTICE
+                      , "failed auth request by %s for service %s as %s(%d)"
+                      , uidtoname( getuid() )
+                      , service ? service : "**unknown**", name
+                      , smb_pwent->smb_userid );
+            }
+        } else {
+            _log_err( LOG_NOTICE
+                      , "failed auth request by %s for service %s as %s(%d)"
+                      , uidtoname( getuid() )
+                      , service ? service : "**unknown**", name
+                      , smb_pwent->smb_userid );
+            retval = PAM_AUTH_ERR;
+        }
+    }
+
+    _pam_delete( data_name );
+    smb_pwent = NULL;
+    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, const struct smb_passwd *smb_pwent )
+{
+       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 (smb_pwent->smb_passwd == 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
+                        , const char **pass )
+{
+    int authtok_flag;
+    int retval;
+    const 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, (const void **) &item );
+        if (retval != PAM_SUCCESS) {
+            /* very strange. */
+            _log_err( 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 = comment;
+        i = 1;
+    } else {
+        i = 0;
+    }
+
+    pmsg[i] = &msg[i];
+    msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+    msg[i++].msg = prompt1;
+
+    if (prompt2 != NULL) {
+        pmsg[i] = &msg[i];
+        msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+        msg[i++].msg = 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 = xstrdup(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(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( 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
+                            ,(const void **)&item )) != PAM_SUCCESS)
+        {
+            _log_err( 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, (const void **)&item ))
+                             != PAM_SUCCESS)
+        {
+            _log_err( 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( 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;
+}
diff --git a/source3/pam_smbpass/support.h b/source3/pam_smbpass/support.h
new file mode 100644 (file)
index 0000000..85bbd0a
--- /dev/null
@@ -0,0 +1,52 @@
+/* syslogging function for errors and other information */
+extern void _log_err(int, const char *, ...);
+
+/* set the control flags for the UNIX module. */
+extern int set_ctrl(int, int, const char **);
+
+/* generic function for freeing pam data segments */
+extern void _cleanup(pam_handle_t *, void *, int);
+
+/*
+ * Safe duplication of character strings. "Paranoid"; don't leave
+ * evidence of old token around for later stack analysis.
+ */
+
+extern char *xstrdup(const char *);
+
+/* ************************************************************** *
+ * Useful non-trivial functions                                   *
+ * ************************************************************** */
+
+extern void _cleanup_failures(pam_handle_t *, void *, int);
+
+/* compare 2 strings */
+extern BOOL strequal(const char *, const char *);
+
+extern struct smb_passwd *
+_my_get_smbpwnam(FILE *, const char *, BOOL *, BOOL *, long *);
+
+extern int _smb_verify_password( pam_handle_t *pamh
+                                 , const struct smb_passwd *smb_pwent
+                                , const char *p, unsigned int ctrl );
+
+/*
+ * this function obtains the name of the current user and ensures
+ * that the PAM_USER item is set to this value
+ */
+
+extern int _smb_get_user(pam_handle_t *, unsigned int,
+                        const char *, const char **);
+
+/* _smb_blankpasswd() is a quick check for a blank password */
+
+extern int _smb_blankpasswd(unsigned int, const struct smb_passwd *);
+
+
+/* obtain a password from the user */
+extern int _smb_read_password( pam_handle_t *, unsigned int, const char*,
+                               const char *, const char *, const char *,
+                               const char **);
+
+extern int _pam_smb_approve_pass(pam_handle_t *, unsigned int, const char *,
+                                const char *);