Coverage Report

Created: 2025-07-23 07:04

/src/samba/source3/auth/pampass.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   Unix SMB/CIFS implementation.
3
   PAM Password checking
4
   Copyright (C) Andrew Tridgell 1992-2001
5
   Copyright (C) John H Terpsta 1999-2001
6
   Copyright (C) Andrew Bartlett 2001
7
   Copyright (C) Jeremy Allison 2001
8
9
   This program is free software; you can redistribute it and/or modify
10
   it under the terms of the GNU General Public License as published by
11
   the Free Software Foundation; either version 3 of the License, or
12
   (at your option) any later version.
13
14
   This program is distributed in the hope that it will be useful,
15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
16
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
   GNU General Public License for more details.
18
19
   You should have received a copy of the GNU General Public License
20
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
*/
22
23
/*
24
 * This module provides PAM based functions for validation of
25
 * username/password pairs, account management, session and access control.
26
 * Note: SMB password checking is done in smbpass.c
27
 */
28
29
#include "includes.h"
30
#include "auth.h"
31
#include "../libcli/auth/pam_errors.h"
32
#include "lib/util/string_wrappers.h"
33
34
#undef DBGC_CLASS
35
0
#define DBGC_CLASS DBGC_AUTH
36
37
#ifdef WITH_PAM
38
39
/*******************************************************************
40
 * Handle PAM authentication
41
 *  - Access, Authentication, Session, Password
42
 *   Note: See PAM Documentation and refer to local system PAM implementation
43
 *   which determines what actions/limitations/allowances become affected.
44
 *********************************************************************/
45
46
#if defined(HAVE_SECURITY_PAM_APPL_H)
47
#include <security/pam_appl.h>
48
#elif defined(HAVE_PAM_PAM_APPL_H)
49
#include <pam/pam_appl.h>
50
#endif
51
52
/*
53
 * Structure used to communicate between the conversation function
54
 * and the server_login/change password functions.
55
 */
56
57
struct smb_pam_userdata {
58
  const char *PAM_username;
59
  const char *PAM_password;
60
  const char *PAM_newpassword;
61
};
62
63
typedef int (*smb_pam_conv_fn)(int, const struct pam_message **, struct pam_response **, void *appdata_ptr);
64
65
static char *smb_pam_copy_string(const char *s)
66
0
{
67
0
  if (s == NULL) {
68
0
    return NULL;
69
0
  }
70
0
  return SMB_STRDUP(s);
71
0
}
72
73
static char *smb_pam_copy_fstring(const char *s)
74
0
{
75
0
  if (s[0] == '\0') {
76
0
    return NULL;
77
0
  }
78
0
  return SMB_STRDUP(s);
79
0
}
80
81
/*******************************************************************
82
 PAM error handler.
83
 *********************************************************************/
84
85
static bool smb_pam_error_handler(pam_handle_t *pamh, int pam_error, const char *msg, int dbglvl)
86
0
{
87
88
0
  if( pam_error != PAM_SUCCESS) {
89
0
    DEBUG(dbglvl, ("smb_pam_error_handler: PAM: %s : %s\n",
90
0
        msg, pam_strerror(pamh, pam_error)));
91
0
    return False;
92
0
  }
93
0
  return True;
94
0
}
95
96
/*******************************************************************
97
 This function is a sanity check, to make sure that we NEVER report
98
 failure as success.
99
*********************************************************************/
100
101
static bool smb_pam_nt_status_error_handler(pam_handle_t *pamh, int pam_error,
102
              const char *msg, int dbglvl,
103
              NTSTATUS *nt_status)
104
0
{
105
0
  *nt_status = pam_to_nt_status(pam_error);
106
107
0
  if (smb_pam_error_handler(pamh, pam_error, msg, dbglvl))
108
0
    return True;
109
110
0
  if (NT_STATUS_IS_OK(*nt_status)) {
111
    /* Complain LOUDLY */
112
0
    DEBUG(0, ("smb_pam_nt_status_error_handler: PAM: BUG: PAM and NT_STATUS \
113
0
error MISMATCH, forcing to NT_STATUS_LOGON_FAILURE\n"));
114
0
    *nt_status = NT_STATUS_LOGON_FAILURE;
115
0
  }
116
0
  return False;
117
0
}
118
119
/*
120
 * PAM conversation function
121
 * Here we assume (for now, at least) that echo on means login name, and
122
 * echo off means password.
123
 */
124
125
static int smb_pam_conv(int num_msg,
126
        const struct pam_message **msg,
127
        struct pam_response **resp,
128
        void *appdata_ptr)
129
0
{
130
0
  int replies = 0;
131
0
  struct pam_response *reply = NULL;
132
0
  struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr;
133
134
0
  if (resp != NULL) {
135
0
    *resp = NULL;
136
0
  }
137
138
0
  if (num_msg <= 0)
139
0
    return PAM_CONV_ERR;
140
141
  /*
142
   * Apparently HPUX has a buggy PAM that doesn't support the
143
   * appdata_ptr. Fail if this is the case. JRA.
144
   */
145
146
0
  if (udp == NULL) {
147
0
    DEBUG(0,("smb_pam_conv: PAM on this system is broken - appdata_ptr == NULL !\n"));
148
0
    return PAM_CONV_ERR;
149
0
  }
150
151
0
  reply = SMB_MALLOC_ARRAY(struct pam_response, num_msg);
152
0
  if (!reply)
153
0
    return PAM_CONV_ERR;
154
155
0
  memset(reply, '\0', sizeof(struct pam_response) * num_msg);
156
157
0
  for (replies = 0; replies < num_msg; replies++) {
158
0
    switch (msg[replies]->msg_style) {
159
0
      case PAM_PROMPT_ECHO_ON:
160
0
        reply[replies].resp_retcode = PAM_SUCCESS;
161
0
        reply[replies].resp = smb_pam_copy_string(
162
0
          udp->PAM_username);
163
        /* PAM frees resp */
164
0
        break;
165
166
0
      case PAM_PROMPT_ECHO_OFF:
167
0
        reply[replies].resp_retcode = PAM_SUCCESS;
168
0
        reply[replies].resp = smb_pam_copy_string(
169
0
          udp->PAM_password);
170
        /* PAM frees resp */
171
0
        break;
172
173
0
      case PAM_TEXT_INFO:
174
0
        FALL_THROUGH;
175
176
0
      case PAM_ERROR_MSG:
177
        /* ignore it... */
178
0
        reply[replies].resp_retcode = PAM_SUCCESS;
179
0
        reply[replies].resp = NULL;
180
0
        break;
181
182
0
      default:
183
        /* Must be an error of some sort... */
184
0
        SAFE_FREE(reply);
185
0
        return PAM_CONV_ERR;
186
0
    }
187
0
  }
188
0
  if (reply != NULL) {
189
0
    if (resp != NULL) {
190
0
      *resp = reply;
191
0
    } else {
192
0
      SAFE_FREE(reply);
193
0
    }
194
0
  }
195
0
  return PAM_SUCCESS;
196
0
}
197
198
/*
199
 * PAM password change conversation function
200
 * Here we assume (for now, at least) that echo on means login name, and
201
 * echo off means password.
202
 */
203
204
static void special_char_sub(char *buf)
205
0
{
206
0
  all_string_sub(buf, "\\n", "", 0);
207
0
  all_string_sub(buf, "\\r", "", 0);
208
0
  all_string_sub(buf, "\\s", " ", 0);
209
0
  all_string_sub(buf, "\\t", "\t", 0);
210
0
}
211
212
static void pwd_sub(char *buf, const char *username, const char *oldpass, const char *newpass)
213
0
{
214
0
  string_sub(buf, "%u", username, sizeof(fstring));
215
0
  all_string_sub(buf, "%o", oldpass, sizeof(fstring));
216
0
  all_string_sub(buf, "%n", newpass, sizeof(fstring));
217
0
}
218
219
220
struct chat_struct {
221
  struct chat_struct *next, *prev;
222
  fstring prompt;
223
  fstring reply;
224
};
225
226
static void free_pw_chat(struct chat_struct *list);
227
228
/**************************************************************
229
 Create a linked list containing chat data.
230
***************************************************************/
231
232
static struct chat_struct *make_pw_chat(const char *p)
233
0
{
234
0
  char *prompt;
235
0
  char *reply;
236
0
  struct chat_struct *list = NULL;
237
0
  struct chat_struct *t;
238
0
  TALLOC_CTX *frame = talloc_stackframe();
239
240
0
  while (1) {
241
0
    t = SMB_CALLOC_ARRAY(struct chat_struct, 1);
242
0
    if (!t) {
243
0
      DEBUG(0,("make_pw_chat: malloc failed!\n"));
244
0
      free_pw_chat(list);
245
0
      TALLOC_FREE(frame);
246
0
      return NULL;
247
0
    }
248
249
0
    DLIST_ADD_END(list, t);
250
251
0
    if (!next_token_talloc(frame, &p, &prompt, NULL)) {
252
0
      break;
253
0
    }
254
255
0
    if (strequal(prompt,".")) {
256
0
      fstrcpy(prompt,"*");
257
0
    }
258
259
0
    special_char_sub(prompt);
260
0
    fstrcpy(t->prompt, prompt);
261
0
    (void)strlower_m(t->prompt);
262
0
    trim_char(t->prompt, ' ', ' ');
263
264
0
    if (!next_token_talloc(frame, &p, &reply, NULL)) {
265
0
      break;
266
0
    }
267
268
0
    if (strequal(reply,".")) {
269
0
      fstrcpy(reply,"");
270
0
    }
271
272
0
    special_char_sub(reply);
273
0
    fstrcpy(t->reply, reply);
274
0
    (void)strlower_m(t->reply);
275
0
    trim_char(t->reply, ' ', ' ');
276
277
0
  }
278
0
  TALLOC_FREE(frame);
279
0
  return list;
280
0
}
281
282
static void free_pw_chat(struct chat_struct *list)
283
0
{
284
0
    while (list) {
285
0
        struct chat_struct *old_head = list;
286
0
        DLIST_REMOVE(list, list);
287
0
        SAFE_FREE(old_head);
288
0
    }
289
0
}
290
291
static int smb_pam_passchange_conv(int num_msg,
292
        const struct pam_message **msg,
293
        struct pam_response **resp,
294
        void *appdata_ptr)
295
0
{
296
0
  int replies = 0;
297
0
  struct pam_response *reply = NULL;
298
0
  fstring current_prompt;
299
0
  fstring current_reply;
300
0
  struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr;
301
0
  struct chat_struct *pw_chat;
302
0
  struct chat_struct *t;
303
0
  const struct loadparm_substitution *lp_sub =
304
0
    loadparm_s3_global_substitution();
305
0
  bool found;
306
0
  *resp = NULL;
307
308
0
  DEBUG(10,("smb_pam_passchange_conv: starting conversation for %d messages\n", num_msg));
309
310
0
  if (num_msg <= 0)
311
0
    return PAM_CONV_ERR;
312
313
0
  if ((pw_chat = make_pw_chat(lp_passwd_chat(talloc_tos(), lp_sub))) == NULL)
314
0
    return PAM_CONV_ERR;
315
316
  /*
317
   * Apparently HPUX has a buggy PAM that doesn't support the
318
   * appdata_ptr. Fail if this is the case. JRA.
319
   */
320
321
0
  if (udp == NULL) {
322
0
    DEBUG(0,("smb_pam_passchange_conv: PAM on this system is broken - appdata_ptr == NULL !\n"));
323
0
    free_pw_chat(pw_chat);
324
0
    return PAM_CONV_ERR;
325
0
  }
326
327
0
  reply = SMB_MALLOC_ARRAY(struct pam_response, num_msg);
328
0
  if (!reply) {
329
0
    DEBUG(0,("smb_pam_passchange_conv: malloc for reply failed!\n"));
330
0
    free_pw_chat(pw_chat);
331
0
    return PAM_CONV_ERR;
332
0
  }
333
334
0
  for (replies = 0; replies < num_msg; replies++) {
335
0
    found = False;
336
0
    DEBUG(10,("smb_pam_passchange_conv: Processing message %d\n", replies));
337
0
    switch (msg[replies]->msg_style) {
338
0
    case PAM_PROMPT_ECHO_ON:
339
0
      DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: PAM said: %s\n", msg[replies]->msg));
340
0
      fstrcpy(current_prompt, msg[replies]->msg);
341
0
      trim_char(current_prompt, ' ', ' ');
342
0
      for (t=pw_chat; t; t=t->next) {
343
344
0
        DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: trying to match |%s| to |%s|\n",
345
0
            t->prompt, current_prompt ));
346
347
0
        if (unix_wild_match(t->prompt, current_prompt)) {
348
0
          fstrcpy(current_reply, t->reply);
349
0
          DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We sent: %s\n", current_reply));
350
0
          pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword);
351
0
#ifdef DEBUG_PASSWORD
352
0
          DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We actually sent: %s\n", current_reply));
353
0
#endif
354
0
          reply[replies].resp_retcode = PAM_SUCCESS;
355
0
          reply[replies].resp = smb_pam_copy_fstring(
356
0
            current_reply);
357
0
          found = True;
358
0
          break;
359
0
        }
360
0
      }
361
      /* PAM frees resp */
362
0
      if (!found) {
363
0
        DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg));
364
0
        free_pw_chat(pw_chat);
365
0
        SAFE_FREE(reply);
366
0
        return PAM_CONV_ERR;
367
0
      }
368
0
      break;
369
370
0
    case PAM_PROMPT_ECHO_OFF:
371
0
      DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: PAM said: %s\n", msg[replies]->msg));
372
0
      fstrcpy(current_prompt, msg[replies]->msg);
373
0
      trim_char(current_prompt, ' ', ' ');
374
0
      for (t=pw_chat; t; t=t->next) {
375
376
0
        DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: trying to match |%s| to |%s|\n",
377
0
            t->prompt, current_prompt ));
378
379
0
        if (unix_wild_match(t->prompt, current_prompt)) {
380
0
          fstrcpy(current_reply, t->reply);
381
0
          DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We sent: %s\n", current_reply));
382
0
          pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword);
383
0
          reply[replies].resp_retcode = PAM_SUCCESS;
384
0
          reply[replies].resp = smb_pam_copy_fstring(
385
0
            current_reply);
386
0
#ifdef DEBUG_PASSWORD
387
0
          DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We actually sent: %s\n", current_reply));
388
0
#endif
389
0
          found = True;
390
0
          break;
391
0
        }
392
0
      }
393
      /* PAM frees resp */
394
395
0
      if (!found) {
396
0
        DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg));
397
0
        free_pw_chat(pw_chat);
398
0
        SAFE_FREE(reply);
399
0
        return PAM_CONV_ERR;
400
0
      }
401
0
      break;
402
403
0
    case PAM_TEXT_INFO:
404
0
      FALL_THROUGH;
405
406
0
    case PAM_ERROR_MSG:
407
      /* ignore it... */
408
0
      reply[replies].resp_retcode = PAM_SUCCESS;
409
0
      reply[replies].resp = NULL;
410
0
      break;
411
412
0
    default:
413
      /* Must be an error of some sort... */
414
0
      free_pw_chat(pw_chat);
415
0
      SAFE_FREE(reply);
416
0
      return PAM_CONV_ERR;
417
0
    }
418
0
  }
419
420
0
  free_pw_chat(pw_chat);
421
0
  if (reply)
422
0
    *resp = reply;
423
0
  return PAM_SUCCESS;
424
0
}
425
426
/***************************************************************************
427
 Free up a malloced pam_conv struct.
428
****************************************************************************/
429
430
static void smb_free_pam_conv(struct pam_conv *pconv)
431
0
{
432
0
  if (pconv)
433
0
    SAFE_FREE(pconv->appdata_ptr);
434
435
0
  SAFE_FREE(pconv);
436
0
}
437
438
/***************************************************************************
439
 Allocate a pam_conv struct.
440
****************************************************************************/
441
442
static struct pam_conv *smb_setup_pam_conv(smb_pam_conv_fn smb_pam_conv_fnptr, const char *user,
443
          const char *passwd, const char *newpass)
444
0
{
445
0
  struct pam_conv *pconv = SMB_MALLOC_P(struct pam_conv);
446
0
  struct smb_pam_userdata *udp = SMB_MALLOC_P(struct smb_pam_userdata);
447
448
0
  if (pconv == NULL || udp == NULL) {
449
0
    SAFE_FREE(pconv);
450
0
    SAFE_FREE(udp);
451
0
    return NULL;
452
0
  }
453
454
0
  udp->PAM_username = user;
455
0
  udp->PAM_password = passwd;
456
0
  udp->PAM_newpassword = newpass;
457
458
0
  pconv->conv = smb_pam_conv_fnptr;
459
0
  pconv->appdata_ptr = (void *)udp;
460
0
  return pconv;
461
0
}
462
463
/*
464
 * PAM Closing out cleanup handler
465
 */
466
467
static bool smb_pam_end(pam_handle_t *pamh, struct pam_conv *smb_pam_conv_ptr)
468
0
{
469
0
  int pam_error;
470
471
0
  smb_free_pam_conv(smb_pam_conv_ptr);
472
473
0
  if( pamh != NULL ) {
474
0
    pam_error = pam_end(pamh, 0);
475
0
    if (pam_error == PAM_SUCCESS) {
476
0
      DBG_NOTICE("PAM: PAM_END OK.\n");
477
0
      return True;
478
0
    }
479
480
0
    DBG_WARNING("PAM: PAM_END FAILED (%d).\n", pam_error);
481
0
  } else {
482
0
    DBG_INFO("PAM: not initialised\n");
483
0
  }
484
485
0
  return False;
486
0
}
487
488
/*
489
 * Start PAM authentication for specified account
490
 */
491
492
static bool smb_pam_start(pam_handle_t **pamh, const char *user, const char *rhost, struct pam_conv *pconv)
493
0
{
494
0
  int pam_error;
495
496
0
  *pamh = (pam_handle_t *)NULL;
497
498
0
  DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", user));
499
500
0
  pam_error = pam_start("samba", user, pconv, pamh);
501
0
  if( !smb_pam_error_handler(*pamh, pam_error, "Init Failed", 0)) {
502
0
    *pamh = (pam_handle_t *)NULL;
503
0
    return False;
504
0
  }
505
506
0
#ifdef HAVE_PAM_RHOST
507
0
  DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", rhost));
508
0
  pam_error = pam_set_item(*pamh, PAM_RHOST, rhost);
509
0
  if(!smb_pam_error_handler(*pamh, pam_error, "set rhost failed", 0)) {
510
0
    smb_pam_end(*pamh, pconv);
511
0
    *pamh = (pam_handle_t *)NULL;
512
0
    return False;
513
0
  }
514
0
#endif
515
0
#ifdef HAVE_PAM_TTY
516
0
  DEBUG(4,("smb_pam_start: PAM: setting tty\n"));
517
0
  pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
518
0
  if (!smb_pam_error_handler(*pamh, pam_error, "set tty failed", 0)) {
519
0
    smb_pam_end(*pamh, pconv);
520
0
    *pamh = (pam_handle_t *)NULL;
521
0
    return False;
522
0
  }
523
0
#endif
524
0
  DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", user));
525
0
  return True;
526
0
}
527
528
/*
529
 * PAM Authentication Handler
530
 */
531
static NTSTATUS smb_pam_auth(pam_handle_t *pamh, const char *user)
532
0
{
533
0
  int pam_error;
534
0
  NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
535
536
  /*
537
   * To enable debugging set in /etc/pam.d/samba:
538
   *  auth required /lib/security/pam_pwdb.so nullok shadow audit
539
   */
540
541
0
  DEBUG(4,("smb_pam_auth: PAM: Authenticate User: %s\n", user));
542
0
  pam_error = pam_authenticate(pamh, PAM_SILENT | (lp_null_passwords() ? 0 : PAM_DISALLOW_NULL_AUTHTOK));
543
0
  switch( pam_error ){
544
0
    case PAM_AUTH_ERR:
545
0
      DEBUG(2, ("smb_pam_auth: PAM: Authentication Error for user %s\n", user));
546
0
      break;
547
0
    case PAM_CRED_INSUFFICIENT:
548
0
      DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user));
549
0
      break;
550
0
    case PAM_AUTHINFO_UNAVAIL:
551
0
      DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user));
552
0
      break;
553
0
    case PAM_USER_UNKNOWN:
554
0
      DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user));
555
0
      break;
556
0
    case PAM_MAXTRIES:
557
0
      DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user));
558
0
      break;
559
0
    case PAM_ABORT:
560
0
      DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user));
561
0
      break;
562
0
    case PAM_SUCCESS:
563
0
      DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user));
564
0
      break;
565
0
    default:
566
0
      DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user));
567
0
      break;
568
0
  }
569
570
0
  smb_pam_nt_status_error_handler(pamh, pam_error, "Authentication Failure", 2, &nt_status);
571
0
  return nt_status;
572
0
}
573
574
/*
575
 * PAM Account Handler
576
 */
577
static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user)
578
0
{
579
0
  int pam_error;
580
0
  NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED;
581
582
0
  DEBUG(4,("smb_pam_account: PAM: Account Management for User: %s\n", user));
583
0
  pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */
584
0
  switch( pam_error ) {
585
0
    case PAM_AUTHTOK_EXPIRED:
586
0
      DEBUG(2, ("smb_pam_account: PAM: User %s is valid but password is expired\n", user));
587
0
      break;
588
0
    case PAM_ACCT_EXPIRED:
589
0
      DEBUG(2, ("smb_pam_account: PAM: User %s no longer permitted to access system\n", user));
590
0
      break;
591
0
    case PAM_AUTH_ERR:
592
0
      DEBUG(2, ("smb_pam_account: PAM: There was an authentication error for user %s\n", user));
593
0
      break;
594
0
    case PAM_PERM_DENIED:
595
0
      DEBUG(0, ("smb_pam_account: PAM: User %s is NOT permitted to access system at this time\n", user));
596
0
      break;
597
0
    case PAM_USER_UNKNOWN:
598
0
      DEBUG(0, ("smb_pam_account: PAM: User \"%s\" is NOT known to account management\n", user));
599
0
      break;
600
0
    case PAM_SUCCESS:
601
0
      DEBUG(4, ("smb_pam_account: PAM: Account OK for User: %s\n", user));
602
0
      break;
603
0
    default:
604
0
      DEBUG(0, ("smb_pam_account: PAM: UNKNOWN PAM ERROR (%d) during Account Management for User: %s\n", pam_error, user));
605
0
      break;
606
0
  }
607
608
0
  smb_pam_nt_status_error_handler(pamh, pam_error, "Account Check Failed", 2, &nt_status);
609
0
  return nt_status;
610
0
}
611
612
/*
613
 * PAM Credential Setting
614
 */
615
616
static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, const char * user)
617
0
{
618
0
  int pam_error;
619
0
  NTSTATUS nt_status = NT_STATUS_NO_TOKEN;
620
621
  /*
622
   * This will allow samba to acquire a kerberos token. And, when
623
   * exporting an AFS cell, be able to /write/ to this cell.
624
   */
625
626
0
  DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user));
627
0
  pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT));
628
0
  switch( pam_error ) {
629
0
    case PAM_CRED_UNAVAIL:
630
0
      DEBUG(0, ("smb_pam_setcred: PAM: Credentials not found for user:%s\n", user ));
631
0
      break;
632
0
    case PAM_CRED_EXPIRED:
633
0
      DEBUG(0, ("smb_pam_setcred: PAM: Credentials for user: \"%s\" EXPIRED!\n", user ));
634
0
      break;
635
0
    case PAM_USER_UNKNOWN:
636
0
      DEBUG(0, ("smb_pam_setcred: PAM: User: \"%s\" is NOT known so can not set credentials!\n", user ));
637
0
      break;
638
0
    case PAM_CRED_ERR:
639
0
      DEBUG(0, ("smb_pam_setcred: PAM: Unknown setcredentials error - unable to set credentials for %s\n", user ));
640
0
      break;
641
0
    case PAM_SUCCESS:
642
0
      DEBUG(4, ("smb_pam_setcred: PAM: SetCredentials OK for User: %s\n", user));
643
0
      break;
644
0
    default:
645
0
      DEBUG(0, ("smb_pam_setcred: PAM: UNKNOWN PAM ERROR (%d) during SetCredentials for User: %s\n", pam_error, user));
646
0
      break;
647
0
  }
648
649
0
  smb_pam_nt_status_error_handler(pamh, pam_error, "Set Credential Failure", 2, &nt_status);
650
0
  return nt_status;
651
0
}
652
653
/*
654
 * PAM Internal Session Handler
655
 */
656
static bool smb_internal_pam_session(pam_handle_t *pamh, const char *user, const char *tty, bool flag)
657
0
{
658
0
  int pam_error;
659
660
0
#ifdef HAVE_PAM_TTY
661
0
  DEBUG(4,("smb_internal_pam_session: PAM: tty set to: %s\n", tty));
662
0
  pam_error = pam_set_item(pamh, PAM_TTY, tty);
663
0
  if (!smb_pam_error_handler(pamh, pam_error, "set tty failed", 0))
664
0
    return False;
665
0
#endif
666
667
0
  if (flag) {
668
0
    pam_error = pam_open_session(pamh, PAM_SILENT);
669
0
    if (!smb_pam_error_handler(pamh, pam_error, "session setup failed", 0))
670
0
      return False;
671
0
  } else {
672
0
    pam_setcred(pamh, (PAM_DELETE_CRED|PAM_SILENT)); /* We don't care if this fails */
673
0
    pam_error = pam_close_session(pamh, PAM_SILENT); /* This will probably pick up the error anyway */
674
0
    if (!smb_pam_error_handler(pamh, pam_error, "session close failed", 0))
675
0
      return False;
676
0
  }
677
0
  return (True);
678
0
}
679
680
/*
681
 * Internal PAM Password Changer.
682
 */
683
684
static bool smb_pam_chauthtok(pam_handle_t *pamh, const char * user)
685
0
{
686
0
  int pam_error;
687
688
0
  DEBUG(4,("smb_pam_chauthtok: PAM: Password Change for User: %s\n", user));
689
690
0
  pam_error = pam_chauthtok(pamh, PAM_SILENT); /* Change Password */
691
692
0
  switch( pam_error ) {
693
0
  case PAM_AUTHTOK_ERR:
694
0
    DEBUG(2, ("PAM: unable to obtain the new authentication token - is password to weak?\n"));
695
0
    break;
696
697
  /* This doesn't seem to be defined on Solaris. JRA */
698
0
#ifdef PAM_AUTHTOK_RECOVER_ERR
699
0
  case PAM_AUTHTOK_RECOVER_ERR:
700
0
    DEBUG(2, ("PAM: unable to obtain the old authentication token - was the old password wrong?.\n"));
701
0
    break;
702
0
#endif
703
704
0
  case PAM_AUTHTOK_LOCK_BUSY:
705
0
    DEBUG(2, ("PAM: unable to change the authentication token since it is currently locked.\n"));
706
0
    break;
707
0
  case PAM_AUTHTOK_DISABLE_AGING:
708
0
    DEBUG(2, ("PAM: Authentication token aging has been disabled.\n"));
709
0
    break;
710
0
  case PAM_PERM_DENIED:
711
0
    DEBUG(0, ("PAM: Permission denied.\n"));
712
0
    break;
713
0
  case PAM_TRY_AGAIN:
714
0
    DEBUG(0, ("PAM: Could not update all authentication token(s). No authentication tokens were updated.\n"));
715
0
    break;
716
0
  case PAM_USER_UNKNOWN:
717
0
    DEBUG(0, ("PAM: User not known to PAM\n"));
718
0
    break;
719
0
  case PAM_SUCCESS:
720
0
    DEBUG(4, ("PAM: Account OK for User: %s\n", user));
721
0
    break;
722
0
  default:
723
0
    DEBUG(0, ("PAM: UNKNOWN PAM ERROR (%d) for User: %s\n", pam_error, user));
724
0
  }
725
726
0
  if(!smb_pam_error_handler(pamh, pam_error, "Password Change Failed", 2)) {
727
0
    return False;
728
0
  }
729
730
  /* If this point is reached, the password has changed. */
731
0
  return True;
732
0
}
733
734
/*
735
 * PAM Externally accessible Session handler
736
 */
737
738
bool smb_pam_claim_session(const char *user, const char *tty, const char *rhost)
739
0
{
740
0
  pam_handle_t *pamh = NULL;
741
0
  struct pam_conv *pconv = NULL;
742
743
  /* Ignore PAM if told to. */
744
745
0
  if (!lp_obey_pam_restrictions())
746
0
    return True;
747
748
0
  if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
749
0
    return False;
750
751
0
  if (!smb_pam_start(&pamh, user, rhost, pconv))
752
0
    return False;
753
754
0
  if (!smb_internal_pam_session(pamh, user, tty, True)) {
755
0
    smb_pam_end(pamh, pconv);
756
0
    return False;
757
0
  }
758
759
0
  return smb_pam_end(pamh, pconv);
760
0
}
761
762
/*
763
 * PAM Externally accessible Session handler
764
 */
765
766
bool smb_pam_close_session(const char *user, const char *tty, const char *rhost)
767
0
{
768
0
  pam_handle_t *pamh = NULL;
769
0
  struct pam_conv *pconv = NULL;
770
771
  /* Ignore PAM if told to. */
772
773
0
  if (!lp_obey_pam_restrictions())
774
0
    return True;
775
776
0
  if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
777
0
    return False;
778
779
0
  if (!smb_pam_start(&pamh, user, rhost, pconv))
780
0
    return False;
781
782
0
  if (!smb_internal_pam_session(pamh, user, tty, False)) {
783
0
    smb_pam_end(pamh, pconv);
784
0
    return False;
785
0
  }
786
787
0
  return smb_pam_end(pamh, pconv);
788
0
}
789
790
/*
791
 * PAM Externally accessible Account handler
792
 */
793
794
NTSTATUS smb_pam_accountcheck(const char *user, const char *rhost)
795
0
{
796
0
  NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED;
797
0
  pam_handle_t *pamh = NULL;
798
0
  struct pam_conv *pconv = NULL;
799
800
  /* Ignore PAM if told to. */
801
802
0
  if (!lp_obey_pam_restrictions())
803
0
    return NT_STATUS_OK;
804
805
0
  if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
806
0
    return NT_STATUS_NO_MEMORY;
807
808
0
  if (!smb_pam_start(&pamh, user, rhost, pconv))
809
0
    return NT_STATUS_ACCOUNT_DISABLED;
810
811
0
  if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user)))
812
0
    DEBUG(0, ("smb_pam_accountcheck: PAM: Account Validation Failed - Rejecting User %s!\n", user));
813
814
0
  smb_pam_end(pamh, pconv);
815
0
  return nt_status;
816
0
}
817
818
/*
819
 * PAM Password Validation Suite
820
 */
821
822
NTSTATUS smb_pam_passcheck(const char * user, const char * rhost,
823
         const char * password)
824
0
{
825
0
  pam_handle_t *pamh = NULL;
826
0
  NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
827
0
  struct pam_conv *pconv = NULL;
828
829
  /*
830
   * Note we can't ignore PAM here as this is the only
831
   * way of doing auths on plaintext passwords when
832
   * compiled --with-pam.
833
   */
834
835
0
  if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, password, NULL)) == NULL)
836
0
    return NT_STATUS_LOGON_FAILURE;
837
838
0
  if (!smb_pam_start(&pamh, user, rhost, pconv))
839
0
    return NT_STATUS_LOGON_FAILURE;
840
841
0
  if (!NT_STATUS_IS_OK(nt_status = smb_pam_auth(pamh, user))) {
842
0
    DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_auth failed - Rejecting User %s !\n", user));
843
0
    smb_pam_end(pamh, pconv);
844
0
    return nt_status;
845
0
  }
846
847
0
  if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user))) {
848
0
    DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_account failed - Rejecting User %s !\n", user));
849
0
    smb_pam_end(pamh, pconv);
850
0
    return nt_status;
851
0
  }
852
853
0
  if (!NT_STATUS_IS_OK(nt_status = smb_pam_setcred(pamh, user))) {
854
0
    DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_setcred failed - Rejecting User %s !\n", user));
855
0
    smb_pam_end(pamh, pconv);
856
0
    return nt_status;
857
0
  }
858
859
0
  smb_pam_end(pamh, pconv);
860
0
  return nt_status;
861
0
}
862
863
/*
864
 * PAM Password Change Suite
865
 */
866
867
bool smb_pam_passchange(const char *user, const char *rhost,
868
      const char *oldpassword, const char *newpassword)
869
0
{
870
  /* Appropriate quantities of root should be obtained BEFORE calling this function */
871
0
  struct pam_conv *pconv = NULL;
872
0
  pam_handle_t *pamh = NULL;
873
874
0
  if ((pconv = smb_setup_pam_conv(smb_pam_passchange_conv, user, oldpassword, newpassword)) == NULL)
875
0
    return False;
876
877
0
  if(!smb_pam_start(&pamh, user, rhost, pconv))
878
0
    return False;
879
880
0
  if (!smb_pam_chauthtok(pamh, user)) {
881
0
    DEBUG(0, ("smb_pam_passchange: PAM: Password Change Failed for user %s!\n", user));
882
0
    smb_pam_end(pamh, pconv);
883
0
    return False;
884
0
  }
885
886
0
  return smb_pam_end(pamh, pconv);
887
0
}
888
889
#else
890
891
/* If PAM not used, no PAM restrictions on accounts. */
892
NTSTATUS smb_pam_accountcheck(const char *user, const char *rhost)
893
{
894
  return NT_STATUS_OK;
895
}
896
897
/* If PAM not used, also no PAM restrictions on sessions. */
898
bool smb_pam_claim_session(const char *user, const char *tty, const char *rhost)
899
{
900
  return True;
901
}
902
903
/* If PAM not used, also no PAM restrictions on sessions. */
904
bool smb_pam_close_session(const char *in_user, const char *tty, const char *rhost)
905
{
906
  return True;
907
}
908
#endif /* WITH_PAM */