1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-10 01:18:15 +03:00

Added Steve Langasek <vorlon@netexpress.net> pam_smbpass PAM module code.

Note: Still have to add build stuff - not ready yet.
This commit is contained in:
John Terpstra 0001-01-01 00:00:00 +00:00
parent ff4cdb5a34
commit 1de7022f98
14 changed files with 1668 additions and 0 deletions

View File

@ -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.

66
source/pam_smbpass/README Normal file
View File

@ -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>

7
source/pam_smbpass/TODO Normal file
View File

@ -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)
-

View File

@ -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 */
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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 *);