1
/* Unix NT password database implementation, version 0.6.
3
* This program is free software; you can redistribute it and/or modify it under
4
* the terms of the GNU General Public License as published by the Free
5
* Software Foundation; either version 2 of the License, or (at your option)
8
* This program is distributed in the hope that it will be useful, but WITHOUT
9
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13
* You should have received a copy of the GNU General Public License along with
14
* this program; if not, write to the Free Software Foundation, Inc., 675
15
* Mass Ave, Cambridge, MA 02139, USA.
24
#define _pam_overwrite(x) \
26
register char *__xx__; \
33
* Don't just free it, forget it too.
36
#define _pam_drop(X) \
44
#define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
48
for (reply_i=0; reply_i<replies; ++reply_i) { \
49
if (reply[reply_i].resp) { \
50
_pam_overwrite(reply[reply_i].resp); \
51
free(reply[reply_i].resp); \
59
int converse(pam_handle_t *, int, int, struct pam_message **,
60
struct pam_response **);
61
int make_remark(pam_handle_t *, unsigned int, int, const char *);
62
void _cleanup(pam_handle_t *, void *, int);
63
char *_pam_delete(register char *);
65
/* default configuration file location */
67
char *servicesf = dyn_CONFIGFILE;
69
/* syslogging function for errors and other information */
71
void _log_err( int err, const char *format, ... )
75
va_start( args, format );
76
openlog( "PAM_smbpass", LOG_CONS | LOG_PID, LOG_AUTH );
77
vsyslog( err, format, args );
82
/* this is a front-end for module-application conversations */
84
int converse( pam_handle_t * pamh, int ctrl, int nargs
85
, struct pam_message **message
86
, struct pam_response **response )
89
struct pam_conv *conv;
91
retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
92
if (retval == PAM_SUCCESS) {
94
retval = conv->conv(nargs, (const struct pam_message **) message
95
,response, conv->appdata_ptr);
97
if (retval != PAM_SUCCESS && on(SMB_DEBUG, ctrl)) {
98
_log_err(LOG_DEBUG, "conversation failure [%s]"
99
,pam_strerror(pamh, retval));
102
_log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
103
,pam_strerror(pamh, retval));
106
return retval; /* propagate error status */
109
int make_remark( pam_handle_t * pamh, unsigned int ctrl
110
, int type, const char *text )
112
if (off(SMB__QUIET, ctrl)) {
113
struct pam_message *pmsg[1], msg[1];
114
struct pam_response *resp;
117
msg[0].msg = CONST_DISCARD(char *, text);
118
msg[0].msg_style = type;
121
return converse(pamh, ctrl, 1, pmsg, &resp);
127
/* set the control flags for the SMB module. */
129
int set_ctrl( int flags, int argc, const char **argv )
132
const char *service_file = dyn_CONFIGFILE;
135
ctrl = SMB_DEFAULTS; /* the default selection of options */
137
/* set some flags manually */
139
/* A good, sane default (matches Samba's behavior). */
140
set( SMB__NONULL, ctrl );
142
/* initialize service file location */
143
service_file=servicesf;
145
if (flags & PAM_SILENT) {
146
set( SMB__QUIET, ctrl );
149
/* Run through the arguments once, looking for an alternate smb config
154
for (j = 0; j < SMB_CTRLS_; ++j) {
155
if (smb_args[j].token
156
&& !strncmp(argv[i], smb_args[j].token, strlen(smb_args[j].token)))
162
if (j == SMB_CONF_FILE) {
163
service_file = argv[i] + 8;
168
/* Read some options from the Samba config. Can be overridden by
170
if(lp_load(service_file,True,False,False,True) == False) {
171
_log_err( LOG_ERR, "Error loading service file %s", service_file );
176
if (lp_null_passwords()) {
177
set( SMB__NULLOK, ctrl );
180
/* now parse the rest of the arguments to this module */
185
for (j = 0; j < SMB_CTRLS_; ++j) {
186
if (smb_args[j].token
187
&& !strncmp(*argv, smb_args[j].token, strlen(smb_args[j].token)))
193
if (j >= SMB_CTRLS_) {
194
_log_err( LOG_ERR, "unrecognized option [%s]", *argv );
196
ctrl &= smb_args[j].mask; /* for turning things off */
197
ctrl |= smb_args[j].flag; /* for turning things on */
200
++argv; /* step to next argument */
203
/* auditing is a more sensitive version of debug */
205
if (on( SMB_AUDIT, ctrl )) {
206
set( SMB_DEBUG, ctrl );
208
/* return the set of flags */
213
/* use this to free strings. ESPECIALLY password strings */
215
char * _pam_delete( register char *xx )
217
_pam_overwrite( xx );
222
void _cleanup( pam_handle_t * pamh, void *x, int error_status )
224
x = _pam_delete( (char *) x );
229
* Safe duplication of character strings. "Paranoid"; don't leave
230
* evidence of old token around for later stack analysis.
233
char * smbpXstrDup( const char *x )
235
register char *newstr = NULL;
240
for (i = 0; x[i]; ++i); /* length of string */
241
if ((newstr = SMB_MALLOC_ARRAY(char, ++i)) == NULL) {
243
_log_err( LOG_CRIT, "out of memory in smbpXstrDup" );
251
return newstr; /* return the duplicate or NULL on error */
254
/* ************************************************************** *
255
* Useful non-trivial functions *
256
* ************************************************************** */
258
void _cleanup_failures( pam_handle_t * pamh, void *fl, int err )
261
const char *service = NULL;
262
struct _pam_failed_auth *failure;
264
#ifdef PAM_DATA_SILENT
265
quiet = err & PAM_DATA_SILENT; /* should we log something? */
269
#ifdef PAM_DATA_REPLACE
270
err &= PAM_DATA_REPLACE; /* are we just replacing data? */
272
failure = (struct _pam_failed_auth *) fl;
274
if (failure != NULL) {
276
#ifdef PAM_DATA_SILENT
277
if (!quiet && !err) { /* under advisement from Sun,may go away */
279
if (!quiet) { /* under advisement from Sun,may go away */
282
/* log the number of authentication failures */
283
if (failure->count != 0) {
284
pam_get_item( pamh, PAM_SERVICE, (const void **) &service );
286
, "%d authentication %s "
287
"from %s for service %s as %s(%d)"
289
, failure->count == 1 ? "failure" : "failures"
291
, service == NULL ? "**unknown**" : service
292
, failure->user, failure->id );
293
if (failure->count > SMB_MAX_RETRIES) {
295
, "service(%s) ignoring max retries; %d > %d"
296
, service == NULL ? "**unknown**" : service
302
_pam_delete( failure->agent ); /* tidy up */
303
_pam_delete( failure->user ); /* tidy up */
304
SAFE_FREE( failure );
308
int _smb_verify_password( pam_handle_t * pamh, struct samu *sampass,
309
const char *p, unsigned int ctrl )
313
int retval = PAM_AUTH_ERR;
320
name = pdb_get_username(sampass);
322
#ifdef HAVE_PAM_FAIL_DELAY
323
if (off( SMB_NODELAY, ctrl )) {
324
(void) pam_fail_delay( pamh, 1000000 ); /* 1 sec delay for on failure */
328
if (!pdb_get_lanman_passwd(sampass))
330
_log_err( LOG_DEBUG, "user %s has null SMB password"
333
if (off( SMB__NONULL, ctrl )
334
&& (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ))
335
{ /* this means we've succeeded */
340
pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
341
_log_err( LOG_NOTICE, "failed auth request by %s for service %s as %s",
342
uidtoname(getuid()), service ? service : "**unknown**", name);
347
data_name = SMB_MALLOC_ARRAY(char, sizeof(FAIL_PREFIX) + strlen( name ));
348
if (data_name == NULL) {
349
_log_err( LOG_CRIT, "no memory for data-name" );
351
strncpy( data_name, FAIL_PREFIX, sizeof(FAIL_PREFIX) );
352
strncpy( data_name + sizeof(FAIL_PREFIX) - 1, name, strlen( name ) + 1 );
355
* The password we were given wasn't an encrypted password, or it
356
* didn't match the one we have. We encrypt the password now and try
360
nt_lm_owf_gen(p, nt_pw, lm_pw);
362
/* the moment of truth -- do we agree with the password? */
364
if (!memcmp( nt_pw, pdb_get_nt_passwd(sampass), 16 )) {
366
retval = PAM_SUCCESS;
367
if (data_name) { /* reset failures */
368
pam_set_data(pamh, data_name, NULL, _cleanup_failures);
374
pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
376
if (data_name != NULL) {
377
struct _pam_failed_auth *newauth = NULL;
378
const struct _pam_failed_auth *old = NULL;
380
/* get a failure recorder */
382
newauth = SMB_MALLOC_P( struct _pam_failed_auth );
384
if (newauth != NULL) {
386
/* any previous failures for this user ? */
387
pam_get_data(pamh, data_name, (const void **) &old);
390
newauth->count = old->count + 1;
391
if (newauth->count >= SMB_MAX_RETRIES) {
392
retval = PAM_MAXTRIES;
396
"failed auth request by %s for service %s as %s",
398
service ? service : "**unknown**", name);
401
if (!sid_to_uid(pdb_get_user_sid(sampass), &(newauth->id))) {
403
"failed auth request by %s for service %s as %s",
405
service ? service : "**unknown**", name);
407
newauth->user = smbpXstrDup( name );
408
newauth->agent = smbpXstrDup( uidtoname( getuid() ) );
409
pam_set_data( pamh, data_name, newauth, _cleanup_failures );
412
_log_err( LOG_CRIT, "no memory for failure recorder" );
414
"failed auth request by %s for service %s as %s(%d)",
416
service ? service : "**unknown**", name);
420
"failed auth request by %s for service %s as %s(%d)",
422
service ? service : "**unknown**", name);
423
retval = PAM_AUTH_ERR;
427
_pam_delete( data_name );
434
* _smb_blankpasswd() is a quick check for a blank password
436
* returns TRUE if user does not have a password
437
* - to avoid prompting for one in such cases (CG)
440
int _smb_blankpasswd( unsigned int ctrl, struct samu *sampass )
445
* This function does not have to be too smart if something goes
446
* wrong, return FALSE and let this case to be treated somewhere
450
if (on( SMB__NONULL, ctrl ))
451
return 0; /* will fail but don't let on yet */
453
if (pdb_get_lanman_passwd(sampass) == NULL)
462
* obtain a password from the user
465
int _smb_read_password( pam_handle_t * pamh, unsigned int ctrl,
466
const char *comment, const char *prompt1,
467
const char *prompt2, const char *data_name, char **pass )
474
struct pam_message msg[3], *pmsg[3];
475
struct pam_response *resp;
479
/* make sure nothing inappropriate gets returned */
481
*pass = token = NULL;
483
/* which authentication token are we getting? */
485
authtok_flag = on(SMB__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
487
/* should we obtain the password from a PAM item ? */
489
if (on(SMB_TRY_FIRST_PASS, ctrl) || on(SMB_USE_FIRST_PASS, ctrl)) {
490
retval = pam_get_item( pamh, authtok_flag, (const void **) &item );
491
if (retval != PAM_SUCCESS) {
494
, "pam_get_item returned error to smb_read_password" );
496
} else if (item != NULL) { /* we have a password! */
500
} else if (on( SMB_USE_FIRST_PASS, ctrl )) {
501
return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
502
} else if (on( SMB_USE_AUTHTOK, ctrl )
503
&& off( SMB__OLD_PASSWD, ctrl ))
505
return PAM_AUTHTOK_RECOVER_ERR;
510
* getting here implies we will have to get the password from the
514
/* prepare to converse */
515
if (comment != NULL && off(SMB__QUIET, ctrl)) {
517
msg[0].msg_style = PAM_TEXT_INFO;
518
msg[0].msg = CONST_DISCARD(char *, comment);
525
msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
526
msg[i++].msg = CONST_DISCARD(char *, prompt1);
528
if (prompt2 != NULL) {
530
msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
531
msg[i++].msg = CONST_DISCARD(char *, prompt2);
538
retval = converse( pamh, ctrl, i, pmsg, &resp );
541
int j = comment ? 1 : 0;
542
/* interpret the response */
544
if (retval == PAM_SUCCESS) { /* a good conversation */
546
token = smbpXstrDup(resp[j++].resp);
549
/* verify that password entered correctly */
550
if (!resp[j].resp || strcmp( token, resp[j].resp )) {
551
_pam_delete( token );
552
retval = PAM_AUTHTOK_RECOVER_ERR;
553
make_remark( pamh, ctrl, PAM_ERROR_MSG
558
_log_err(LOG_NOTICE, "could not recover authentication token");
563
_pam_drop_reply( resp, expect );
566
retval = (retval == PAM_SUCCESS) ? PAM_AUTHTOK_RECOVER_ERR : retval;
569
if (retval != PAM_SUCCESS) {
570
if (on( SMB_DEBUG, ctrl ))
571
_log_err( LOG_DEBUG, "unable to obtain a password" );
574
/* 'token' is the entered password */
576
if (off( SMB_NOT_SET_PASS, ctrl )) {
578
/* we store this password as an item */
580
retval = pam_set_item( pamh, authtok_flag, (const void *)token );
581
_pam_delete( token ); /* clean it up */
582
if (retval != PAM_SUCCESS
583
|| (retval = pam_get_item( pamh, authtok_flag
584
,(const void **)&item )) != PAM_SUCCESS)
586
_log_err( LOG_CRIT, "error manipulating password" );
591
* then store it as data specific to this module. pam_end()
592
* will arrange to clean it up.
595
retval = pam_set_data( pamh, data_name, (void *) token, _cleanup );
596
if (retval != PAM_SUCCESS
597
|| (retval = pam_get_data( pamh, data_name, (const void **)&item ))
600
_log_err( LOG_CRIT, "error manipulating password data [%s]"
601
, pam_strerror( pamh, retval ));
602
_pam_delete( token );
606
token = NULL; /* break link to password */
610
item = NULL; /* break link to password */
615
int _pam_smb_approve_pass(pam_handle_t * pamh,
617
const char *pass_old,
618
const char *pass_new )
621
/* Further checks should be handled through module stacking. -SRL */
622
if (pass_new == NULL || (pass_old && !strcmp( pass_old, pass_new )))
624
if (on(SMB_DEBUG, ctrl)) {
626
"passwd: bad authentication token (null or unchanged)" );
628
make_remark( pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
629
"No password supplied" : "Password unchanged" );
630
return PAM_AUTHTOK_ERR;