source: branches/samba-3.2.x/source/nsswitch/pam_winbind.c

Last change on this file was 335, checked in by Herwig Bauernfeind, 16 years ago

Update 3.2 to 3.2.14 (final)

File size: 69.3 KB
Line 
1/* pam_winbind module
2
3 Copyright Andrew Tridgell <tridge@samba.org> 2000
4 Copyright Tim Potter <tpot@samba.org> 2000
5 Copyright Andrew Bartlett <abartlet@samba.org> 2002
6 Copyright Guenther Deschner <gd@samba.org> 2005-2008
7
8 largely based on pam_userdb by Cristian Gafton <gafton@redhat.com> also
9 contains large slabs of code from pam_unix by Elliot Lee
10 <sopwith@redhat.com> (see copyright below for full details)
11*/
12
13#include "pam_winbind.h"
14
15#define _PAM_LOG_FUNCTION_ENTER(function, ctx) \
16 do { \
17 _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] ENTER: " \
18 function " (flags: 0x%04x)", ctx->pamh, ctx->flags); \
19 _pam_log_state(ctx); \
20 } while (0)
21
22#define _PAM_LOG_FUNCTION_LEAVE(function, ctx, retval) \
23 do { \
24 _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] LEAVE: " \
25 function " returning %d", ctx->pamh, retval); \
26 _pam_log_state(ctx); \
27 } while (0)
28
29/* data tokens */
30
31#define MAX_PASSWD_TRIES 3
32
33/*
34 * Work around the pam API that has functions with void ** as parameters
35 * These lead to strict aliasing warnings with gcc.
36 */
37static int _pam_get_item(const pam_handle_t *pamh,
38 int item_type,
39 const void *_item)
40{
41 const void **item = (const void **)_item;
42 return pam_get_item(pamh, item_type, item);
43}
44static int _pam_get_data(const pam_handle_t *pamh,
45 const char *module_data_name,
46 const void *_data)
47{
48 const void **data = (const void **)_data;
49 return pam_get_data(pamh, module_data_name, data);
50}
51
52/* some syslogging */
53
54#ifdef HAVE_PAM_VSYSLOG
55static void _pam_log_int(const pam_handle_t *pamh,
56 int err,
57 const char *format,
58 va_list args)
59{
60 pam_vsyslog(pamh, err, format, args);
61}
62#else
63static void _pam_log_int(const pam_handle_t *pamh,
64 int err,
65 const char *format,
66 va_list args)
67{
68 char *format2 = NULL;
69 const char *service;
70
71 _pam_get_item(pamh, PAM_SERVICE, &service);
72
73 format2 = (char *)malloc(strlen(MODULE_NAME)+strlen(format)+strlen(service)+5);
74 if (format2 == NULL) {
75 /* what else todo ? */
76 vsyslog(err, format, args);
77 return;
78 }
79
80 sprintf(format2, "%s(%s): %s", MODULE_NAME, service, format);
81 vsyslog(err, format2, args);
82 SAFE_FREE(format2);
83}
84#endif /* HAVE_PAM_VSYSLOG */
85
86static bool _pam_log_is_silent(int ctrl)
87{
88 return on(ctrl, WINBIND_SILENT);
89}
90
91static void _pam_log(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
92static void _pam_log(struct pwb_context *r, int err, const char *format, ...)
93{
94 va_list args;
95
96 if (_pam_log_is_silent(r->ctrl)) {
97 return;
98 }
99
100 va_start(args, format);
101 _pam_log_int(r->pamh, err, format, args);
102 va_end(args);
103}
104static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
105static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
106{
107 va_list args;
108
109 if (_pam_log_is_silent(ctrl)) {
110 return;
111 }
112
113 va_start(args, format);
114 _pam_log_int(pamh, err, format, args);
115 va_end(args);
116}
117
118static bool _pam_log_is_debug_enabled(int ctrl)
119{
120 if (ctrl == -1) {
121 return false;
122 }
123
124 if (_pam_log_is_silent(ctrl)) {
125 return false;
126 }
127
128 if (!(ctrl & WINBIND_DEBUG_ARG)) {
129 return false;
130 }
131
132 return true;
133}
134
135static bool _pam_log_is_debug_state_enabled(int ctrl)
136{
137 if (!(ctrl & WINBIND_DEBUG_STATE)) {
138 return false;
139 }
140
141 return _pam_log_is_debug_enabled(ctrl);
142}
143
144static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
145static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...)
146{
147 va_list args;
148
149 if (!_pam_log_is_debug_enabled(r->ctrl)) {
150 return;
151 }
152
153 va_start(args, format);
154 _pam_log_int(r->pamh, err, format, args);
155 va_end(args);
156}
157static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
158static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
159{
160 va_list args;
161
162 if (!_pam_log_is_debug_enabled(ctrl)) {
163 return;
164 }
165
166 va_start(args, format);
167 _pam_log_int(pamh, err, format, args);
168 va_end(args);
169}
170
171static void _pam_log_state_datum(struct pwb_context *ctx,
172 int item_type,
173 const char *key,
174 int is_string)
175{
176 const void *data = NULL;
177 if (item_type != 0) {
178 pam_get_item(ctx->pamh, item_type, &data);
179 } else {
180 pam_get_data(ctx->pamh, key, &data);
181 }
182 if (data != NULL) {
183 const char *type = (item_type != 0) ? "ITEM" : "DATA";
184 if (is_string != 0) {
185 _pam_log_debug(ctx, LOG_DEBUG,
186 "[pamh: %p] STATE: %s(%s) = \"%s\" (%p)",
187 ctx->pamh, type, key, (const char *)data,
188 data);
189 } else {
190 _pam_log_debug(ctx, LOG_DEBUG,
191 "[pamh: %p] STATE: %s(%s) = %p",
192 ctx->pamh, type, key, data);
193 }
194 }
195}
196
197#define _PAM_LOG_STATE_DATA_POINTER(ctx, module_data_name) \
198 _pam_log_state_datum(ctx, 0, module_data_name, 0)
199
200#define _PAM_LOG_STATE_DATA_STRING(ctx, module_data_name) \
201 _pam_log_state_datum(ctx, 0, module_data_name, 1)
202
203#define _PAM_LOG_STATE_ITEM_POINTER(ctx, item_type) \
204 _pam_log_state_datum(ctx, item_type, #item_type, 0)
205
206#define _PAM_LOG_STATE_ITEM_STRING(ctx, item_type) \
207 _pam_log_state_datum(ctx, item_type, #item_type, 1)
208
209#ifdef DEBUG_PASSWORD
210#define _LOG_PASSWORD_AS_STRING 1
211#else
212#define _LOG_PASSWORD_AS_STRING 0
213#endif
214
215#define _PAM_LOG_STATE_ITEM_PASSWORD(ctx, item_type) \
216 _pam_log_state_datum(ctx, item_type, #item_type, \
217 _LOG_PASSWORD_AS_STRING)
218
219static void _pam_log_state(struct pwb_context *ctx)
220{
221 if (!_pam_log_is_debug_state_enabled(ctx->ctrl)) {
222 return;
223 }
224
225 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_SERVICE);
226 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER);
227 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_TTY);
228 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RHOST);
229 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RUSER);
230 _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_OLDAUTHTOK);
231 _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_AUTHTOK);
232 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER_PROMPT);
233 _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_CONV);
234#ifdef PAM_FAIL_DELAY
235 _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_FAIL_DELAY);
236#endif
237#ifdef PAM_REPOSITORY
238 _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_REPOSITORY);
239#endif
240
241 _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_HOMEDIR);
242 _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSCRIPT);
243 _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSERVER);
244 _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_PROFILEPATH);
245 _PAM_LOG_STATE_DATA_STRING(ctx,
246 PAM_WINBIND_NEW_AUTHTOK_REQD);
247 /* Use atoi to get PAM result code */
248 _PAM_LOG_STATE_DATA_STRING(ctx,
249 PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH);
250 _PAM_LOG_STATE_DATA_POINTER(ctx, PAM_WINBIND_PWD_LAST_SET);
251}
252
253static int _pam_parse(const pam_handle_t *pamh,
254 int flags,
255 int argc,
256 const char **argv,
257 dictionary **result_d)
258{
259 int ctrl = 0;
260 const char *config_file = NULL;
261 int i;
262 const char **v;
263 dictionary *d = NULL;
264
265 if (flags & PAM_SILENT) {
266 ctrl |= WINBIND_SILENT;
267 }
268
269 for (i=argc,v=argv; i-- > 0; ++v) {
270 if (!strncasecmp(*v, "config", strlen("config"))) {
271 ctrl |= WINBIND_CONFIG_FILE;
272 config_file = v[i];
273 break;
274 }
275 }
276
277 if (config_file == NULL) {
278 config_file = PAM_WINBIND_CONFIG_FILE;
279 }
280
281 d = iniparser_load(config_file);
282 if (d == NULL) {
283 goto config_from_pam;
284 }
285
286 if (iniparser_getboolean(d, "global:debug", false)) {
287 ctrl |= WINBIND_DEBUG_ARG;
288 }
289
290 if (iniparser_getboolean(d, "global:debug_state", false)) {
291 ctrl |= WINBIND_DEBUG_STATE;
292 }
293
294 if (iniparser_getboolean(d, "global:cached_login", false)) {
295 ctrl |= WINBIND_CACHED_LOGIN;
296 }
297
298 if (iniparser_getboolean(d, "global:krb5_auth", false)) {
299 ctrl |= WINBIND_KRB5_AUTH;
300 }
301
302 if (iniparser_getboolean(d, "global:silent", false)) {
303 ctrl |= WINBIND_SILENT;
304 }
305
306 if (iniparser_getstr(d, "global:krb5_ccache_type") != NULL) {
307 ctrl |= WINBIND_KRB5_CCACHE_TYPE;
308 }
309
310 if ((iniparser_getstr(d, "global:require-membership-of") != NULL) ||
311 (iniparser_getstr(d, "global:require_membership_of") != NULL)) {
312 ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
313 }
314
315 if (iniparser_getboolean(d, "global:try_first_pass", false)) {
316 ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
317 }
318
319 if (iniparser_getint(d, "global:warn_pwd_expire", 0)) {
320 ctrl |= WINBIND_WARN_PWD_EXPIRE;
321 }
322
323config_from_pam:
324 /* step through arguments */
325 for (i=argc,v=argv; i-- > 0; ++v) {
326
327 /* generic options */
328 if (!strcmp(*v,"debug"))
329 ctrl |= WINBIND_DEBUG_ARG;
330 else if (!strcasecmp(*v, "debug_state"))
331 ctrl |= WINBIND_DEBUG_STATE;
332 else if (!strcasecmp(*v, "silent"))
333 ctrl |= WINBIND_SILENT;
334 else if (!strcasecmp(*v, "use_authtok"))
335 ctrl |= WINBIND_USE_AUTHTOK_ARG;
336 else if (!strcasecmp(*v, "use_first_pass"))
337 ctrl |= WINBIND_USE_FIRST_PASS_ARG;
338 else if (!strcasecmp(*v, "try_first_pass"))
339 ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
340 else if (!strcasecmp(*v, "unknown_ok"))
341 ctrl |= WINBIND_UNKNOWN_OK_ARG;
342 else if (!strncasecmp(*v, "require_membership_of",
343 strlen("require_membership_of")))
344 ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
345 else if (!strncasecmp(*v, "require-membership-of",
346 strlen("require-membership-of")))
347 ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
348 else if (!strcasecmp(*v, "krb5_auth"))
349 ctrl |= WINBIND_KRB5_AUTH;
350 else if (!strncasecmp(*v, "krb5_ccache_type",
351 strlen("krb5_ccache_type")))
352 ctrl |= WINBIND_KRB5_CCACHE_TYPE;
353 else if (!strcasecmp(*v, "cached_login"))
354 ctrl |= WINBIND_CACHED_LOGIN;
355 else {
356 __pam_log(pamh, ctrl, LOG_ERR,
357 "pam_parse: unknown option: %s", *v);
358 return -1;
359 }
360
361 }
362
363 if (result_d) {
364 *result_d = d;
365 } else {
366 if (d) {
367 iniparser_freedict(d);
368 }
369 }
370
371 return ctrl;
372};
373
374static void _pam_winbind_free_context(struct pwb_context *ctx)
375{
376 if (ctx->dict) {
377 iniparser_freedict(ctx->dict);
378 }
379
380 SAFE_FREE(ctx);
381}
382
383static int _pam_winbind_init_context(pam_handle_t *pamh,
384 int flags,
385 int argc,
386 const char **argv,
387 struct pwb_context **ctx_p)
388{
389 struct pwb_context *r = NULL;
390
391 r = (struct pwb_context *)malloc(sizeof(struct pwb_context));
392 if (!r) {
393 return PAM_BUF_ERR;
394 }
395
396 ZERO_STRUCTP(r);
397
398 r->pamh = pamh;
399 r->flags = flags;
400 r->argc = argc;
401 r->argv = argv;
402 r->ctrl = _pam_parse(pamh, flags, argc, argv, &r->dict);
403 if (r->ctrl == -1) {
404 _pam_winbind_free_context(r);
405 return PAM_SYSTEM_ERR;
406 }
407
408 *ctx_p = r;
409
410 return PAM_SUCCESS;
411}
412
413static void _pam_winbind_cleanup_func(pam_handle_t *pamh,
414 void *data,
415 int error_status)
416{
417 int ctrl = _pam_parse(pamh, 0, 0, NULL, NULL);
418 if (_pam_log_is_debug_state_enabled(ctrl)) {
419 __pam_log_debug(pamh, ctrl, LOG_DEBUG,
420 "[pamh: %p] CLEAN: cleaning up PAM data %p "
421 "(error_status = %d)", pamh, data,
422 error_status);
423 }
424 SAFE_FREE(data);
425}
426
427
428static const struct ntstatus_errors {
429 const char *ntstatus_string;
430 const char *error_string;
431} ntstatus_errors[] = {
432 {"NT_STATUS_OK",
433 "Success"},
434 {"NT_STATUS_BACKUP_CONTROLLER",
435 "No primary Domain Controler available"},
436 {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
437 "No domain controllers found"},
438 {"NT_STATUS_NO_LOGON_SERVERS",
439 "No logon servers"},
440 {"NT_STATUS_PWD_TOO_SHORT",
441 "Password too short"},
442 {"NT_STATUS_PWD_TOO_RECENT",
443 "The password of this user is too recent to change"},
444 {"NT_STATUS_PWD_HISTORY_CONFLICT",
445 "Password is already in password history"},
446 {"NT_STATUS_PASSWORD_EXPIRED",
447 "Your password has expired"},
448 {"NT_STATUS_PASSWORD_MUST_CHANGE",
449 "You need to change your password now"},
450 {"NT_STATUS_INVALID_WORKSTATION",
451 "You are not allowed to logon from this workstation"},
452 {"NT_STATUS_INVALID_LOGON_HOURS",
453 "You are not allowed to logon at this time"},
454 {"NT_STATUS_ACCOUNT_EXPIRED",
455 "Your account has expired. "
456 "Please contact your System administrator"}, /* SCNR */
457 {"NT_STATUS_ACCOUNT_DISABLED",
458 "Your account is disabled. "
459 "Please contact your System administrator"}, /* SCNR */
460 {"NT_STATUS_ACCOUNT_LOCKED_OUT",
461 "Your account has been locked. "
462 "Please contact your System administrator"}, /* SCNR */
463 {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
464 "Invalid Trust Account"},
465 {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
466 "Invalid Trust Account"},
467 {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
468 "Invalid Trust Account"},
469 {"NT_STATUS_ACCESS_DENIED",
470 "Access is denied"},
471 {NULL, NULL}
472};
473
474static const char *_get_ntstatus_error_string(const char *nt_status_string)
475{
476 int i;
477 for (i=0; ntstatus_errors[i].ntstatus_string != NULL; i++) {
478 if (!strcasecmp(ntstatus_errors[i].ntstatus_string,
479 nt_status_string)) {
480 return ntstatus_errors[i].error_string;
481 }
482 }
483 return NULL;
484}
485
486/* --- authentication management functions --- */
487
488/* Attempt a conversation */
489
490static int converse(const pam_handle_t *pamh,
491 int nargs,
492 struct pam_message **message,
493 struct pam_response **response)
494{
495 int retval;
496 struct pam_conv *conv;
497
498 retval = _pam_get_item(pamh, PAM_CONV, &conv);
499 if (retval == PAM_SUCCESS) {
500 retval = conv->conv(nargs,
501 (const struct pam_message **)message,
502 response, conv->appdata_ptr);
503 }
504
505 return retval; /* propagate error status */
506}
507
508
509static int _make_remark(struct pwb_context *ctx,
510 int type,
511 const char *text)
512{
513 int retval = PAM_SUCCESS;
514
515 struct pam_message *pmsg[1], msg[1];
516 struct pam_response *resp;
517
518 if (ctx->flags & WINBIND_SILENT) {
519 return PAM_SUCCESS;
520 }
521
522 pmsg[0] = &msg[0];
523 msg[0].msg = discard_const_p(char, text);
524 msg[0].msg_style = type;
525
526 resp = NULL;
527 retval = converse(ctx->pamh, 1, pmsg, &resp);
528
529 if (resp) {
530 _pam_drop_reply(resp, 1);
531 }
532 return retval;
533}
534
535static int _make_remark_v(struct pwb_context *ctx,
536 int type,
537 const char *format,
538 va_list args)
539{
540 char *var;
541 int ret;
542
543 ret = vasprintf(&var, format, args);
544 if (ret < 0) {
545 _pam_log(ctx, LOG_ERR, "memory allocation failure");
546 return ret;
547 }
548
549 ret = _make_remark(ctx, type, var);
550 SAFE_FREE(var);
551 return ret;
552}
553
554static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
555static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...)
556{
557 int ret;
558 va_list args;
559
560 va_start(args, format);
561 ret = _make_remark_v(ctx, type, format, args);
562 va_end(args);
563 return ret;
564}
565
566static int pam_winbind_request(struct pwb_context *ctx,
567 enum winbindd_cmd req_type,
568 struct winbindd_request *request,
569 struct winbindd_response *response)
570{
571 /* Fill in request and send down pipe */
572 winbindd_init_request(request, req_type);
573
574 if (winbind_write_sock(request, sizeof(*request), 0, 0) == -1) {
575 _pam_log(ctx, LOG_ERR,
576 "pam_winbind_request: write to socket failed!");
577 winbind_close_sock();
578 return PAM_SERVICE_ERR;
579 }
580
581 /* Wait for reply */
582 if (winbindd_read_reply(response) == -1) {
583 _pam_log(ctx, LOG_ERR,
584 "pam_winbind_request: read from socket failed!");
585 winbind_close_sock();
586 return PAM_SERVICE_ERR;
587 }
588
589 /* We are done with the socket - close it and avoid mischeif */
590 winbind_close_sock();
591
592 /* Copy reply data from socket */
593 if (response->result == WINBINDD_OK) {
594 return PAM_SUCCESS;
595 }
596
597 /* no need to check for pam_error codes for getpwnam() */
598 switch (req_type) {
599
600 case WINBINDD_GETPWNAM:
601 case WINBINDD_LOOKUPNAME:
602 if (strlen(response->data.auth.nt_status_string) > 0) {
603 _pam_log(ctx, LOG_ERR,
604 "request failed, NT error was %s",
605 response->data.auth.nt_status_string);
606 } else {
607 _pam_log(ctx, LOG_ERR, "request failed");
608 }
609 return PAM_USER_UNKNOWN;
610 default:
611 break;
612 }
613
614 if (response->data.auth.pam_error != PAM_SUCCESS) {
615 _pam_log(ctx, LOG_ERR,
616 "request failed: %s, "
617 "PAM error was %s (%d), NT error was %s",
618 response->data.auth.error_string,
619 pam_strerror(ctx->pamh, response->data.auth.pam_error),
620 response->data.auth.pam_error,
621 response->data.auth.nt_status_string);
622 return response->data.auth.pam_error;
623 }
624
625 _pam_log(ctx, LOG_ERR, "request failed, but PAM error 0!");
626
627 return PAM_SERVICE_ERR;
628}
629
630static int pam_winbind_request_log(struct pwb_context *ctx,
631 enum winbindd_cmd req_type,
632 struct winbindd_request *request,
633 struct winbindd_response *response,
634 const char *user)
635{
636 int retval;
637
638 retval = pam_winbind_request(ctx, req_type, request, response);
639
640 switch (retval) {
641 case PAM_AUTH_ERR:
642 /* incorrect password */
643 _pam_log(ctx, LOG_WARNING, "user '%s' denied access "
644 "(incorrect password or invalid membership)", user);
645 return retval;
646 case PAM_ACCT_EXPIRED:
647 /* account expired */
648 _pam_log(ctx, LOG_WARNING, "user '%s' account expired",
649 user);
650 return retval;
651 case PAM_AUTHTOK_EXPIRED:
652 /* password expired */
653 _pam_log(ctx, LOG_WARNING, "user '%s' password expired",
654 user);
655 return retval;
656 case PAM_NEW_AUTHTOK_REQD:
657 /* new password required */
658 _pam_log(ctx, LOG_WARNING, "user '%s' new password "
659 "required", user);
660 return retval;
661 case PAM_USER_UNKNOWN:
662 /* the user does not exist */
663 _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
664 user);
665 if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
666 return PAM_IGNORE;
667 }
668 return retval;
669 case PAM_SUCCESS:
670 /* Otherwise, the authentication looked good */
671 switch (req_type) {
672 case WINBINDD_INFO:
673 break;
674 case WINBINDD_PAM_AUTH:
675 _pam_log(ctx, LOG_NOTICE,
676 "user '%s' granted access", user);
677 break;
678 case WINBINDD_PAM_CHAUTHTOK:
679 _pam_log(ctx, LOG_NOTICE,
680 "user '%s' password changed", user);
681 break;
682 default:
683 _pam_log(ctx, LOG_NOTICE,
684 "user '%s' OK", user);
685 break;
686 }
687
688 return retval;
689 default:
690 /* we don't know anything about this return value */
691 _pam_log(ctx, LOG_ERR,
692 "internal module error (retval = %d, user = '%s')",
693 retval, user);
694 return retval;
695 }
696}
697
698/**
699 * send a password expiry message if required
700 *
701 * @param pamh PAM handle
702 * @param ctrl PAM winbind options.
703 * @param next_change expected (calculated) next expiry date.
704 * @param already_expired pointer to a boolean to indicate if the password is
705 * already expired.
706 *
707 * @return boolean Returns true if message has been sent, false if not.
708 */
709
710static bool _pam_send_password_expiry_message(struct pwb_context *ctx,
711 time_t next_change,
712 time_t now,
713 int warn_pwd_expire,
714 bool *already_expired)
715{
716 int days = 0;
717 struct tm tm_now, tm_next_change;
718
719 if (already_expired) {
720 *already_expired = false;
721 }
722
723 if (next_change <= now) {
724 PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PASSWORD_EXPIRED");
725 if (already_expired) {
726 *already_expired = true;
727 }
728 return true;
729 }
730
731 if ((next_change < 0) ||
732 (next_change > now + warn_pwd_expire * SECONDS_PER_DAY)) {
733 return false;
734 }
735
736 if ((localtime_r(&now, &tm_now) == NULL) ||
737 (localtime_r(&next_change, &tm_next_change) == NULL)) {
738 return false;
739 }
740
741 days = (tm_next_change.tm_yday+tm_next_change.tm_year*365) -
742 (tm_now.tm_yday+tm_now.tm_year*365);
743
744 if (days == 0) {
745 _make_remark(ctx, PAM_TEXT_INFO,
746 "Your password expires today");
747 return true;
748 }
749
750 if (days > 0 && days < warn_pwd_expire) {
751 _make_remark_format(ctx, PAM_TEXT_INFO,
752 "Your password will expire in %d %s",
753 days, (days > 1) ? "days":"day");
754 return true;
755 }
756
757 return false;
758}
759
760/**
761 * Send a warning if the password expires in the near future
762 *
763 * @param pamh PAM handle
764 * @param ctrl PAM winbind options.
765 * @param response The full authentication response structure.
766 * @param already_expired boolean, is the pwd already expired?
767 *
768 * @return void.
769 */
770
771static void _pam_warn_password_expiry(struct pwb_context *ctx,
772 const struct winbindd_response *response,
773 int warn_pwd_expire,
774 bool *already_expired)
775{
776 time_t now = time(NULL);
777 time_t next_change = 0;
778
779 if (already_expired) {
780 *already_expired = false;
781 }
782
783 /* accounts with ACB_PWNOEXP set never receive a warning */
784 if (response->data.auth.info3.acct_flags & ACB_PWNOEXP) {
785 return;
786 }
787
788 /* no point in sending a warning if this is a grace logon */
789 if (PAM_WB_GRACE_LOGON(response->data.auth.info3.user_flgs)) {
790 return;
791 }
792
793 /* check if the info3 must change timestamp has been set */
794 next_change = response->data.auth.info3.pass_must_change_time;
795
796 if (_pam_send_password_expiry_message(ctx, next_change, now,
797 warn_pwd_expire,
798 already_expired)) {
799 return;
800 }
801
802 /* now check for the global password policy */
803 /* good catch from Ralf Haferkamp: an expiry of "never" is translated
804 * to -1 */
805 if (response->data.auth.policy.expire <= 0) {
806 return;
807 }
808
809 next_change = response->data.auth.info3.pass_last_set_time +
810 response->data.auth.policy.expire;
811
812 if (_pam_send_password_expiry_message(ctx, next_change, now,
813 warn_pwd_expire,
814 already_expired)) {
815 return;
816 }
817
818 /* no warning sent */
819}
820
821#define IS_SID_STRING(name) (strncmp("S-", name, 2) == 0)
822
823/**
824 * Append a string, making sure not to overflow and to always return a
825 * NULL-terminated string.
826 *
827 * @param dest Destination string buffer (must already be NULL-terminated).
828 * @param src Source string buffer.
829 * @param dest_buffer_size Size of dest buffer in bytes.
830 *
831 * @return false if dest buffer is not big enough (no bytes copied), true on
832 * success.
833 */
834
835static bool safe_append_string(char *dest,
836 const char *src,
837 int dest_buffer_size)
838{
839 int dest_length = strlen(dest);
840 int src_length = strlen(src);
841
842 if (dest_length + src_length + 1 > dest_buffer_size) {
843 return false;
844 }
845
846 memcpy(dest + dest_length, src, src_length + 1);
847 return true;
848}
849
850/**
851 * Convert a names into a SID string, appending it to a buffer.
852 *
853 * @param pamh PAM handle
854 * @param ctrl PAM winbind options.
855 * @param user User in PAM request.
856 * @param name Name to convert.
857 * @param sid_list_buffer Where to append the string sid.
858 * @param sid_list_buffer Size of sid_list_buffer (in bytes).
859 *
860 * @return false on failure, true on success.
861 */
862static bool winbind_name_to_sid_string(struct pwb_context *ctx,
863 const char *user,
864 const char *name,
865 char *sid_list_buffer,
866 int sid_list_buffer_size)
867{
868 const char* sid_string;
869 struct winbindd_response sid_response;
870
871 /* lookup name? */
872 if (IS_SID_STRING(name)) {
873 sid_string = name;
874 } else {
875 struct winbindd_request sid_request;
876
877 ZERO_STRUCT(sid_request);
878 ZERO_STRUCT(sid_response);
879
880 _pam_log_debug(ctx, LOG_DEBUG,
881 "no sid given, looking up: %s\n", name);
882
883 /* fortunatly winbindd can handle non-separated names */
884 strncpy(sid_request.data.name.name, name,
885 sizeof(sid_request.data.name.name) - 1);
886
887 if (pam_winbind_request_log(ctx, WINBINDD_LOOKUPNAME,
888 &sid_request, &sid_response,
889 user)) {
890 _pam_log(ctx, LOG_INFO,
891 "could not lookup name: %s\n", name);
892 return false;
893 }
894
895 sid_string = sid_response.data.sid.sid;
896 }
897
898 if (!safe_append_string(sid_list_buffer, sid_string,
899 sid_list_buffer_size)) {
900 return false;
901 }
902
903 return true;
904}
905
906/**
907 * Convert a list of names into a list of sids.
908 *
909 * @param pamh PAM handle
910 * @param ctrl PAM winbind options.
911 * @param user User in PAM request.
912 * @param name_list List of names or string sids, separated by commas.
913 * @param sid_list_buffer Where to put the list of string sids.
914 * @param sid_list_buffer Size of sid_list_buffer (in bytes).
915 *
916 * @return false on failure, true on success.
917 */
918static bool winbind_name_list_to_sid_string_list(struct pwb_context *ctx,
919 const char *user,
920 const char *name_list,
921 char *sid_list_buffer,
922 int sid_list_buffer_size)
923{
924 bool result = false;
925 char *current_name = NULL;
926 const char *search_location;
927 const char *comma;
928
929 if (sid_list_buffer_size > 0) {
930 sid_list_buffer[0] = 0;
931 }
932
933 search_location = name_list;
934 while ((comma = strstr(search_location, ",")) != NULL) {
935 current_name = strndup(search_location,
936 comma - search_location);
937 if (NULL == current_name) {
938 goto out;
939 }
940
941 if (!winbind_name_to_sid_string(ctx, user,
942 current_name,
943 sid_list_buffer,
944 sid_list_buffer_size)) {
945 goto out;
946 }
947
948 SAFE_FREE(current_name);
949
950 if (!safe_append_string(sid_list_buffer, ",",
951 sid_list_buffer_size)) {
952 goto out;
953 }
954
955 search_location = comma + 1;
956 }
957
958 if (!winbind_name_to_sid_string(ctx, user, search_location,
959 sid_list_buffer,
960 sid_list_buffer_size)) {
961 goto out;
962 }
963
964 result = true;
965
966out:
967 SAFE_FREE(current_name);
968 return result;
969}
970
971/**
972 * put krb5ccname variable into environment
973 *
974 * @param pamh PAM handle
975 * @param ctrl PAM winbind options.
976 * @param krb5ccname env variable retrieved from winbindd.
977 *
978 * @return void.
979 */
980
981static void _pam_setup_krb5_env(struct pwb_context *ctx,
982 const char *krb5ccname)
983{
984 char var[PATH_MAX];
985 int ret;
986
987 if (off(ctx->ctrl, WINBIND_KRB5_AUTH)) {
988 return;
989 }
990
991 if (!krb5ccname || (strlen(krb5ccname) == 0)) {
992 return;
993 }
994
995 _pam_log_debug(ctx, LOG_DEBUG,
996 "request returned KRB5CCNAME: %s", krb5ccname);
997
998 if (snprintf(var, sizeof(var), "KRB5CCNAME=%s", krb5ccname) == -1) {
999 return;
1000 }
1001
1002 ret = pam_putenv(ctx->pamh, var);
1003 if (ret) {
1004 _pam_log(ctx, LOG_ERR,
1005 "failed to set KRB5CCNAME to %s: %s",
1006 var, pam_strerror(ctx->pamh, ret));
1007 }
1008}
1009
1010/**
1011 * Set string into the PAM stack.
1012 *
1013 * @param pamh PAM handle
1014 * @param ctrl PAM winbind options.
1015 * @param data_name Key name for pam_set_data.
1016 * @param value String value.
1017 *
1018 * @return void.
1019 */
1020
1021static void _pam_set_data_string(struct pwb_context *ctx,
1022 const char *data_name,
1023 const char *value)
1024{
1025 int ret;
1026
1027 if (!data_name || !value || (strlen(data_name) == 0) ||
1028 (strlen(value) == 0)) {
1029 return;
1030 }
1031
1032 ret = pam_set_data(ctx->pamh, data_name, (void *)strdup(value),
1033 _pam_winbind_cleanup_func);
1034 if (ret) {
1035 _pam_log_debug(ctx, LOG_DEBUG,
1036 "Could not set data %s: %s\n",
1037 data_name, pam_strerror(ctx->pamh, ret));
1038 }
1039
1040}
1041
1042/**
1043 * Set info3 strings into the PAM stack.
1044 *
1045 * @param pamh PAM handle
1046 * @param ctrl PAM winbind options.
1047 * @param data_name Key name for pam_set_data.
1048 * @param value String value.
1049 *
1050 * @return void.
1051 */
1052
1053static void _pam_set_data_info3(struct pwb_context *ctx,
1054 struct winbindd_response *response)
1055{
1056 _pam_set_data_string(ctx, PAM_WINBIND_HOMEDIR,
1057 response->data.auth.info3.home_dir);
1058 _pam_set_data_string(ctx, PAM_WINBIND_LOGONSCRIPT,
1059 response->data.auth.info3.logon_script);
1060 _pam_set_data_string(ctx, PAM_WINBIND_LOGONSERVER,
1061 response->data.auth.info3.logon_srv);
1062 _pam_set_data_string(ctx, PAM_WINBIND_PROFILEPATH,
1063 response->data.auth.info3.profile_path);
1064}
1065
1066/**
1067 * Free info3 strings in the PAM stack.
1068 *
1069 * @param pamh PAM handle
1070 *
1071 * @return void.
1072 */
1073
1074static void _pam_free_data_info3(pam_handle_t *pamh)
1075{
1076 pam_set_data(pamh, PAM_WINBIND_HOMEDIR, NULL, NULL);
1077 pam_set_data(pamh, PAM_WINBIND_LOGONSCRIPT, NULL, NULL);
1078 pam_set_data(pamh, PAM_WINBIND_LOGONSERVER, NULL, NULL);
1079 pam_set_data(pamh, PAM_WINBIND_PROFILEPATH, NULL, NULL);
1080}
1081
1082/**
1083 * Send PAM_ERROR_MSG for cached or grace logons.
1084 *
1085 * @param pamh PAM handle
1086 * @param ctrl PAM winbind options.
1087 * @param username User in PAM request.
1088 * @param info3_user_flgs Info3 flags containing logon type bits.
1089 *
1090 * @return void.
1091 */
1092
1093static void _pam_warn_logon_type(struct pwb_context *ctx,
1094 const char *username,
1095 uint32_t info3_user_flgs)
1096{
1097 /* inform about logon type */
1098 if (PAM_WB_GRACE_LOGON(info3_user_flgs)) {
1099
1100 _make_remark(ctx, PAM_ERROR_MSG,
1101 "Grace login. "
1102 "Please change your password as soon you're "
1103 "online again");
1104 _pam_log_debug(ctx, LOG_DEBUG,
1105 "User %s logged on using grace logon\n",
1106 username);
1107
1108 } else if (PAM_WB_CACHED_LOGON(info3_user_flgs)) {
1109
1110 _make_remark(ctx, PAM_ERROR_MSG,
1111 "Domain Controller unreachable, "
1112 "using cached credentials instead. "
1113 "Network resources may be unavailable");
1114 _pam_log_debug(ctx, LOG_DEBUG,
1115 "User %s logged on using cached credentials\n",
1116 username);
1117 }
1118}
1119
1120/**
1121 * Send PAM_ERROR_MSG for krb5 errors.
1122 *
1123 * @param pamh PAM handle
1124 * @param ctrl PAM winbind options.
1125 * @param username User in PAM request.
1126 * @param info3_user_flgs Info3 flags containing logon type bits.
1127 *
1128 * @return void.
1129 */
1130
1131static void _pam_warn_krb5_failure(struct pwb_context *ctx,
1132 const char *username,
1133 uint32_t info3_user_flgs)
1134{
1135 if (PAM_WB_KRB5_CLOCK_SKEW(info3_user_flgs)) {
1136 _make_remark(ctx, PAM_ERROR_MSG,
1137 "Failed to establish your Kerberos Ticket cache "
1138 "due time differences\n"
1139 "with the domain controller. "
1140 "Please verify the system time.\n");
1141 _pam_log_debug(ctx, LOG_DEBUG,
1142 "User %s: Clock skew when getting Krb5 TGT\n",
1143 username);
1144 }
1145}
1146
1147/**
1148 * Compose Password Restriction String for a PAM_ERROR_MSG conversation.
1149 *
1150 * @param response The struct winbindd_response.
1151 *
1152 * @return string (caller needs to free).
1153 */
1154
1155static char *_pam_compose_pwd_restriction_string(struct winbindd_response *response)
1156{
1157 char *str = NULL;
1158 size_t offset = 0, ret = 0, str_size = 1024;
1159
1160 str = (char *)malloc(str_size);
1161 if (!str) {
1162 return NULL;
1163 }
1164
1165 memset(str, '\0', str_size);
1166
1167 offset = snprintf(str, str_size, "Your password ");
1168 if (offset == -1) {
1169 goto failed;
1170 }
1171
1172 if (response->data.auth.policy.min_length_password > 0) {
1173 ret = snprintf(str+offset, str_size-offset,
1174 "must be at least %d characters; ",
1175 response->data.auth.policy.min_length_password);
1176 if (ret == -1) {
1177 goto failed;
1178 }
1179 offset += ret;
1180 }
1181
1182 if (response->data.auth.policy.password_history > 0) {
1183 ret = snprintf(str+offset, str_size-offset,
1184 "cannot repeat any of your previous %d "
1185 "passwords; ",
1186 response->data.auth.policy.password_history);
1187 if (ret == -1) {
1188 goto failed;
1189 }
1190 offset += ret;
1191 }
1192
1193 if (response->data.auth.policy.password_properties &
1194 DOMAIN_PASSWORD_COMPLEX) {
1195 ret = snprintf(str+offset, str_size-offset,
1196 "must contain capitals, numerals "
1197 "or punctuation; "
1198 "and cannot contain your account "
1199 "or full name; ");
1200 if (ret == -1) {
1201 goto failed;
1202 }
1203 offset += ret;
1204 }
1205
1206 ret = snprintf(str+offset, str_size-offset,
1207 "Please type a different password. "
1208 "Type a password which meets these requirements in "
1209 "both text boxes.");
1210 if (ret == -1) {
1211 goto failed;
1212 }
1213
1214 return str;
1215
1216 failed:
1217 SAFE_FREE(str);
1218 return NULL;
1219}
1220
1221/* talk to winbindd */
1222static int winbind_auth_request(struct pwb_context *ctx,
1223 const char *user,
1224 const char *pass,
1225 const char *member,
1226 const char *cctype,
1227 const int warn_pwd_expire,
1228 struct winbindd_response *p_response,
1229 time_t *pwd_last_set,
1230 char **user_ret)
1231{
1232 struct winbindd_request request;
1233 struct winbindd_response response;
1234 int ret;
1235 bool already_expired = false;
1236
1237 ZERO_STRUCT(request);
1238 ZERO_STRUCT(response);
1239
1240 if (pwd_last_set) {
1241 *pwd_last_set = 0;
1242 }
1243
1244 strncpy(request.data.auth.user, user,
1245 sizeof(request.data.auth.user)-1);
1246
1247 strncpy(request.data.auth.pass, pass,
1248 sizeof(request.data.auth.pass)-1);
1249
1250 request.data.auth.krb5_cc_type[0] = '\0';
1251 request.data.auth.uid = -1;
1252
1253 request.flags = WBFLAG_PAM_INFO3_TEXT | WBFLAG_PAM_GET_PWD_POLICY;
1254
1255 /* Krb5 auth always has to go against the KDC of the user's realm */
1256
1257 if (ctx->ctrl & WINBIND_KRB5_AUTH) {
1258 request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
1259 }
1260
1261 if (ctx->ctrl & (WINBIND_KRB5_AUTH|WINBIND_CACHED_LOGIN)) {
1262 struct passwd *pwd = NULL;
1263
1264 pwd = getpwnam(user);
1265 if (pwd == NULL) {
1266 return PAM_USER_UNKNOWN;
1267 }
1268 request.data.auth.uid = pwd->pw_uid;
1269 }
1270
1271 if (ctx->ctrl & WINBIND_KRB5_AUTH) {
1272
1273 _pam_log_debug(ctx, LOG_DEBUG,
1274 "enabling krb5 login flag\n");
1275
1276 request.flags |= WBFLAG_PAM_KRB5 |
1277 WBFLAG_PAM_FALLBACK_AFTER_KRB5;
1278 }
1279
1280 if (ctx->ctrl & WINBIND_CACHED_LOGIN) {
1281 _pam_log_debug(ctx, LOG_DEBUG,
1282 "enabling cached login flag\n");
1283 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
1284 }
1285
1286 if (user_ret) {
1287 *user_ret = NULL;
1288 request.flags |= WBFLAG_PAM_UNIX_NAME;
1289 }
1290
1291 if (cctype != NULL) {
1292 strncpy(request.data.auth.krb5_cc_type, cctype,
1293 sizeof(request.data.auth.krb5_cc_type) - 1);
1294 _pam_log_debug(ctx, LOG_DEBUG,
1295 "enabling request for a %s krb5 ccache\n",
1296 cctype);
1297 }
1298
1299 request.data.auth.require_membership_of_sid[0] = '\0';
1300
1301 if (member != NULL) {
1302
1303 if (!winbind_name_list_to_sid_string_list(ctx, user,
1304 member,
1305 request.data.auth.require_membership_of_sid,
1306 sizeof(request.data.auth.require_membership_of_sid))) {
1307
1308 _pam_log_debug(ctx, LOG_ERR,
1309 "failed to serialize membership of sid "
1310 "\"%s\"\n", member);
1311 return PAM_AUTH_ERR;
1312 }
1313 }
1314
1315 ret = pam_winbind_request_log(ctx, WINBINDD_PAM_AUTH,
1316 &request, &response, user);
1317
1318 if (pwd_last_set) {
1319 *pwd_last_set = response.data.auth.info3.pass_last_set_time;
1320 }
1321
1322 if (p_response) {
1323 /* We want to process the response in the caller. */
1324 *p_response = response;
1325 return ret;
1326 }
1327
1328 if (ret) {
1329 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1330 "NT_STATUS_PASSWORD_EXPIRED");
1331 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1332 "NT_STATUS_PASSWORD_MUST_CHANGE");
1333 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1334 "NT_STATUS_INVALID_WORKSTATION");
1335 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1336 "NT_STATUS_INVALID_LOGON_HOURS");
1337 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1338 "NT_STATUS_ACCOUNT_EXPIRED");
1339 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1340 "NT_STATUS_ACCOUNT_DISABLED");
1341 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1342 "NT_STATUS_ACCOUNT_LOCKED_OUT");
1343 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1344 "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT");
1345 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1346 "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT");
1347 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1348 "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT");
1349 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1350 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
1351 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1352 "NT_STATUS_NO_LOGON_SERVERS");
1353 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1354 "NT_STATUS_WRONG_PASSWORD");
1355 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1356 "NT_STATUS_ACCESS_DENIED");
1357 }
1358
1359 if (ret == PAM_SUCCESS) {
1360
1361 /* warn a user if the password is about to expire soon */
1362 _pam_warn_password_expiry(ctx, &response,
1363 warn_pwd_expire,
1364 &already_expired);
1365
1366 if (already_expired == true) {
1367 SMB_TIME_T last_set;
1368 last_set = response.data.auth.info3.pass_last_set_time;
1369 _pam_log_debug(ctx, LOG_DEBUG,
1370 "Password has expired "
1371 "(Password was last set: %lld, "
1372 "the policy says it should expire here "
1373 "%lld (now it's: %lu))\n",
1374 (long long int)last_set,
1375 (long long int)last_set +
1376 response.data.auth.policy.expire,
1377 time(NULL));
1378
1379 return PAM_AUTHTOK_EXPIRED;
1380 }
1381
1382 /* inform about logon type */
1383 _pam_warn_logon_type(ctx, user,
1384 response.data.auth.info3.user_flgs);
1385
1386 /* inform about krb5 failures */
1387 _pam_warn_krb5_failure(ctx, user,
1388 response.data.auth.info3.user_flgs);
1389
1390 /* set some info3 info for other modules in the stack */
1391 _pam_set_data_info3(ctx, &response);
1392
1393 /* put krb5ccname into env */
1394 _pam_setup_krb5_env(ctx, response.data.auth.krb5ccname);
1395
1396 /* If winbindd returned a username, return the pointer to it
1397 * here. */
1398 if (user_ret && response.data.auth.unix_username[0]) {
1399 /* We have to trust it's a null terminated string. */
1400 *user_ret = strndup(response.data.auth.unix_username,
1401 sizeof(response.data.auth.unix_username) - 1);
1402 }
1403 }
1404
1405 return ret;
1406}
1407
1408/* talk to winbindd */
1409static int winbind_chauthtok_request(struct pwb_context *ctx,
1410 const char *user,
1411 const char *oldpass,
1412 const char *newpass,
1413 time_t pwd_last_set)
1414{
1415 struct winbindd_request request;
1416 struct winbindd_response response;
1417 int ret;
1418
1419 ZERO_STRUCT(request);
1420 ZERO_STRUCT(response);
1421
1422 if (request.data.chauthtok.user == NULL) {
1423 return -2;
1424 }
1425
1426 strncpy(request.data.chauthtok.user, user,
1427 sizeof(request.data.chauthtok.user) - 1);
1428
1429 if (oldpass != NULL) {
1430 strncpy(request.data.chauthtok.oldpass, oldpass,
1431 sizeof(request.data.chauthtok.oldpass) - 1);
1432 } else {
1433 request.data.chauthtok.oldpass[0] = '\0';
1434 }
1435
1436 if (newpass != NULL) {
1437 strncpy(request.data.chauthtok.newpass, newpass,
1438 sizeof(request.data.chauthtok.newpass) - 1);
1439 } else {
1440 request.data.chauthtok.newpass[0] = '\0';
1441 }
1442
1443 if (ctx->ctrl & WINBIND_KRB5_AUTH) {
1444 request.flags = WBFLAG_PAM_KRB5 |
1445 WBFLAG_PAM_CONTACT_TRUSTDOM;
1446 }
1447
1448 if (ctx->ctrl & WINBIND_CACHED_LOGIN) {
1449 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
1450 }
1451
1452 ret = pam_winbind_request_log(ctx, WINBINDD_PAM_CHAUTHTOK,
1453 &request, &response, user);
1454
1455 if (ret == PAM_SUCCESS) {
1456 return ret;
1457 }
1458
1459 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1460 "NT_STATUS_BACKUP_CONTROLLER");
1461 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1462 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
1463 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1464 "NT_STATUS_NO_LOGON_SERVERS");
1465 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1466 "NT_STATUS_ACCESS_DENIED");
1467
1468 /* TODO: tell the min pwd length ? */
1469 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1470 "NT_STATUS_PWD_TOO_SHORT");
1471
1472 /* TODO: tell the minage ? */
1473 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1474 "NT_STATUS_PWD_TOO_RECENT");
1475
1476 /* TODO: tell the history length ? */
1477 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1478 "NT_STATUS_PWD_HISTORY_CONFLICT");
1479
1480 if (!strcasecmp(response.data.auth.nt_status_string,
1481 "NT_STATUS_PASSWORD_RESTRICTION")) {
1482
1483 char *pwd_restriction_string = NULL;
1484 SMB_TIME_T min_pwd_age;
1485 uint32_t reject_reason = response.data.auth.reject_reason;
1486 min_pwd_age = response.data.auth.policy.min_passwordage;
1487
1488 /* FIXME: avoid to send multiple PAM messages after another */
1489 switch (reject_reason) {
1490 case -1:
1491 break;
1492 case SAMR_REJECT_OTHER:
1493 if ((min_pwd_age > 0) &&
1494 (pwd_last_set + min_pwd_age > time(NULL))) {
1495 PAM_WB_REMARK_DIRECT(ctx,
1496 "NT_STATUS_PWD_TOO_RECENT");
1497 }
1498 break;
1499 case SAMR_REJECT_TOO_SHORT:
1500 PAM_WB_REMARK_DIRECT(ctx,
1501 "NT_STATUS_PWD_TOO_SHORT");
1502 break;
1503 case SAMR_REJECT_IN_HISTORY:
1504 PAM_WB_REMARK_DIRECT(ctx,
1505 "NT_STATUS_PWD_HISTORY_CONFLICT");
1506 break;
1507 case SAMR_REJECT_COMPLEXITY:
1508 _make_remark(ctx, PAM_ERROR_MSG,
1509 "Password does not meet "
1510 "complexity requirements");
1511 break;
1512 default:
1513 _pam_log_debug(ctx, LOG_DEBUG,
1514 "unknown password change "
1515 "reject reason: %d",
1516 reject_reason);
1517 break;
1518 }
1519
1520 pwd_restriction_string =
1521 _pam_compose_pwd_restriction_string(&response);
1522 if (pwd_restriction_string) {
1523 _make_remark(ctx, PAM_ERROR_MSG,
1524 pwd_restriction_string);
1525 SAFE_FREE(pwd_restriction_string);
1526 }
1527 }
1528
1529 return ret;
1530}
1531
1532/*
1533 * Checks if a user has an account
1534 *
1535 * return values:
1536 * 1 = User not found
1537 * 0 = OK
1538 * -1 = System error
1539 */
1540static int valid_user(struct pwb_context *ctx,
1541 const char *user)
1542{
1543 /* check not only if the user is available over NSS calls, also make
1544 * sure it's really a winbind user, this is important when stacking PAM
1545 * modules in the 'account' or 'password' facility. */
1546
1547 struct passwd *pwd = NULL;
1548 struct winbindd_request request;
1549 struct winbindd_response response;
1550 int ret;
1551
1552 ZERO_STRUCT(request);
1553 ZERO_STRUCT(response);
1554
1555 pwd = getpwnam(user);
1556 if (pwd == NULL) {
1557 return 1;
1558 }
1559
1560 strncpy(request.data.username, user,
1561 sizeof(request.data.username) - 1);
1562
1563 ret = pam_winbind_request_log(ctx, WINBINDD_GETPWNAM,
1564 &request, &response, user);
1565
1566 switch (ret) {
1567 case PAM_USER_UNKNOWN:
1568 return 1;
1569 case PAM_SUCCESS:
1570 return 0;
1571 default:
1572 break;
1573 }
1574 return -1;
1575}
1576
1577static char *_pam_delete(register char *xx)
1578{
1579 _pam_overwrite(xx);
1580 _pam_drop(xx);
1581 return NULL;
1582}
1583
1584/*
1585 * obtain a password from the user
1586 */
1587
1588static int _winbind_read_password(struct pwb_context *ctx,
1589 unsigned int ctrl,
1590 const char *comment,
1591 const char *prompt1,
1592 const char *prompt2,
1593 const char **pass)
1594{
1595 int authtok_flag;
1596 int retval;
1597 const char *item;
1598 char *token;
1599
1600 _pam_log(ctx, LOG_DEBUG, "getting password (0x%08x)", ctrl);
1601
1602 /*
1603 * make sure nothing inappropriate gets returned
1604 */
1605
1606 *pass = token = NULL;
1607
1608 /*
1609 * which authentication token are we getting?
1610 */
1611
1612 if (on(WINBIND__OLD_PASSWORD, ctrl)) {
1613 authtok_flag = PAM_OLDAUTHTOK;
1614 } else {
1615 authtok_flag = PAM_AUTHTOK;
1616 }
1617
1618 /*
1619 * should we obtain the password from a PAM item ?
1620 */
1621
1622 if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) ||
1623 on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
1624 retval = _pam_get_item(ctx->pamh, authtok_flag, &item);
1625 if (retval != PAM_SUCCESS) {
1626 /* very strange. */
1627 _pam_log(ctx, LOG_ALERT,
1628 "pam_get_item returned error "
1629 "to unix-read-password");
1630 return retval;
1631 } else if (item != NULL) { /* we have a password! */
1632 *pass = item;
1633 item = NULL;
1634 _pam_log(ctx, LOG_DEBUG,
1635 "pam_get_item returned a password");
1636 return PAM_SUCCESS;
1637 } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
1638 return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
1639 } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
1640 && off(WINBIND__OLD_PASSWORD, ctrl)) {
1641 return PAM_AUTHTOK_RECOVER_ERR;
1642 }
1643 }
1644 /*
1645 * getting here implies we will have to get the password from the
1646 * user directly.
1647 */
1648
1649 {
1650 struct pam_message msg[3], *pmsg[3];
1651 struct pam_response *resp;
1652 int i, replies;
1653
1654 /* prepare to converse */
1655
1656 if (comment != NULL && off(ctrl, WINBIND_SILENT)) {
1657 pmsg[0] = &msg[0];
1658 msg[0].msg_style = PAM_TEXT_INFO;
1659 msg[0].msg = discard_const_p(char, comment);
1660 i = 1;
1661 } else {
1662 i = 0;
1663 }
1664
1665 pmsg[i] = &msg[i];
1666 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
1667 msg[i++].msg = discard_const_p(char, prompt1);
1668 replies = 1;
1669
1670 if (prompt2 != NULL) {
1671 pmsg[i] = &msg[i];
1672 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
1673 msg[i++].msg = discard_const_p(char, prompt2);
1674 ++replies;
1675 }
1676 /* so call the conversation expecting i responses */
1677 resp = NULL;
1678 retval = converse(ctx->pamh, i, pmsg, &resp);
1679 if (resp == NULL) {
1680 if (retval == PAM_SUCCESS) {
1681 retval = PAM_AUTHTOK_RECOVER_ERR;
1682 }
1683 goto done;
1684 }
1685 if (retval != PAM_SUCCESS) {
1686 _pam_drop_reply(resp, i);
1687 goto done;
1688 }
1689
1690 /* interpret the response */
1691
1692 token = x_strdup(resp[i - replies].resp);
1693 if (!token) {
1694 _pam_log(ctx, LOG_NOTICE,
1695 "could not recover "
1696 "authentication token");
1697 retval = PAM_AUTHTOK_RECOVER_ERR;
1698 goto done;
1699 }
1700
1701 if (replies == 2) {
1702 /* verify that password entered correctly */
1703 if (!resp[i - 1].resp ||
1704 strcmp(token, resp[i - 1].resp)) {
1705 _pam_delete(token); /* mistyped */
1706 retval = PAM_AUTHTOK_RECOVER_ERR;
1707 _make_remark(ctx, PAM_ERROR_MSG,
1708 MISTYPED_PASS);
1709 }
1710 }
1711
1712 /*
1713 * tidy up the conversation (resp_retcode) is ignored
1714 * -- what is it for anyway? AGM
1715 */
1716 _pam_drop_reply(resp, i);
1717 }
1718
1719 done:
1720 if (retval != PAM_SUCCESS) {
1721 _pam_log_debug(ctx, LOG_DEBUG,
1722 "unable to obtain a password");
1723 return retval;
1724 }
1725 /* 'token' is the entered password */
1726
1727 /* we store this password as an item */
1728
1729 retval = pam_set_item(ctx->pamh, authtok_flag, token);
1730 _pam_delete(token); /* clean it up */
1731 if (retval != PAM_SUCCESS ||
1732 (retval = _pam_get_item(ctx->pamh, authtok_flag, &item)) != PAM_SUCCESS) {
1733
1734 _pam_log(ctx, LOG_CRIT, "error manipulating password");
1735 return retval;
1736
1737 }
1738
1739 *pass = item;
1740 item = NULL; /* break link to password */
1741
1742 return PAM_SUCCESS;
1743}
1744
1745static const char *get_conf_item_string(struct pwb_context *ctx,
1746 const char *item,
1747 int config_flag)
1748{
1749 int i = 0;
1750 const char *parm_opt = NULL;
1751
1752 if (!(ctx->ctrl & config_flag)) {
1753 goto out;
1754 }
1755
1756 /* let the pam opt take precedence over the pam_winbind.conf option */
1757 for (i=0; i<ctx->argc; i++) {
1758
1759 if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
1760 char *p;
1761
1762 if ((p = strchr(ctx->argv[i], '=')) == NULL) {
1763 _pam_log(ctx, LOG_INFO,
1764 "no \"=\" delimiter for \"%s\" found\n",
1765 item);
1766 goto out;
1767 }
1768 _pam_log_debug(ctx, LOG_INFO,
1769 "PAM config: %s '%s'\n", item, p+1);
1770 return p + 1;
1771 }
1772 }
1773
1774 if (ctx->dict) {
1775 char *key = NULL;
1776
1777 if (!asprintf(&key, "global:%s", item)) {
1778 goto out;
1779 }
1780
1781 parm_opt = iniparser_getstr(ctx->dict, key);
1782 SAFE_FREE(key);
1783
1784 _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%s'\n",
1785 item, parm_opt);
1786 }
1787out:
1788 return parm_opt;
1789}
1790
1791static int get_config_item_int(struct pwb_context *ctx,
1792 const char *item,
1793 int config_flag)
1794{
1795 int i, parm_opt = -1;
1796
1797 if (!(ctx->ctrl & config_flag)) {
1798 goto out;
1799 }
1800
1801 /* let the pam opt take precedence over the pam_winbind.conf option */
1802 for (i = 0; i < ctx->argc; i++) {
1803
1804 if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
1805 char *p;
1806
1807 if ((p = strchr(ctx->argv[i], '=')) == NULL) {
1808 _pam_log(ctx, LOG_INFO,
1809 "no \"=\" delimiter for \"%s\" found\n",
1810 item);
1811 goto out;
1812 }
1813 parm_opt = atoi(p + 1);
1814 _pam_log_debug(ctx, LOG_INFO,
1815 "PAM config: %s '%d'\n",
1816 item, parm_opt);
1817 return parm_opt;
1818 }
1819 }
1820
1821 if (ctx->dict) {
1822 char *key = NULL;
1823
1824 if (!asprintf(&key, "global:%s", item)) {
1825 goto out;
1826 }
1827
1828 parm_opt = iniparser_getint(ctx->dict, key, -1);
1829 SAFE_FREE(key);
1830
1831 _pam_log_debug(ctx, LOG_INFO,
1832 "CONFIG file: %s '%d'\n",
1833 item, parm_opt);
1834 }
1835out:
1836 return parm_opt;
1837}
1838
1839static const char *get_krb5_cc_type_from_config(struct pwb_context *ctx)
1840{
1841 return get_conf_item_string(ctx, "krb5_ccache_type",
1842 WINBIND_KRB5_CCACHE_TYPE);
1843}
1844
1845static const char *get_member_from_config(struct pwb_context *ctx)
1846{
1847 const char *ret = NULL;
1848 ret = get_conf_item_string(ctx, "require_membership_of",
1849 WINBIND_REQUIRED_MEMBERSHIP);
1850 if (ret) {
1851 return ret;
1852 }
1853 return get_conf_item_string(ctx, "require-membership-of",
1854 WINBIND_REQUIRED_MEMBERSHIP);
1855}
1856
1857static int get_warn_pwd_expire_from_config(struct pwb_context *ctx)
1858{
1859 int ret;
1860 ret = get_config_item_int(ctx, "warn_pwd_expire",
1861 WINBIND_WARN_PWD_EXPIRE);
1862 /* no or broken setting */
1863 if (ret <= 0) {
1864 return DEFAULT_DAYS_TO_WARN_BEFORE_PWD_EXPIRES;
1865 }
1866 return ret;
1867}
1868
1869/**
1870 * Retrieve the winbind separator.
1871 *
1872 * @param pamh PAM handle
1873 * @param ctrl PAM winbind options.
1874 *
1875 * @return string separator character. NULL on failure.
1876 */
1877
1878static char winbind_get_separator(struct pwb_context *ctx)
1879{
1880 struct winbindd_request request;
1881 struct winbindd_response response;
1882
1883 ZERO_STRUCT(request);
1884 ZERO_STRUCT(response);
1885
1886 if (pam_winbind_request_log(ctx, WINBINDD_INFO,
1887 &request, &response, NULL)) {
1888 return '\0';
1889 }
1890
1891 return response.data.info.winbind_separator;
1892}
1893
1894/**
1895 * Convert a upn to a name.
1896 *
1897 * @param pamh PAM handle
1898 * @param ctrl PAM winbind options.
1899 * @param upn USer UPN to be trabslated.
1900 *
1901 * @return converted name. NULL pointer on failure. Caller needs to free.
1902 */
1903
1904static char* winbind_upn_to_username(struct pwb_context *ctx,
1905 const char *upn)
1906{
1907 struct winbindd_request req;
1908 struct winbindd_response resp;
1909 int retval;
1910 char *account_name;
1911 int account_name_len;
1912 char sep;
1913 char *p;
1914 char *name;
1915 char *domain;
1916
1917 /* This cannot work when the winbind separator = @ */
1918
1919 sep = winbind_get_separator(ctx);
1920 if (!sep || sep == '@') {
1921 return NULL;
1922 }
1923
1924 name = strdup(upn);
1925 if (!name) {
1926 return NULL;
1927 }
1928 if ((p = strchr(name, '@')) != NULL) {
1929 *p = 0;
1930 domain = p + 1;
1931 }
1932
1933 /* Convert the UPN to a SID */
1934
1935 ZERO_STRUCT(req);
1936 ZERO_STRUCT(resp);
1937
1938 strncpy(req.data.name.dom_name, domain,
1939 sizeof(req.data.name.dom_name) - 1);
1940 strncpy(req.data.name.name, name,
1941 sizeof(req.data.name.name) - 1);
1942 retval = pam_winbind_request_log(ctx, WINBINDD_LOOKUPNAME,
1943 &req, &resp, upn);
1944 if (retval != PAM_SUCCESS) {
1945 return NULL;
1946 }
1947
1948 /* Convert the the SID back to the sAMAccountName */
1949
1950 ZERO_STRUCT(req);
1951 strncpy(req.data.sid, resp.data.sid.sid, sizeof(req.data.sid)-1);
1952 ZERO_STRUCT(resp);
1953 retval = pam_winbind_request_log(ctx, WINBINDD_LOOKUPSID,
1954 &req, &resp, upn);
1955 if (retval != PAM_SUCCESS) {
1956 return NULL;
1957 }
1958
1959 account_name_len = asprintf(&account_name, "%s\\%s",
1960 resp.data.name.dom_name,
1961 resp.data.name.name);
1962 SAFE_FREE(name);
1963
1964 return account_name;
1965}
1966
1967PAM_EXTERN
1968int pam_sm_authenticate(pam_handle_t *pamh, int flags,
1969 int argc, const char **argv)
1970{
1971 const char *username;
1972 const char *password;
1973 const char *member = NULL;
1974 const char *cctype = NULL;
1975 int warn_pwd_expire;
1976 int retval = PAM_AUTH_ERR;
1977 char *username_ret = NULL;
1978 char *new_authtok_required = NULL;
1979 char *real_username = NULL;
1980 struct pwb_context *ctx = NULL;
1981
1982 retval = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
1983 if (retval) {
1984 goto out;
1985 }
1986
1987 _PAM_LOG_FUNCTION_ENTER("pam_sm_authenticate", ctx);
1988
1989 /* Get the username */
1990 retval = pam_get_user(pamh, &username, NULL);
1991 if ((retval != PAM_SUCCESS) || (!username)) {
1992 _pam_log_debug(ctx, LOG_DEBUG,
1993 "can not get the username");
1994 retval = PAM_SERVICE_ERR;
1995 goto out;
1996 }
1997
1998
1999#if defined(AIX)
2000 /* Decode the user name since AIX does not support logn user
2001 names by default. The name is encoded as _#uid. */
2002
2003 if (username[0] == '_') {
2004 uid_t id = atoi(&username[1]);
2005 struct passwd *pw = NULL;
2006
2007 if ((id!=0) && ((pw = getpwuid(id)) != NULL)) {
2008 real_username = strdup(pw->pw_name);
2009 }
2010 }
2011#endif
2012
2013 if (!real_username) {
2014 /* Just making a copy of the username we got from PAM */
2015 if ((real_username = strdup(username)) == NULL) {
2016 _pam_log_debug(ctx, LOG_DEBUG,
2017 "memory allocation failure when copying "
2018 "username");
2019 retval = PAM_SERVICE_ERR;
2020 goto out;
2021 }
2022 }
2023
2024 /* Maybe this was a UPN */
2025
2026 if (strchr(real_username, '@') != NULL) {
2027 char *samaccountname = NULL;
2028
2029 samaccountname = winbind_upn_to_username(ctx,
2030 real_username);
2031 if (samaccountname) {
2032 free(real_username);
2033 real_username = samaccountname;
2034 }
2035 }
2036
2037 retval = _winbind_read_password(ctx, ctx->ctrl, NULL,
2038 "Password: ", NULL,
2039 &password);
2040
2041 if (retval != PAM_SUCCESS) {
2042 _pam_log(ctx, LOG_ERR,
2043 "Could not retrieve user's password");
2044 retval = PAM_AUTHTOK_ERR;
2045 goto out;
2046 }
2047
2048 /* Let's not give too much away in the log file */
2049
2050#ifdef DEBUG_PASSWORD
2051 _pam_log_debug(ctx, LOG_INFO,
2052 "Verify user '%s' with password '%s'",
2053 real_username, password);
2054#else
2055 _pam_log_debug(ctx, LOG_INFO,
2056 "Verify user '%s'", real_username);
2057#endif
2058
2059 member = get_member_from_config(ctx);
2060 cctype = get_krb5_cc_type_from_config(ctx);
2061 warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
2062
2063 /* Now use the username to look up password */
2064 retval = winbind_auth_request(ctx, real_username, password,
2065 member, cctype, warn_pwd_expire, NULL,
2066 NULL, &username_ret);
2067
2068 if (retval == PAM_NEW_AUTHTOK_REQD ||
2069 retval == PAM_AUTHTOK_EXPIRED) {
2070
2071 char *new_authtok_required_during_auth = NULL;
2072
2073 if (!asprintf(&new_authtok_required, "%d", retval)) {
2074 retval = PAM_BUF_ERR;
2075 goto out;
2076 }
2077
2078 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
2079 new_authtok_required,
2080 _pam_winbind_cleanup_func);
2081
2082 retval = PAM_SUCCESS;
2083
2084 if (!asprintf(&new_authtok_required_during_auth, "%d", true)) {
2085 retval = PAM_BUF_ERR;
2086 goto out;
2087 }
2088
2089 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
2090 new_authtok_required_during_auth,
2091 _pam_winbind_cleanup_func);
2092
2093 goto out;
2094 }
2095
2096out:
2097 if (username_ret) {
2098 pam_set_item (pamh, PAM_USER, username_ret);
2099 _pam_log_debug(ctx, LOG_INFO,
2100 "Returned user was '%s'", username_ret);
2101 free(username_ret);
2102 }
2103
2104 if (real_username) {
2105 free(real_username);
2106 }
2107
2108 if (!new_authtok_required) {
2109 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, NULL, NULL);
2110 }
2111
2112 if (retval != PAM_SUCCESS) {
2113 _pam_free_data_info3(pamh);
2114 }
2115
2116 _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", ctx, retval);
2117
2118 _pam_winbind_free_context(ctx);
2119
2120 return retval;
2121}
2122
2123PAM_EXTERN
2124int pam_sm_setcred(pam_handle_t *pamh, int flags,
2125 int argc, const char **argv)
2126{
2127 int ret = PAM_SYSTEM_ERR;
2128 struct pwb_context *ctx = NULL;
2129
2130 ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2131 if (ret) {
2132 goto out;
2133 }
2134
2135 _PAM_LOG_FUNCTION_ENTER("pam_sm_setcred", ctx);
2136
2137 switch (flags & ~PAM_SILENT) {
2138
2139 case PAM_DELETE_CRED:
2140 ret = pam_sm_close_session(pamh, flags, argc, argv);
2141 break;
2142 case PAM_REFRESH_CRED:
2143 _pam_log_debug(ctx, LOG_WARNING,
2144 "PAM_REFRESH_CRED not implemented");
2145 ret = PAM_SUCCESS;
2146 break;
2147 case PAM_REINITIALIZE_CRED:
2148 _pam_log_debug(ctx, LOG_WARNING,
2149 "PAM_REINITIALIZE_CRED not implemented");
2150 ret = PAM_SUCCESS;
2151 break;
2152 case PAM_ESTABLISH_CRED:
2153 _pam_log_debug(ctx, LOG_WARNING,
2154 "PAM_ESTABLISH_CRED not implemented");
2155 ret = PAM_SUCCESS;
2156 break;
2157 default:
2158 ret = PAM_SYSTEM_ERR;
2159 break;
2160 }
2161
2162 out:
2163
2164 _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", ctx, ret);
2165
2166 _pam_winbind_free_context(ctx);
2167
2168 return ret;
2169}
2170
2171/*
2172 * Account management. We want to verify that the account exists
2173 * before returning PAM_SUCCESS
2174 */
2175PAM_EXTERN
2176int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
2177 int argc, const char **argv)
2178{
2179 const char *username;
2180 int ret = PAM_USER_UNKNOWN;
2181 void *tmp = NULL;
2182 struct pwb_context *ctx = NULL;
2183
2184 ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2185 if (ret) {
2186 goto out;
2187 }
2188
2189 _PAM_LOG_FUNCTION_ENTER("pam_sm_acct_mgmt", ctx);
2190
2191
2192 /* Get the username */
2193 ret = pam_get_user(pamh, &username, NULL);
2194 if ((ret != PAM_SUCCESS) || (!username)) {
2195 _pam_log_debug(ctx, LOG_DEBUG,
2196 "can not get the username");
2197 ret = PAM_SERVICE_ERR;
2198 goto out;
2199 }
2200
2201 /* Verify the username */
2202 ret = valid_user(ctx, username);
2203 switch (ret) {
2204 case -1:
2205 /* some sort of system error. The log was already printed */
2206 ret = PAM_SERVICE_ERR;
2207 goto out;
2208 case 1:
2209 /* the user does not exist */
2210 _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
2211 username);
2212 if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
2213 ret = PAM_IGNORE;
2214 goto out;
2215 }
2216 ret = PAM_USER_UNKNOWN;
2217 goto out;
2218 case 0:
2219 pam_get_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
2220 (const void **)&tmp);
2221 if (tmp != NULL) {
2222 ret = atoi((const char *)tmp);
2223 switch (ret) {
2224 case PAM_AUTHTOK_EXPIRED:
2225 /* fall through, since new token is required in this case */
2226 case PAM_NEW_AUTHTOK_REQD:
2227 _pam_log(ctx, LOG_WARNING,
2228 "pam_sm_acct_mgmt success but %s is set",
2229 PAM_WINBIND_NEW_AUTHTOK_REQD);
2230 _pam_log(ctx, LOG_NOTICE,
2231 "user '%s' needs new password",
2232 username);
2233 /* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */
2234 ret = PAM_NEW_AUTHTOK_REQD;
2235 goto out;
2236 default:
2237 _pam_log(ctx, LOG_WARNING,
2238 "pam_sm_acct_mgmt success");
2239 _pam_log(ctx, LOG_NOTICE,
2240 "user '%s' granted access", username);
2241 ret = PAM_SUCCESS;
2242 goto out;
2243 }
2244 }
2245
2246 /* Otherwise, the authentication looked good */
2247 _pam_log(ctx, LOG_NOTICE,
2248 "user '%s' granted access", username);
2249 ret = PAM_SUCCESS;
2250 goto out;
2251 default:
2252 /* we don't know anything about this return value */
2253 _pam_log(ctx, LOG_ERR,
2254 "internal module error (ret = %d, user = '%s')",
2255 ret, username);
2256 ret = PAM_SERVICE_ERR;
2257 goto out;
2258 }
2259
2260 /* should not be reached */
2261 ret = PAM_IGNORE;
2262
2263 out:
2264
2265 _PAM_LOG_FUNCTION_LEAVE("pam_sm_acct_mgmt", ctx, ret);
2266
2267 _pam_winbind_free_context(ctx);
2268
2269 return ret;
2270}
2271
2272PAM_EXTERN
2273int pam_sm_open_session(pam_handle_t *pamh, int flags,
2274 int argc, const char **argv)
2275{
2276 int ret = PAM_SYSTEM_ERR;
2277 struct pwb_context *ctx = NULL;
2278
2279 ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2280 if (ret) {
2281 goto out;
2282 }
2283
2284 _PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", ctx);
2285
2286 ret = PAM_SUCCESS;
2287
2288 out:
2289 _PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", ctx, ret);
2290
2291 _pam_winbind_free_context(ctx);
2292
2293 return ret;
2294}
2295
2296PAM_EXTERN
2297int pam_sm_close_session(pam_handle_t *pamh, int flags,
2298 int argc, const char **argv)
2299{
2300 int retval = PAM_SUCCESS;
2301 struct pwb_context *ctx = NULL;
2302
2303 retval = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2304 if (retval) {
2305 goto out;
2306 }
2307
2308 _PAM_LOG_FUNCTION_ENTER("pam_sm_close_session", ctx);
2309
2310 if (!(flags & PAM_DELETE_CRED)) {
2311 retval = PAM_SUCCESS;
2312 goto out;
2313 }
2314
2315 if (ctx->ctrl & WINBIND_KRB5_AUTH) {
2316
2317 /* destroy the ccache here */
2318 struct winbindd_request request;
2319 struct winbindd_response response;
2320 const char *user;
2321 const char *ccname = NULL;
2322 struct passwd *pwd = NULL;
2323
2324 ZERO_STRUCT(request);
2325 ZERO_STRUCT(response);
2326
2327 retval = pam_get_user(pamh, &user, "Username: ");
2328 if (retval) {
2329 _pam_log(ctx, LOG_ERR,
2330 "could not identify user");
2331 goto out;
2332 }
2333
2334 if (user == NULL) {
2335 _pam_log(ctx, LOG_ERR,
2336 "username was NULL!");
2337 retval = PAM_USER_UNKNOWN;
2338 goto out;
2339 }
2340
2341 _pam_log_debug(ctx, LOG_DEBUG,
2342 "username [%s] obtained", user);
2343
2344 ccname = pam_getenv(pamh, "KRB5CCNAME");
2345 if (ccname == NULL) {
2346 _pam_log_debug(ctx, LOG_DEBUG,
2347 "user has no KRB5CCNAME environment");
2348 }
2349
2350 strncpy(request.data.logoff.user, user,
2351 sizeof(request.data.logoff.user) - 1);
2352
2353 if (ccname) {
2354 strncpy(request.data.logoff.krb5ccname, ccname,
2355 sizeof(request.data.logoff.krb5ccname) - 1);
2356 }
2357
2358 pwd = getpwnam(user);
2359 if (pwd == NULL) {
2360 retval = PAM_USER_UNKNOWN;
2361 goto out;
2362 }
2363 request.data.logoff.uid = pwd->pw_uid;
2364
2365 request.flags = WBFLAG_PAM_KRB5 |
2366 WBFLAG_PAM_CONTACT_TRUSTDOM;
2367
2368 retval = pam_winbind_request_log(ctx,
2369 WINBINDD_PAM_LOGOFF,
2370 &request, &response, user);
2371 }
2372
2373out:
2374 /*
2375 * Delete the krb5 ccname variable from the PAM environment
2376 * if it was set by winbind.
2377 */
2378 if ((ctx->ctrl & WINBIND_KRB5_AUTH) && pam_getenv(pamh, "KRB5CCNAME")) {
2379 pam_putenv(pamh, "KRB5CCNAME");
2380 }
2381
2382 _PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", ctx, retval);
2383
2384 _pam_winbind_free_context(ctx);
2385
2386 return retval;
2387}
2388
2389/**
2390 * evaluate whether we need to re-authenticate with kerberos after a
2391 * password change
2392 *
2393 * @param pamh PAM handle
2394 * @param ctrl PAM winbind options.
2395 * @param user The username
2396 *
2397 * @return boolean Returns true if required, false if not.
2398 */
2399
2400static bool _pam_require_krb5_auth_after_chauthtok(struct pwb_context *ctx,
2401 const char *user)
2402{
2403
2404 /* Make sure that we only do this if a) the chauthtok got initiated
2405 * during a logon attempt (authenticate->acct_mgmt->chauthtok) b) any
2406 * later password change via the "passwd" command if done by the user
2407 * itself
2408 * NB. If we login from gdm or xdm and the password expires,
2409 * we change the password, but there is no memory cache.
2410 * Thus, even for passthrough login, we should do the
2411 * authentication again to update memory cache.
2412 * --- BoYang
2413 * */
2414
2415 char *new_authtok_reqd_during_auth = NULL;
2416 struct passwd *pwd = NULL;
2417
2418 _pam_get_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
2419 &new_authtok_reqd_during_auth);
2420 pam_set_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
2421 NULL, NULL);
2422
2423 if (new_authtok_reqd_during_auth) {
2424 return true;
2425 }
2426
2427 pwd = getpwnam(user);
2428 if (!pwd) {
2429 return false;
2430 }
2431
2432 if (getuid() == pwd->pw_uid) {
2433 return true;
2434 }
2435
2436 return false;
2437}
2438
2439
2440PAM_EXTERN
2441int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
2442 int argc, const char **argv)
2443{
2444 unsigned int lctrl;
2445 int ret;
2446 bool cached_login = false;
2447
2448 /* <DO NOT free() THESE> */
2449 const char *user;
2450 char *pass_old, *pass_new;
2451 /* </DO NOT free() THESE> */
2452
2453 char *Announce;
2454
2455 int retry = 0;
2456 char *username_ret = NULL;
2457 struct winbindd_response response;
2458 struct pwb_context *ctx = NULL;
2459
2460 ZERO_STRUCT(response);
2461
2462 ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2463 if (ret) {
2464 goto out;
2465 }
2466
2467 _PAM_LOG_FUNCTION_ENTER("pam_sm_chauthtok", ctx);
2468
2469 cached_login = (ctx->ctrl & WINBIND_CACHED_LOGIN);
2470
2471 /* clearing offline bit for auth */
2472 ctx->ctrl &= ~WINBIND_CACHED_LOGIN;
2473
2474 /*
2475 * First get the name of a user
2476 */
2477 ret = pam_get_user(pamh, &user, "Username: ");
2478 if (ret) {
2479 _pam_log(ctx, LOG_ERR,
2480 "password - could not identify user");
2481 goto out;
2482 }
2483
2484 if (user == NULL) {
2485 _pam_log(ctx, LOG_ERR, "username was NULL!");
2486 ret = PAM_USER_UNKNOWN;
2487 goto out;
2488 }
2489
2490 _pam_log_debug(ctx, LOG_DEBUG, "username [%s] obtained", user);
2491
2492 /* check if this is really a user in winbindd, not only in NSS */
2493 ret = valid_user(ctx, user);
2494 switch (ret) {
2495 case 1:
2496 ret = PAM_USER_UNKNOWN;
2497 goto out;
2498 case -1:
2499 ret = PAM_SYSTEM_ERR;
2500 goto out;
2501 default:
2502 break;
2503 }
2504
2505 /*
2506 * obtain and verify the current password (OLDAUTHTOK) for
2507 * the user.
2508 */
2509
2510 if (flags & PAM_PRELIM_CHECK) {
2511 time_t pwdlastset_prelim = 0;
2512
2513 /* instruct user what is happening */
2514#define greeting "Changing password for "
2515 Announce = (char *) malloc(sizeof(greeting) + strlen(user));
2516 if (Announce == NULL) {
2517 _pam_log(ctx, LOG_CRIT,
2518 "password - out of memory");
2519 ret = PAM_BUF_ERR;
2520 goto out;
2521 }
2522 (void) strcpy(Announce, greeting);
2523 (void) strcpy(Announce + sizeof(greeting) - 1, user);
2524#undef greeting
2525
2526 lctrl = ctx->ctrl | WINBIND__OLD_PASSWORD;
2527 ret = _winbind_read_password(ctx, lctrl,
2528 Announce,
2529 "(current) NT password: ",
2530 NULL,
2531 (const char **) &pass_old);
2532 if (ret != PAM_SUCCESS) {
2533 _pam_log(ctx, LOG_NOTICE,
2534 "password - (old) token not obtained");
2535 goto out;
2536 }
2537
2538 /* verify that this is the password for this user */
2539
2540 ret = winbind_auth_request(ctx, user, pass_old,
2541 NULL, NULL, 0, &response,
2542 &pwdlastset_prelim, NULL);
2543
2544 if (ret != PAM_ACCT_EXPIRED &&
2545 ret != PAM_AUTHTOK_EXPIRED &&
2546 ret != PAM_NEW_AUTHTOK_REQD &&
2547 ret != PAM_SUCCESS) {
2548 pass_old = NULL;
2549 goto out;
2550 }
2551
2552 pam_set_data(pamh, PAM_WINBIND_PWD_LAST_SET,
2553 (void *)pwdlastset_prelim, NULL);
2554
2555 ret = pam_set_item(pamh, PAM_OLDAUTHTOK,
2556 (const void *) pass_old);
2557 pass_old = NULL;
2558 if (ret != PAM_SUCCESS) {
2559 _pam_log(ctx, LOG_CRIT,
2560 "failed to set PAM_OLDAUTHTOK");
2561 }
2562 } else if (flags & PAM_UPDATE_AUTHTOK) {
2563
2564 time_t pwdlastset_update = 0;
2565
2566 /*
2567 * obtain the proposed password
2568 */
2569
2570 /*
2571 * get the old token back.
2572 */
2573
2574 ret = _pam_get_item(pamh, PAM_OLDAUTHTOK, &pass_old);
2575
2576 if (ret != PAM_SUCCESS) {
2577 _pam_log(ctx, LOG_NOTICE,
2578 "user not authenticated");
2579 goto out;
2580 }
2581
2582 lctrl = ctx->ctrl & ~WINBIND_TRY_FIRST_PASS_ARG;
2583
2584 if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
2585 lctrl |= WINBIND_USE_FIRST_PASS_ARG;
2586 }
2587 retry = 0;
2588 ret = PAM_AUTHTOK_ERR;
2589 while ((ret != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
2590 /*
2591 * use_authtok is to force the use of a previously entered
2592 * password -- needed for pluggable password strength checking
2593 */
2594
2595 ret = _winbind_read_password(ctx, lctrl,
2596 NULL,
2597 "Enter new NT password: ",
2598 "Retype new NT password: ",
2599 (const char **)&pass_new);
2600
2601 if (ret != PAM_SUCCESS) {
2602 _pam_log_debug(ctx, LOG_ALERT,
2603 "password - "
2604 "new password not obtained");
2605 pass_old = NULL;/* tidy up */
2606 goto out;
2607 }
2608
2609 /*
2610 * At this point we know who the user is and what they
2611 * propose as their new password. Verify that the new
2612 * password is acceptable.
2613 */
2614
2615 if (pass_new[0] == '\0') {/* "\0" password = NULL */
2616 pass_new = NULL;
2617 }
2618 }
2619
2620 /*
2621 * By reaching here we have approved the passwords and must now
2622 * rebuild the password database file.
2623 */
2624 _pam_get_data(pamh, PAM_WINBIND_PWD_LAST_SET,
2625 &pwdlastset_update);
2626
2627 /*
2628 * if cached creds were enabled, make sure to set the
2629 * WINBIND_CACHED_LOGIN bit here in order to have winbindd
2630 * update the cached creds storage - gd
2631 */
2632 if (cached_login) {
2633 ctx->ctrl |= WINBIND_CACHED_LOGIN;
2634 }
2635
2636 ret = winbind_chauthtok_request(ctx, user, pass_old,
2637 pass_new, pwdlastset_update);
2638 if (ret) {
2639 _pam_overwrite(pass_new);
2640 _pam_overwrite(pass_old);
2641 pass_old = pass_new = NULL;
2642 goto out;
2643 }
2644
2645 if (_pam_require_krb5_auth_after_chauthtok(ctx, user)) {
2646
2647 const char *member = NULL;
2648 const char *cctype = NULL;
2649 int warn_pwd_expire;
2650
2651 member = get_member_from_config(ctx);
2652 cctype = get_krb5_cc_type_from_config(ctx);
2653 warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
2654
2655 /* Keep WINBIND_CACHED_LOGIN bit for
2656 * authentication after changing the password.
2657 * This will update the cached credentials in case
2658 * that winbindd_dual_pam_chauthtok() fails
2659 * to update them.
2660 * --- BoYang
2661 * */
2662
2663 ret = winbind_auth_request(ctx, user, pass_new,
2664 member, cctype, 0, &response,
2665 NULL, &username_ret);
2666 _pam_overwrite(pass_new);
2667 _pam_overwrite(pass_old);
2668 pass_old = pass_new = NULL;
2669
2670 if (ret == PAM_SUCCESS) {
2671
2672 /* warn a user if the password is about to
2673 * expire soon */
2674 _pam_warn_password_expiry(ctx, &response,
2675 warn_pwd_expire,
2676 NULL);
2677
2678 /* set some info3 info for other modules in the
2679 * stack */
2680 _pam_set_data_info3(ctx, &response);
2681
2682 /* put krb5ccname into env */
2683 _pam_setup_krb5_env(ctx,
2684 response.data.auth.krb5ccname);
2685
2686 if (username_ret) {
2687 pam_set_item(pamh, PAM_USER,
2688 username_ret);
2689 _pam_log_debug(ctx, LOG_INFO,
2690 "Returned user was '%s'",
2691 username_ret);
2692 free(username_ret);
2693 }
2694 }
2695
2696 goto out;
2697 }
2698 } else {
2699 ret = PAM_SERVICE_ERR;
2700 }
2701
2702out:
2703
2704 /* Deal with offline errors. */
2705 PAM_WB_REMARK_CHECK_RESPONSE(ctx, response,
2706 "NT_STATUS_NO_LOGON_SERVERS");
2707 PAM_WB_REMARK_CHECK_RESPONSE(ctx, response,
2708 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
2709 PAM_WB_REMARK_CHECK_RESPONSE(ctx, response,
2710 "NT_STATUS_ACCESS_DENIED");
2711
2712 _PAM_LOG_FUNCTION_LEAVE("pam_sm_chauthtok", ctx, ret);
2713
2714 _pam_winbind_free_context(ctx);
2715
2716 return ret;
2717}
2718
2719#ifdef PAM_STATIC
2720
2721/* static module data */
2722
2723struct pam_module _pam_winbind_modstruct = {
2724 MODULE_NAME,
2725 pam_sm_authenticate,
2726 pam_sm_setcred,
2727 pam_sm_acct_mgmt,
2728 pam_sm_open_session,
2729 pam_sm_close_session,
2730 pam_sm_chauthtok
2731};
2732
2733#endif
2734
2735/*
2736 * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
2737 * Copyright (c) Tim Potter <tpot@samba.org> 2000
2738 * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
2739 * Copyright (c) Guenther Deschner <gd@samba.org> 2005-2008
2740 * Copyright (c) Jan Rêkorajski 1999.
2741 * Copyright (c) Andrew G. Morgan 1996-8.
2742 * Copyright (c) Alex O. Yuriev, 1996.
2743 * Copyright (c) Cristian Gafton 1996.
2744 * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
2745 *
2746 * Redistribution and use in source and binary forms, with or without
2747 * modification, are permitted provided that the following conditions
2748 * are met:
2749 * 1. Redistributions of source code must retain the above copyright
2750 * notice, and the entire permission notice in its entirety,
2751 * including the disclaimer of warranties.
2752 * 2. Redistributions in binary form must reproduce the above copyright
2753 * notice, this list of conditions and the following disclaimer in the
2754 * documentation and/or other materials provided with the distribution.
2755 * 3. The name of the author may not be used to endorse or promote
2756 * products derived from this software without specific prior
2757 * written permission.
2758 *
2759 * ALTERNATIVELY, this product may be distributed under the terms of
2760 * the GNU Public License, in which case the provisions of the GPL are
2761 * required INSTEAD OF the above restrictions. (This clause is
2762 * necessary due to a potential bad interaction between the GPL and
2763 * the restrictions contained in a BSD-style copyright.)
2764 *
2765 * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
2766 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2767 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2768 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2769 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2770 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2771 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2772 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2773 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2774 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
2775 * OF THE POSSIBILITY OF SUCH DAMAGE.
2776 */
Note: See TracBrowser for help on using the repository browser.