r14940: Remove pam_winbind's ability to create home directories on it's own.
[vlendec/samba-autobuild/.git] / source3 / nsswitch / pam_winbind.c
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-2006
7
8    largely based on pam_userdb by Cristian Gafton <gafton@redhat.com> 
9    also contains large slabs of code from pam_unix by Elliot Lee <sopwith@redhat.com>
10    (see copyright below for full details)
11 */
12
13 #include "includes.h"
14 #include "pam_winbind.h"
15
16 /* data tokens */
17
18 #define MAX_PASSWD_TRIES        3
19
20 /* some syslogging */
21 static void _pam_log(int err, const char *format, ...)
22 {
23         va_list args;
24
25         va_start(args, format);
26         openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
27         vsyslog(err, format, args);
28         va_end(args);
29         closelog();
30 }
31
32 static void _pam_log_debug(int ctrl, int err, const char *format, ...)
33 {
34         va_list args;
35
36         if (!(ctrl & WINBIND_DEBUG_ARG)) {
37                 return;
38         }
39
40         va_start(args, format);
41         openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
42         vsyslog(err, format, args);
43         va_end(args);
44         closelog();
45 }
46
47 static int _pam_parse(int argc, const char **argv)
48 {
49         int ctrl = 0;
50
51         load_case_tables();
52
53         if (!lp_load(dyn_CONFIGFILE,True,False,False,True)) {
54                 return -1;
55         }
56
57         if (lp_parm_bool(-1, "pam_winbind", "cached_login", False)) {
58                 ctrl |= WINBIND_CACHED_LOGIN;
59         }
60         if (lp_parm_bool(-1, "pam_winbind", "krb5_auth", False)) {
61                 ctrl |= WINBIND_KRB5_AUTH;
62         }
63         if (lp_parm_const_string(-1, "pam_winbind", "krb5_ccache_type", NULL) != NULL) {
64                 ctrl |= WINBIND_KRB5_CCACHE_TYPE;
65         }
66         if ((lp_parm_const_string(-1, "pam_winbind", "require-membership-of", NULL) != NULL) || 
67             (lp_parm_const_string(-1, "pam_winbind", "require_membership_of", NULL) != NULL)) { 
68                 ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
69         }
70
71         /* step through arguments */
72         for (; argc-- > 0; ++argv) {
73
74                 /* generic options */
75
76                 if (!StrCaseCmp(*argv, "debug"))
77                         ctrl |= WINBIND_DEBUG_ARG;
78                 else if (strequal(*argv, "use_authtok"))
79                         ctrl |= WINBIND_USE_AUTHTOK_ARG;
80                 else if (strequal(*argv, "use_first_pass"))
81                         ctrl |= WINBIND_USE_FIRST_PASS_ARG;
82                 else if (strequal(*argv, "try_first_pass"))
83                         ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
84                 else if (strequal(*argv, "unknown_ok"))
85                         ctrl |= WINBIND_UNKNOWN_OK_ARG;
86                 else if (strnequal(*argv, "require_membership_of", strlen("require_membership_of")))
87                         ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
88                 else if (strnequal(*argv, "require-membership-of", strlen("require-membership-of")))
89                         ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
90                 else if (strequal(*argv, "krb5_auth"))
91                         ctrl |= WINBIND_KRB5_AUTH;
92                 else if (strnequal(*argv, "krb5_ccache_type", strlen("krb5_ccache_type")))
93                         ctrl |= WINBIND_KRB5_CCACHE_TYPE;
94                 else if (strequal(*argv, "cached_login"))
95                         ctrl |= WINBIND_CACHED_LOGIN;
96                 else {
97                         _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
98                 }
99
100         }
101         return ctrl;
102 };
103
104 static void _pam_winbind_cleanup_func(pam_handle_t *pamh, void *data, int error_status)
105 {
106         SAFE_FREE(data);
107 }
108
109 static const struct ntstatus_errors {
110         const char *ntstatus_string;
111         const char *error_string;
112 } ntstatus_errors[] = {
113         {"NT_STATUS_OK", "Success"},
114         {"NT_STATUS_BACKUP_CONTROLLER", "No primary Domain Controler available"},
115         {"NT_STATUS_PWD_TOO_SHORT", "Password too short"},
116         {"NT_STATUS_PWD_TOO_RECENT", "The password of this user is too recent to change"},
117         {"NT_STATUS_PWD_HISTORY_CONFLICT", "Password is already in password history"},
118         {"NT_STATUS_PASSWORD_EXPIRED", "Your password has expired"},
119         {"NT_STATUS_PASSWORD_MUST_CHANGE", "You need to change your password now"},
120         {"NT_STATUS_INVALID_WORKSTATION", "You are not allowed to logon from this workstation"},
121         {"NT_STATUS_INVALID_LOGON_HOURS", "You are not allowed to logon at this time"},
122         {"NT_STATUS_ACCOUNT_EXPIRED", "Your account has expired. Please contact your System administrator"}, /* SCNR */
123         {"NT_STATUS_ACCOUNT_DISABLED", "Your account is disabled. Please contact your System administrator"}, /* SCNR */
124         {"NT_STATUS_ACCOUNT_LOCKED_OUT", "Your account has been locked. Please contact your System administrator"}, /* SCNR */
125         {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", "Invalid Trust Account"},
126         {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", "Invalid Trust Account"},
127         {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", "Invalid Trust Account"},
128         {"NT_STATUS_ACCESS_DENIED", "Access is denied"},
129         {NULL, NULL}
130 };
131
132 const char *_get_ntstatus_error_string(const char *nt_status_string) 
133 {
134         int i;
135         for (i=0; ntstatus_errors[i].ntstatus_string != NULL; i++) {
136                 if (strequal(ntstatus_errors[i].ntstatus_string, nt_status_string)) {
137                         return ntstatus_errors[i].error_string;
138                 }
139         }
140         return NULL;
141 }
142
143 /* --- authentication management functions --- */
144
145 /* Attempt a conversation */
146
147 static int converse(pam_handle_t *pamh, int nargs,
148                     struct pam_message **message,
149                     struct pam_response **response)
150 {
151         int retval;
152         struct pam_conv *conv;
153
154         retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv );
155         if (retval == PAM_SUCCESS) {
156                 retval = conv->conv(nargs, (const struct pam_message **)message,
157                                     response, conv->appdata_ptr);
158         }
159         
160         return retval; /* propagate error status */
161 }
162
163
164 static int _make_remark(pam_handle_t * pamh, int type, const char *text)
165 {
166         int retval = PAM_SUCCESS;
167
168         struct pam_message *pmsg[1], msg[1];
169         struct pam_response *resp;
170         
171         pmsg[0] = &msg[0];
172         msg[0].msg = text;
173         msg[0].msg_style = type;
174         
175         resp = NULL;
176         retval = converse(pamh, 1, pmsg, &resp);
177         
178         if (resp) {
179                 _pam_drop_reply(resp, 1);
180         }
181         return retval;
182 }
183
184 static int _make_remark_format(pam_handle_t * pamh, int type, const char *format, ...)
185 {
186         va_list args;
187         char *var;
188
189         va_start(args, format);
190         vasprintf(&var, format, args);
191         va_end(args);
192
193         return _make_remark(pamh, type, var);
194 }
195
196 static int pam_winbind_request(pam_handle_t * pamh, int ctrl,
197                                enum winbindd_cmd req_type,
198                                struct winbindd_request *request,
199                                struct winbindd_response *response)
200 {
201         /* Fill in request and send down pipe */
202         init_request(request, req_type);
203         
204         if (write_sock(request, sizeof(*request), 0) == -1) {
205                 _pam_log(LOG_ERR, "write to socket failed!");
206                 close_sock();
207                 return PAM_SERVICE_ERR;
208         }
209         
210         /* Wait for reply */
211         if (read_reply(response) == -1) {
212                 _pam_log(LOG_ERR, "read from socket failed!");
213                 close_sock();
214                 return PAM_SERVICE_ERR;
215         }
216
217         /* We are done with the socket - close it and avoid mischeif */
218         close_sock();
219
220         /* Copy reply data from socket */
221         if (response->result != WINBINDD_OK) {
222                 if (response->data.auth.pam_error != PAM_SUCCESS) {
223                         _pam_log(LOG_ERR, "request failed: %s, PAM error was %d, NT error was %s", 
224                                  response->data.auth.error_string,
225                                  response->data.auth.pam_error,
226                                  response->data.auth.nt_status_string);
227                         return response->data.auth.pam_error;
228                 } else {
229                         _pam_log(LOG_ERR, "request failed, but PAM error 0!");
230                         return PAM_SERVICE_ERR;
231                 }
232         }
233
234         return PAM_SUCCESS;
235 }
236
237 static int pam_winbind_request_log(pam_handle_t * pamh, 
238                                    int ctrl,
239                                    enum winbindd_cmd req_type,
240                                    struct winbindd_request *request,
241                                    struct winbindd_response *response,
242                                    const char *user)
243 {
244         int retval;
245
246         retval = pam_winbind_request(pamh, ctrl, req_type, request, response);
247
248         switch (retval) {
249         case PAM_AUTH_ERR:
250                 /* incorrect password */
251                 _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password or invalid membership)", user);
252                 return retval;
253         case PAM_ACCT_EXPIRED:
254                 /* account expired */
255                 _pam_log(LOG_WARNING, "user `%s' account expired", user);
256                 return retval;
257         case PAM_AUTHTOK_EXPIRED:
258                 /* password expired */
259                 _pam_log(LOG_WARNING, "user `%s' password expired", user);
260                 return retval;
261         case PAM_NEW_AUTHTOK_REQD:
262                 /* new password required */
263                 _pam_log(LOG_WARNING, "user `%s' new password required", user);
264                 return retval;
265         case PAM_USER_UNKNOWN:
266                 /* the user does not exist */
267                 _pam_log_debug(ctrl, LOG_NOTICE, "user `%s' not found",
268                                  user);
269                 if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
270                         return PAM_IGNORE;
271                 }        
272                 return retval;
273         case PAM_SUCCESS:
274                 if (req_type == WINBINDD_PAM_AUTH) {
275                         /* Otherwise, the authentication looked good */
276                         _pam_log(LOG_NOTICE, "user '%s' granted access", user);
277                 } else if (req_type == WINBINDD_PAM_CHAUTHTOK) {
278                         /* Otherwise, the authentication looked good */
279                         _pam_log(LOG_NOTICE, "user '%s' password changed", user);
280                 } else { 
281                         /* Otherwise, the authentication looked good */
282                         _pam_log(LOG_NOTICE, "user '%s' OK", user);
283                 }
284         
285                 return retval;
286         default:
287                 /* we don't know anything about this return value */
288                 _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s')",
289                          retval, user);
290                 return retval;
291         }
292 }
293
294 /* talk to winbindd */
295 static int winbind_auth_request(pam_handle_t * pamh, 
296                                 int ctrl, 
297                                 const char *user, 
298                                 const char *pass, 
299                                 const char *member, 
300                                 const char *cctype,
301                                 int process_result)
302 {
303         struct winbindd_request request;
304         struct winbindd_response response;
305         int ret;
306
307         ZERO_STRUCT(request);
308         ZERO_STRUCT(response);
309
310         strncpy(request.data.auth.user, user, 
311                 sizeof(request.data.auth.user)-1);
312
313         strncpy(request.data.auth.pass, pass, 
314                 sizeof(request.data.auth.pass)-1);
315
316         request.data.auth.krb5_cc_type[0] = '\0';
317         request.data.auth.uid = -1;
318         
319         request.flags = WBFLAG_PAM_INFO3_TEXT | WBFLAG_PAM_GET_PWD_POLICY;
320
321         if (ctrl & WINBIND_KRB5_AUTH) {
322
323                 struct passwd *pwd = NULL;
324
325                 _pam_log_debug(ctrl, LOG_DEBUG, "enabling krb5 login flag\n"); 
326
327                 request.flags |= WBFLAG_PAM_KRB5 | WBFLAG_PAM_FALLBACK_AFTER_KRB5;
328
329                 pwd = getpwnam(user);
330                 if (pwd == NULL) {
331                         return PAM_USER_UNKNOWN;
332                 }
333                 request.data.auth.uid = pwd->pw_uid;
334         }
335
336         if (ctrl & WINBIND_CACHED_LOGIN) {
337                 _pam_log_debug(ctrl, LOG_DEBUG, "enabling cached login flag\n"); 
338                 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
339         }
340
341         if (cctype != NULL) {
342                 strncpy(request.data.auth.krb5_cc_type, cctype, 
343                         sizeof(request.data.auth.krb5_cc_type) - 1);
344                 _pam_log_debug(ctrl, LOG_DEBUG, "enabling request for a %s krb5 ccache\n", cctype); 
345         }
346
347         request.data.auth.require_membership_of_sid[0] = '\0';
348
349         if (member != NULL) {
350                 strncpy(request.data.auth.require_membership_of_sid, member, 
351                         sizeof(request.data.auth.require_membership_of_sid)-1);
352         }
353
354         /* lookup name? */ 
355         if ( (member != NULL) && (strncmp("S-", member, 2) != 0) ) {
356                 
357                 struct winbindd_request sid_request;
358                 struct winbindd_response sid_response;
359
360                 ZERO_STRUCT(sid_request);
361                 ZERO_STRUCT(sid_response);
362
363                 _pam_log_debug(ctrl, LOG_DEBUG, "no sid given, looking up: %s\n", member);
364
365                 /* fortunatly winbindd can handle non-separated names */
366                 fstrcpy(sid_request.data.name.name, member);
367
368                 if (pam_winbind_request_log(pamh, ctrl, WINBINDD_LOOKUPNAME, &sid_request, &sid_response, user)) {
369                         _pam_log(LOG_INFO, "could not lookup name: %s\n", member); 
370                         return PAM_AUTH_ERR;
371                 }
372
373                 member = sid_response.data.sid.sid;
374
375                 strncpy(request.data.auth.require_membership_of_sid, member, 
376                         sizeof(request.data.auth.require_membership_of_sid)-1);
377         }
378         
379         ret = pam_winbind_request_log(pamh, ctrl, WINBINDD_PAM_AUTH, &request, &response, user);
380
381         if ((ctrl & WINBIND_KRB5_AUTH) && 
382             response.data.auth.krb5ccname[0] != '\0') {
383
384                 char var[PATH_MAX];
385
386                 _pam_log_debug(ctrl, LOG_DEBUG, "request returned KRB5CCNAME: %s", 
387                                response.data.auth.krb5ccname);
388         
389                 snprintf(var, sizeof(var), "KRB5CCNAME=%s", response.data.auth.krb5ccname);
390         
391                 ret = pam_putenv(pamh, var);
392                 if (ret != PAM_SUCCESS) {
393                         _pam_log(LOG_ERR, "failed to set KRB5CCNAME to %s", var);
394                         return ret;
395                 }
396         }
397
398         if (!process_result) {
399                 return ret;
400         }
401
402         if (ret) {
403                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PASSWORD_EXPIRED");
404                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PASSWORD_MUST_CHANGE");
405                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_INVALID_WORKSTATION");
406                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_INVALID_LOGON_HOURS");
407                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCOUNT_EXPIRED");
408                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCOUNT_DISABLED");
409                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCOUNT_LOCKED_OUT");
410                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT");
411                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT");
412                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT");
413         }
414
415         /* handle the case where the auth was ok, but the password must expire right now */
416         /* good catch from Ralf Haferkamp: an expiry of "never" is translated to -1 */
417         if ( ! (response.data.auth.info3.acct_flags & ACB_PWNOEXP) &&
418             (response.data.auth.policy.expire > 0) && 
419             (response.data.auth.info3.pass_last_set_time + response.data.auth.policy.expire < time(NULL))) {
420
421                 ret = PAM_AUTHTOK_EXPIRED;
422
423                 _pam_log_debug(ctrl, LOG_DEBUG,"Password has expired (Password was last set: %d, "
424                                "the policy says it should expire here %d (now it's: %d)\n",
425                                response.data.auth.info3.pass_last_set_time,
426                                response.data.auth.info3.pass_last_set_time + response.data.auth.policy.expire,
427                                time(NULL));
428
429                 PAM_WB_REMARK_DIRECT_RET(pamh, "NT_STATUS_PASSWORD_EXPIRED");
430
431         }
432
433         /* warn a user if the password is about to expire soon */
434         if ( ! (response.data.auth.info3.acct_flags & ACB_PWNOEXP) &&
435             (response.data.auth.policy.expire) && 
436             (response.data.auth.info3.pass_last_set_time + response.data.auth.policy.expire > time(NULL) ) ) {
437
438                 int days = response.data.auth.policy.expire / SECONDS_PER_DAY;
439                 if (days <= DAYS_TO_WARN_BEFORE_PWD_EXPIRES) {
440                         _make_remark_format(pamh, PAM_TEXT_INFO, "Your password will expire in %d days", days);
441                 }
442         }
443
444         if (response.data.auth.info3.user_flgs & LOGON_CACHED_ACCOUNT) {
445                 _make_remark(pamh, PAM_ERROR_MSG, "Logging on using cached account. Network ressources can be unavailable");
446         }
447
448         /* save the CIFS homedir for pam_cifs / pam_mount */
449         if (response.data.auth.info3.home_dir[0] != '\0') {
450                 char *buf;
451
452                 if (!asprintf(&buf, "%s", response.data.auth.info3.home_dir)) {
453                         return PAM_BUF_ERR;
454                 }
455
456                 pam_set_data( pamh, PAM_WINBIND_HOMEDIR, (void *)buf, _pam_winbind_cleanup_func);
457         }
458
459         return ret;
460 }
461
462 /* talk to winbindd */
463 static int winbind_chauthtok_request(pam_handle_t * pamh,
464                                      int ctrl,
465                                      const char *user, 
466                                      const char *oldpass,
467                                      const char *newpass) 
468 {
469         struct winbindd_request request;
470         struct winbindd_response response;
471         int ret;
472
473         ZERO_STRUCT(request);
474         ZERO_STRUCT(response);
475
476         if (request.data.chauthtok.user == NULL) return -2;
477
478         strncpy(request.data.chauthtok.user, user, 
479                 sizeof(request.data.chauthtok.user) - 1);
480
481         if (oldpass != NULL) {
482                 strncpy(request.data.chauthtok.oldpass, oldpass, 
483                         sizeof(request.data.chauthtok.oldpass) - 1);
484         } else {
485                 request.data.chauthtok.oldpass[0] = '\0';
486         }
487         
488         if (newpass != NULL) {
489                 strncpy(request.data.chauthtok.newpass, newpass, 
490                         sizeof(request.data.chauthtok.newpass) - 1);
491         } else {
492                 request.data.chauthtok.newpass[0] = '\0';
493         }
494
495         if (ctrl & WINBIND_KRB5_AUTH) {
496                 request.flags = WBFLAG_PAM_KRB5;
497         }
498
499         ret = pam_winbind_request_log(pamh, ctrl, WINBINDD_PAM_CHAUTHTOK, &request, &response, user);
500
501         if (ret == PAM_SUCCESS) {
502                 return ret;
503         }
504
505         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_BACKUP_CONTROLLER");
506         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCESS_DENIED");
507
508         /* TODO: tell the min pwd length ? */
509         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PWD_TOO_SHORT");
510
511         /* TODO: tell the minage ? */
512         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PWD_TOO_RECENT");
513
514         /* TODO: tell the history length ? */
515         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PWD_HISTORY_CONFLICT");
516
517         if (strequal(response.data.auth.nt_status_string, "NT_STATUS_PASSWORD_RESTRICTION")) {
518
519                 /* FIXME: avoid to send multiple PAM messages after another */
520                 switch (response.data.auth.reject_reason) {
521                         case 0:
522                                 break;
523                         case REJECT_REASON_TOO_SHORT:
524                                 PAM_WB_REMARK_DIRECT(pamh, "NT_STATUS_PWD_TOO_SHORT");
525                                 break;
526                         case REJECT_REASON_IN_HISTORY:
527                                 PAM_WB_REMARK_DIRECT(pamh, "NT_STATUS_PWD_HISTORY_CONFLICT");
528                                 break;
529                         case REJECT_REASON_NOT_COMPLEX:
530                                 _make_remark(pamh, PAM_ERROR_MSG, "Password does not meet complexity requirements");
531                                 break;
532                         default:
533                                 _pam_log_debug(ctrl, LOG_DEBUG,
534                                                "unknown password change reject reason: %d", 
535                                                response.data.auth.reject_reason);
536                                 break;
537                 }
538
539                 _make_remark_format(pamh, PAM_ERROR_MSG,  
540                         "Your password must be at least %d characters; "
541                         "cannot repeat any of the your previous %d passwords"
542                         "%s. "
543                         "Please type a different password. "
544                         "Type a password which meets these requirements in both text boxes.",
545                         response.data.auth.policy.min_length_password,
546                         response.data.auth.policy.password_history,
547                         (response.data.auth.policy.password_properties & DOMAIN_PASSWORD_COMPLEX) ? 
548                                 "; must contain capitals, numerals or punctuation; and cannot contain your account or full name" : 
549                                 "");
550
551         }
552
553         return ret;
554 }
555
556 /*
557  * Checks if a user has an account
558  *
559  * return values:
560  *       1  = User not found
561  *       0  = OK
562  *      -1  = System error
563  */
564 static int valid_user(const char *user, pam_handle_t *pamh, int ctrl)
565 {
566         /* check not only if the user is available over NSS calls, also make
567          * sure it's really a winbind user, this is important when stacking PAM
568          * modules in the 'account' or 'password' facility. */
569
570         struct passwd *pwd = NULL;
571         struct winbindd_request request;
572         struct winbindd_response response;
573         int ret;
574
575         ZERO_STRUCT(request);
576         ZERO_STRUCT(response);
577
578         pwd = getpwnam(user);
579         if (pwd == NULL) {
580                 return 1;
581         }
582
583         fstrcpy(request.data.username, user);
584
585         ret = pam_winbind_request_log(pamh, ctrl, WINBINDD_GETPWNAM, &request, &response, user);
586
587         switch (ret) {
588                 case PAM_USER_UNKNOWN:
589                         return 1;
590                 case PAM_SUCCESS:
591                         return 0;
592                 default:
593                         break;
594         }
595         return -1;
596 }
597
598 static char *_pam_delete(register char *xx)
599 {
600         _pam_overwrite(xx);
601         _pam_drop(xx);
602         return NULL;
603 }
604
605 /*
606  * obtain a password from the user
607  */
608
609 static int _winbind_read_password(pam_handle_t * pamh,
610                                   unsigned int ctrl,
611                                   const char *comment,
612                                   const char *prompt1,
613                                   const char *prompt2,
614                                   const char **pass)
615 {
616         int authtok_flag;
617         int retval;
618         const char *item;
619         char *token;
620
621         /*
622          * make sure nothing inappropriate gets returned
623          */
624
625         *pass = token = NULL;
626
627         /*
628          * which authentication token are we getting?
629          */
630
631         authtok_flag = on(WINBIND__OLD_PASSWORD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
632
633         /*
634          * should we obtain the password from a PAM item ?
635          */
636
637         if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
638                 retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
639                 if (retval != PAM_SUCCESS) {
640                         /* very strange. */
641                         _pam_log(LOG_ALERT, 
642                                  "pam_get_item returned error to unix-read-password"
643                             );
644                         return retval;
645                 } else if (item != NULL) {      /* we have a password! */
646                         *pass = item;
647                         item = NULL;
648                         return PAM_SUCCESS;
649                 } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
650                         return PAM_AUTHTOK_RECOVER_ERR;         /* didn't work */
651                 } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
652                            && off(WINBIND__OLD_PASSWORD, ctrl)) {
653                         return PAM_AUTHTOK_RECOVER_ERR;
654                 }
655         }
656         /*
657          * getting here implies we will have to get the password from the
658          * user directly.
659          */
660
661         {
662                 struct pam_message msg[3], *pmsg[3];
663                 struct pam_response *resp;
664                 int i, replies;
665
666                 /* prepare to converse */
667
668                 if (comment != NULL) {
669                         pmsg[0] = &msg[0];
670                         msg[0].msg_style = PAM_TEXT_INFO;
671                         msg[0].msg = comment;
672                         i = 1;
673                 } else {
674                         i = 0;
675                 }
676
677                 pmsg[i] = &msg[i];
678                 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
679                 msg[i++].msg = prompt1;
680                 replies = 1;
681
682                 if (prompt2 != NULL) {
683                         pmsg[i] = &msg[i];
684                         msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
685                         msg[i++].msg = prompt2;
686                         ++replies;
687                 }
688                 /* so call the conversation expecting i responses */
689                 resp = NULL;
690                 retval = converse(pamh, i, pmsg, &resp);
691
692                 if (resp != NULL) {
693
694                         /* interpret the response */
695
696                         if (retval == PAM_SUCCESS) {    /* a good conversation */
697
698                                 token = SMB_STRDUP(resp[i - replies].resp);
699                                 if (token != NULL) {
700                                         if (replies == 2) {
701                                                 /* verify that password entered correctly */
702                                                 if (!resp[i - 1].resp
703                                                     || StrCaseCmp(token, resp[i - 1].resp)) {
704                                                         _pam_delete(token);     /* mistyped */
705                                                         retval = PAM_AUTHTOK_RECOVER_ERR;
706                                                         _make_remark(pamh, PAM_ERROR_MSG, MISTYPED_PASS);
707                                                 }
708                                         }
709                                 } else {
710                                         _pam_log(LOG_NOTICE
711                                                  ,"could not recover authentication token");
712                                 }
713
714                         }
715                         /*
716                          * tidy up the conversation (resp_retcode) is ignored
717                          * -- what is it for anyway? AGM
718                          */
719
720                         _pam_drop_reply(resp, i);
721
722                 } else {
723                         retval = (retval == PAM_SUCCESS)
724                             ? PAM_AUTHTOK_RECOVER_ERR : retval;
725                 }
726         }
727
728         if (retval != PAM_SUCCESS) {
729                 _pam_log_debug(ctrl, LOG_DEBUG,
730                                  "unable to obtain a password");
731                 return retval;
732         }
733         /* 'token' is the entered password */
734
735         /* we store this password as an item */
736         
737         retval = pam_set_item(pamh, authtok_flag, token);
738         _pam_delete(token);     /* clean it up */
739         if (retval != PAM_SUCCESS || 
740             (retval = pam_get_item(pamh, authtok_flag, (const void **) &item)) != PAM_SUCCESS) {
741                 
742                 _pam_log(LOG_CRIT, "error manipulating password");
743                 return retval;
744                 
745         }
746
747         *pass = item;
748         item = NULL;            /* break link to password */
749
750         return PAM_SUCCESS;
751 }
752
753 const char *get_conf_item_string(int argc, 
754                                  const char **argv, 
755                                  int ctrl, 
756                                  const char *item, 
757                                  int flag)
758 {
759         int i = 0;
760         char *parm = NULL;
761         const char *parm_opt = NULL;
762
763         if (!(ctrl & flag)) {
764                 goto out;
765         }
766
767         /* let the pam opt take precedence over the smb.conf option */
768         parm_opt = lp_parm_const_string(-1, "pam_winbind", item, NULL);
769
770         for ( i=0; i<argc; i++ ) {
771
772                 if ((strncmp(argv[i], item, strlen(item)) == 0)) {
773                         char *p;
774
775                         parm = SMB_STRDUP(argv[i]);
776
777                         if ( (p = strchr( parm, '=' )) == NULL) {
778                                 _pam_log(LOG_INFO, "no \"=\" delimiter for \"%s\" found\n", item);
779                                 goto out;
780                         }
781                         SAFE_FREE(parm);
782                         _pam_log_debug(ctrl, LOG_INFO, "PAM config: %s '%s'\n", item, p+1);
783                         return p + 1;
784                 }
785         }
786
787         _pam_log_debug(ctrl, LOG_INFO, "CONFIG file: %s '%s'\n", item, parm_opt);
788 out:
789         SAFE_FREE(parm);
790         return parm_opt;
791 }
792
793 const char *get_krb5_cc_type_from_config(int argc, const char **argv, int ctrl)
794 {
795         return get_conf_item_string(argc, argv, ctrl, "krb5_ccache_type", WINBIND_KRB5_CCACHE_TYPE);
796 }
797
798 const char *get_member_from_config(int argc, const char **argv, int ctrl)
799 {
800         const char *ret = NULL;
801         ret = get_conf_item_string(argc, argv, ctrl, "require_membership_of", WINBIND_REQUIRED_MEMBERSHIP);
802         if (ret) {
803                 return ret;
804         }
805         return get_conf_item_string(argc, argv, ctrl, "require-membership-of", WINBIND_REQUIRED_MEMBERSHIP);
806 }
807
808 PAM_EXTERN
809 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
810                         int argc, const char **argv)
811 {
812         const char *username;
813         const char *password;
814         const char *member = NULL;
815         const char *cctype = NULL;
816         int retval = PAM_AUTH_ERR;
817
818         /* parse arguments */
819         int ctrl = _pam_parse(argc, argv);
820         if (ctrl == -1) {
821                 return PAM_SYSTEM_ERR;
822         }
823
824         _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_authenticate");
825
826         /* Get the username */
827         retval = pam_get_user(pamh, &username, NULL);
828         if ((retval != PAM_SUCCESS) || (!username)) {
829                 _pam_log_debug(ctrl, LOG_DEBUG, "can not get the username");
830                 return PAM_SERVICE_ERR;
831         }
832
833         retval = _winbind_read_password(pamh, ctrl, NULL, 
834                                         "Password: ", NULL,
835                                         &password);
836
837         if (retval != PAM_SUCCESS) {
838                 _pam_log(LOG_ERR, "Could not retrieve user's password");
839                 return PAM_AUTHTOK_ERR;
840         }
841
842         /* Let's not give too much away in the log file */
843
844 #ifdef DEBUG_PASSWORD
845         _pam_log_debug(ctrl, LOG_INFO, "Verify user `%s' with password `%s'", 
846                        username, password);
847 #else
848         _pam_log_debug(ctrl, LOG_INFO, "Verify user `%s'", username);
849 #endif
850
851         member = get_member_from_config(argc, argv, ctrl);
852
853         cctype = get_krb5_cc_type_from_config(argc, argv, ctrl);
854
855         /* Now use the username to look up password */
856         retval = winbind_auth_request(pamh, ctrl, username, password, member, cctype, True);
857
858         if (retval == PAM_NEW_AUTHTOK_REQD ||
859             retval == PAM_AUTHTOK_EXPIRED) {
860
861                 char *buf;
862
863                 if (!asprintf(&buf, "%d", retval)) {
864                         return PAM_BUF_ERR;
865                 }
866
867                 pam_set_data( pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, (void *)buf, _pam_winbind_cleanup_func);
868
869                 return PAM_SUCCESS;
870         }
871
872         return retval;
873 }
874
875 PAM_EXTERN
876 int pam_sm_setcred(pam_handle_t *pamh, int flags,
877                    int argc, const char **argv)
878 {
879         /* parse arguments */
880         int ctrl = _pam_parse(argc, argv);
881         if (ctrl == -1) {
882                 return PAM_SYSTEM_ERR;
883         }
884
885         _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_setcred");
886
887         if (flags & PAM_DELETE_CRED) {
888                 return pam_sm_close_session(pamh, flags, argc, argv);
889         }
890
891         return PAM_SUCCESS;
892 }
893
894 /*
895  * Account management. We want to verify that the account exists 
896  * before returning PAM_SUCCESS
897  */
898 PAM_EXTERN
899 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
900                    int argc, const char **argv)
901 {
902         const char *username;
903         int retval = PAM_USER_UNKNOWN;
904         void *tmp = NULL;
905
906         /* parse arguments */
907         int ctrl = _pam_parse(argc, argv);
908         if (ctrl == -1) {
909                 return PAM_SYSTEM_ERR;
910         }
911
912         _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_acct_mgmt");
913
914
915         /* Get the username */
916         retval = pam_get_user(pamh, &username, NULL);
917         if ((retval != PAM_SUCCESS) || (!username)) {
918                 _pam_log_debug(ctrl, LOG_DEBUG,"can not get the username");
919                 return PAM_SERVICE_ERR;
920         }
921
922         /* Verify the username */
923         retval = valid_user(username, pamh, ctrl);
924         switch (retval) {
925         case -1:
926                 /* some sort of system error. The log was already printed */
927                 return PAM_SERVICE_ERR;
928         case 1:
929                 /* the user does not exist */
930                 _pam_log_debug(ctrl, LOG_NOTICE, "user `%s' not found", username);
931                 if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
932                         return PAM_IGNORE;
933                 }
934                 return PAM_USER_UNKNOWN;
935         case 0:
936                 pam_get_data( pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, (const void **)&tmp);
937                 if (tmp != NULL) {
938                         retval = atoi(tmp);
939                         switch (retval) {
940                         case PAM_AUTHTOK_EXPIRED:
941                                 /* fall through, since new token is required in this case */
942                         case PAM_NEW_AUTHTOK_REQD:
943                                 _pam_log(LOG_WARNING, "pam_sm_acct_mgmt success but %s is set", 
944                                          PAM_WINBIND_NEW_AUTHTOK_REQD);
945                                 _pam_log(LOG_NOTICE, "user '%s' needs new password", username);
946                                 /* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */
947                                 return PAM_NEW_AUTHTOK_REQD; 
948                         default:
949                                 _pam_log(LOG_WARNING, "pam_sm_acct_mgmt success");
950                                 _pam_log(LOG_NOTICE, "user '%s' granted access", username);
951                                 return PAM_SUCCESS;
952                         }
953                 }
954
955                 /* Otherwise, the authentication looked good */
956                 _pam_log(LOG_NOTICE, "user '%s' granted access", username);
957                 return PAM_SUCCESS;
958         default:
959                 /* we don't know anything about this return value */
960                 _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s')", 
961                          retval, username);
962                 return PAM_SERVICE_ERR;
963         }
964
965         /* should not be reached */
966         return PAM_IGNORE;
967 }
968
969 PAM_EXTERN
970 int pam_sm_open_session(pam_handle_t *pamh, int flags,
971                         int argc, const char **argv)
972 {
973         /* parse arguments */
974         int ctrl = _pam_parse(argc, argv);
975         if (ctrl == -1) {
976                 return PAM_SYSTEM_ERR;
977         }
978
979         _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_open_session handler");
980
981         return PAM_SUCCESS;
982 }
983
984 PAM_EXTERN
985 int pam_sm_close_session(pam_handle_t *pamh, int flags,
986                          int argc, const char **argv)
987 {
988         /* parse arguments */
989         int ctrl = _pam_parse(argc, argv);
990         if (ctrl == -1) {
991                 return PAM_SYSTEM_ERR;
992         }
993
994         _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_close_session handler");
995
996         if (!(flags & PAM_DELETE_CRED)) {
997                 return PAM_SUCCESS;
998         }
999
1000         if (ctrl & WINBIND_KRB5_AUTH) {
1001
1002                 /* destroy the ccache here */
1003                 struct winbindd_request request;
1004                 struct winbindd_response response;
1005                 const char *user;
1006                 const char *ccname = NULL;
1007                 struct passwd *pwd = NULL;
1008
1009                 int retval;
1010
1011                 ZERO_STRUCT(request);
1012                 ZERO_STRUCT(response);
1013
1014                 retval = pam_get_user(pamh, &user, "Username: ");
1015                 if (retval == PAM_SUCCESS) {
1016                         if (user == NULL) {
1017                                 _pam_log(LOG_ERR, "username was NULL!");
1018                                 return PAM_USER_UNKNOWN;
1019                         }
1020                         if (retval == PAM_SUCCESS) {
1021                                 _pam_log_debug(ctrl, LOG_DEBUG, "username [%s] obtained", user);
1022                         }
1023                 } else {
1024                         _pam_log_debug(ctrl, LOG_DEBUG, "could not identify user");
1025                         return retval;
1026                 }
1027
1028                 ccname = pam_getenv(pamh, "KRB5CCNAME");
1029                 if (ccname == NULL) {
1030                         _pam_log_debug(ctrl, LOG_DEBUG, "user has no KRB5CCNAME environment");
1031                         return PAM_SUCCESS;
1032                 }
1033
1034                 fstrcpy(request.data.logoff.user, user);
1035                 fstrcpy(request.data.logoff.krb5ccname, ccname);
1036
1037                 pwd = getpwnam(user);
1038                 if (pwd == NULL) {
1039                         return PAM_USER_UNKNOWN;
1040                 }
1041                 request.data.logoff.uid = pwd->pw_uid;
1042
1043                 request.flags = WBFLAG_PAM_KRB5;
1044
1045                 return pam_winbind_request_log(pamh, ctrl, WINBINDD_PAM_LOGOFF, &request, &response, user);
1046         }
1047         
1048         return PAM_SUCCESS;
1049 }
1050
1051
1052
1053 PAM_EXTERN 
1054 int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
1055                      int argc, const char **argv)
1056 {
1057         unsigned int lctrl;
1058         int retval;
1059         unsigned int ctrl;
1060
1061         /* <DO NOT free() THESE> */
1062         const char *user;
1063         char *pass_old, *pass_new;
1064         /* </DO NOT free() THESE> */
1065
1066         fstring Announce;
1067         
1068         int retry = 0;
1069
1070         ctrl = _pam_parse(argc, argv);
1071         if (ctrl == -1) {
1072                 return PAM_SYSTEM_ERR;
1073         }
1074
1075         _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_chauthtok");
1076
1077         /*
1078          * First get the name of a user
1079          */
1080         retval = pam_get_user(pamh, &user, "Username: ");
1081         if (retval == PAM_SUCCESS) {
1082                 if (user == NULL) {
1083                         _pam_log(LOG_ERR, "username was NULL!");
1084                         return PAM_USER_UNKNOWN;
1085                 }
1086                 if (retval == PAM_SUCCESS) {
1087                         _pam_log_debug(ctrl, LOG_DEBUG, "username [%s] obtained",
1088                                  user);
1089                 }
1090         } else {
1091                 _pam_log_debug(ctrl, LOG_DEBUG,
1092                          "password - could not identify user");
1093                 return retval;
1094         }
1095
1096         /* check if this is really a user in winbindd, not only in NSS */
1097         retval = valid_user(user, pamh, ctrl);
1098         switch (retval) {
1099                 case 1:
1100                         return PAM_USER_UNKNOWN;
1101                 case -1:
1102                         return PAM_SYSTEM_ERR;
1103                 default:
1104                         break;
1105         }
1106                 
1107         /*
1108          * obtain and verify the current password (OLDAUTHTOK) for
1109          * the user.
1110          */
1111
1112         if (flags & PAM_PRELIM_CHECK) {
1113                 
1114                 /* instruct user what is happening */
1115 #define greeting "Changing password for "
1116                 fstrcpy(Announce, greeting);
1117                 fstrcat(Announce, user);
1118 #undef greeting
1119                 
1120                 lctrl = ctrl | WINBIND__OLD_PASSWORD;
1121                 retval = _winbind_read_password(pamh, lctrl,
1122                                                 Announce,
1123                                                 "(current) NT password: ",
1124                                                 NULL,
1125                                                 (const char **) &pass_old);
1126                 if (retval != PAM_SUCCESS) {
1127                         _pam_log(LOG_NOTICE, "password - (old) token not obtained");
1128                         return retval;
1129                 }
1130                 /* verify that this is the password for this user */
1131                 
1132                 retval = winbind_auth_request(pamh, ctrl, user, pass_old, NULL, NULL, False);
1133
1134                 if (retval != PAM_ACCT_EXPIRED 
1135                     && retval != PAM_AUTHTOK_EXPIRED
1136                     && retval != PAM_NEW_AUTHTOK_REQD 
1137                     && retval != PAM_SUCCESS) {
1138                         pass_old = NULL;
1139                         return retval;
1140                 }
1141                 
1142                 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
1143                 pass_old = NULL;
1144                 if (retval != PAM_SUCCESS) {
1145                         _pam_log(LOG_CRIT, "failed to set PAM_OLDAUTHTOK");
1146                 }
1147         } else if (flags & PAM_UPDATE_AUTHTOK) {
1148         
1149                 /*
1150                  * obtain the proposed password
1151                  */
1152                 
1153                 /*
1154                  * get the old token back. 
1155                  */
1156                 
1157                 retval = pam_get_item(pamh, PAM_OLDAUTHTOK,
1158                                       (const void **) &pass_old);
1159                 
1160                 if (retval != PAM_SUCCESS) {
1161                         _pam_log(LOG_NOTICE, "user not authenticated");
1162                         return retval;
1163                 }
1164                 
1165                 lctrl = ctrl;
1166                 
1167                 if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
1168                         lctrl |= WINBIND_USE_FIRST_PASS_ARG;
1169                 }
1170                 retry = 0;
1171                 retval = PAM_AUTHTOK_ERR;
1172                 while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
1173                         /*
1174                          * use_authtok is to force the use of a previously entered
1175                          * password -- needed for pluggable password strength checking
1176                          */
1177                         
1178                         retval = _winbind_read_password(pamh, lctrl,
1179                                                         NULL,
1180                                                         "Enter new NT password: ",
1181                                                         "Retype new NT password: ",
1182                                                         (const char **) &pass_new);
1183                         
1184                         if (retval != PAM_SUCCESS) {
1185                                 _pam_log_debug(ctrl, LOG_ALERT
1186                                          ,"password - new password not obtained");
1187                                 pass_old = NULL;/* tidy up */
1188                                 return retval;
1189                         }
1190
1191                         /*
1192                          * At this point we know who the user is and what they
1193                          * propose as their new password. Verify that the new
1194                          * password is acceptable.
1195                          */
1196                         
1197                         if (pass_new[0] == '\0') {/* "\0" password = NULL */
1198                                 pass_new = NULL;
1199                         }
1200                 }
1201                 
1202                 /*
1203                  * By reaching here we have approved the passwords and must now
1204                  * rebuild the password database file.
1205                  */
1206
1207                 retval = winbind_chauthtok_request(pamh, ctrl, user, pass_old, pass_new);
1208                 if (retval) {
1209                         _pam_overwrite(pass_new);
1210                         _pam_overwrite(pass_old);
1211                         pass_old = pass_new = NULL;
1212                         return retval;
1213                 }
1214
1215                 /* just in case we need krb5 creds after a password change over msrpc */
1216
1217                 if (ctrl & WINBIND_KRB5_AUTH) {
1218
1219                         const char *member = get_member_from_config(argc, argv, ctrl);
1220                         const char *cctype = get_krb5_cc_type_from_config(argc, argv, ctrl);
1221
1222                         retval = winbind_auth_request(pamh, ctrl, user, pass_new, member, cctype, False);
1223                         _pam_overwrite(pass_new);
1224                         _pam_overwrite(pass_old);
1225                         pass_old = pass_new = NULL;
1226                 }
1227         } else {
1228                 retval = PAM_SERVICE_ERR;
1229         }
1230
1231         return retval;
1232 }
1233
1234 #ifdef PAM_STATIC
1235
1236 /* static module data */
1237
1238 struct pam_module _pam_winbind_modstruct = {
1239         MODULE_NAME,
1240         pam_sm_authenticate,
1241         pam_sm_setcred,
1242         pam_sm_acct_mgmt,
1243         pam_sm_open_session,
1244         pam_sm_close_session,
1245         pam_sm_chauthtok
1246 };
1247
1248 #endif
1249
1250 /*
1251  * Copyright (c) Andrew Tridgell  <tridge@samba.org>   2000
1252  * Copyright (c) Tim Potter       <tpot@samba.org>     2000
1253  * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
1254  * Copyright (c) Guenther Deschner <gd@samba.org>      2005-2006
1255  * Copyright (c) Jan Rêkorajski 1999.
1256  * Copyright (c) Andrew G. Morgan 1996-8.
1257  * Copyright (c) Alex O. Yuriev, 1996.
1258  * Copyright (c) Cristian Gafton 1996.
1259  * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software. 
1260  *
1261  * Redistribution and use in source and binary forms, with or without
1262  * modification, are permitted provided that the following conditions
1263  * are met:
1264  * 1. Redistributions of source code must retain the above copyright
1265  *    notice, and the entire permission notice in its entirety,
1266  *    including the disclaimer of warranties.
1267  * 2. Redistributions in binary form must reproduce the above copyright
1268  *    notice, this list of conditions and the following disclaimer in the
1269  *    documentation and/or other materials provided with the distribution.
1270  * 3. The name of the author may not be used to endorse or promote
1271  *    products derived from this software without specific prior
1272  *    written permission.
1273  *
1274  * ALTERNATIVELY, this product may be distributed under the terms of
1275  * the GNU Public License, in which case the provisions of the GPL are
1276  * required INSTEAD OF the above restrictions.  (This clause is
1277  * necessary due to a potential bad interaction between the GPL and
1278  * the restrictions contained in a BSD-style copyright.)
1279  *
1280  * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
1281  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1282  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1283  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1284  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1285  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1286  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1287  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
1288  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1289  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
1290  * OF THE POSSIBILITY OF SUCH DAMAGE.
1291  */