~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to nsswitch/pam_winbind.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

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