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:
parent
ff4cdb5a34
commit
1de7022f98
31
source/pam_smbpass/CHANGELOG
Normal file
31
source/pam_smbpass/CHANGELOG
Normal 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
66
source/pam_smbpass/README
Normal 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
7
source/pam_smbpass/TODO
Normal 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)
|
||||
-
|
123
source/pam_smbpass/general.h
Normal file
123
source/pam_smbpass/general.h
Normal 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 */
|
||||
};
|
113
source/pam_smbpass/pam_smb_acct.c
Normal file
113
source/pam_smbpass/pam_smb_acct.c
Normal 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
|
||||
|
246
source/pam_smbpass/pam_smb_auth.c
Normal file
246
source/pam_smbpass/pam_smb_auth.c
Normal 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
|
||||
|
314
source/pam_smbpass/pam_smb_passwd.c
Normal file
314
source/pam_smbpass/pam_smb_passwd.c
Normal 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
|
||||
|
3
source/pam_smbpass/samples/README
Normal file
3
source/pam_smbpass/samples/README
Normal 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.
|
15
source/pam_smbpass/samples/kdc-pdc
Normal file
15
source/pam_smbpass/samples/kdc-pdc
Normal 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
|
14
source/pam_smbpass/samples/password-mature
Normal file
14
source/pam_smbpass/samples/password-mature
Normal 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
|
18
source/pam_smbpass/samples/password-migration
Normal file
18
source/pam_smbpass/samples/password-migration
Normal 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
|
15
source/pam_smbpass/samples/password-sync
Normal file
15
source/pam_smbpass/samples/password-sync
Normal 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
|
651
source/pam_smbpass/support.c
Normal file
651
source/pam_smbpass/support.c
Normal 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;
|
||||
}
|
52
source/pam_smbpass/support.h
Normal file
52
source/pam_smbpass/support.h
Normal 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 *);
|
Loading…
Reference in New Issue
Block a user