first public release of samba4 code
[gd/samba-autobuild/.git] / source4 / 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
7    largely based on pam_userdb by Christian Gafton <gafton@redhat.com> 
8    also contains large slabs of code from pam_unix by Elliot Lee <sopwith@redhat.com>
9    (see copyright below for full details)
10 */
11
12 #include "pam_winbind.h"
13
14 /* data tokens */
15
16 #define MAX_PASSWD_TRIES        3
17
18 /* some syslogging */
19 static void _pam_log(int err, const char *format, ...)
20 {
21         va_list args;
22
23         va_start(args, format);
24         openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
25         vsyslog(err, format, args);
26         va_end(args);
27         closelog();
28 }
29
30 static int _pam_parse(int argc, const char **argv)
31 {
32         int ctrl;
33         /* step through arguments */
34         for (ctrl = 0; argc-- > 0; ++argv) {
35
36                 /* generic options */
37                 
38                 if (!strcmp(*argv,"debug"))
39                         ctrl |= WINBIND_DEBUG_ARG;
40                 else if (!strcasecmp(*argv, "use_authtok"))
41                         ctrl |= WINBIND_USE_AUTHTOK_ARG;
42                 else if (!strcasecmp(*argv, "use_first_pass"))
43                         ctrl |= WINBIND_USE_FIRST_PASS_ARG;
44                 else if (!strcasecmp(*argv, "try_first_pass"))
45                         ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
46                 else if (!strcasecmp(*argv, "unknown_ok"))
47                         ctrl |= WINBIND_UNKNOWN_OK_ARG;
48                 else {
49                         _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
50                 }
51         }
52         
53         return ctrl;
54 }
55
56 /* --- authentication management functions --- */
57
58 /* Attempt a conversation */
59
60 static int converse(pam_handle_t *pamh, int nargs,
61                     struct pam_message **message,
62                     struct pam_response **response)
63 {
64     int retval;
65     struct pam_conv *conv;
66
67     retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv ) ;
68     if (retval == PAM_SUCCESS) {
69         retval = conv->conv(nargs, (const struct pam_message **)message,
70                             response, conv->appdata_ptr);
71     }
72         
73     return retval; /* propagate error status */
74 }
75
76
77 static int _make_remark(pam_handle_t * pamh, int type, const char *text)
78 {
79         int retval = PAM_SUCCESS;
80
81         struct pam_message *pmsg[1], msg[1];
82         struct pam_response *resp;
83         
84         pmsg[0] = &msg[0];
85         msg[0].msg = text;
86         msg[0].msg_style = type;
87         
88         resp = NULL;
89         retval = converse(pamh, 1, pmsg, &resp);
90         
91         if (resp) {
92                 _pam_drop_reply(resp, 1);
93         }
94         return retval;
95 }
96
97 static int pam_winbind_request(enum winbindd_cmd req_type,
98                                struct winbindd_request *request,
99                                struct winbindd_response *response)
100 {
101
102         /* Fill in request and send down pipe */
103         init_request(request, req_type);
104         
105         if (write_sock(request, sizeof(*request)) == -1) {
106                 _pam_log(LOG_ERR, "write to socket failed!");
107                 close_sock();
108                 return PAM_SERVICE_ERR;
109         }
110         
111         /* Wait for reply */
112         if (read_reply(response) == -1) {
113                 _pam_log(LOG_ERR, "read from socket failed!");
114                 close_sock();
115                 return PAM_SERVICE_ERR;
116         }
117
118         /* We are done with the socket - close it and avoid mischeif */
119         close_sock();
120
121         /* Copy reply data from socket */
122         if (response->result != WINBINDD_OK) {
123                 if (response->data.auth.pam_error != PAM_SUCCESS) {
124                         _pam_log(LOG_ERR, "request failed: %s, PAM error was %d, NT error was %s", 
125                                  response->data.auth.error_string,
126                                  response->data.auth.pam_error,
127                                  response->data.auth.nt_status_string);
128                         return response->data.auth.pam_error;
129                 } else {
130                         _pam_log(LOG_ERR, "request failed, but PAM error 0!");
131                         return PAM_SERVICE_ERR;
132                 }
133         }
134         
135         return PAM_SUCCESS;
136 }
137
138 static int pam_winbind_request_log(enum winbindd_cmd req_type,
139                                struct winbindd_request *request,
140                                struct winbindd_response *response,
141                                    int ctrl,
142                                    const char *user)
143 {
144         int retval;
145
146         retval = pam_winbind_request(req_type, request, response);
147
148         switch (retval) {
149         case PAM_AUTH_ERR:
150                 /* incorrect password */
151                 _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password)", user);
152                 return retval;
153         case PAM_ACCT_EXPIRED:
154                 /* account expired */
155                 _pam_log(LOG_WARNING, "user `%s' account expired", user);
156                 return retval;
157         case PAM_AUTHTOK_EXPIRED:
158                 /* password expired */
159                 _pam_log(LOG_WARNING, "user `%s' password expired", user);
160                 return retval;
161         case PAM_NEW_AUTHTOK_REQD:
162                 /* password expired */
163                 _pam_log(LOG_WARNING, "user `%s' new password required", user);
164                 return retval;
165         case PAM_USER_UNKNOWN:
166                 /* the user does not exist */
167                 if (ctrl & WINBIND_DEBUG_ARG)
168                         _pam_log(LOG_NOTICE, "user `%s' not found",
169                                  user);
170                 if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
171                         return PAM_IGNORE;
172                 }        
173                 return retval;
174         case PAM_SUCCESS:
175                 if (req_type == WINBINDD_PAM_AUTH) {
176                         /* Otherwise, the authentication looked good */
177                         _pam_log(LOG_NOTICE, "user '%s' granted acces", user);
178                 } else if (req_type == WINBINDD_PAM_CHAUTHTOK) {
179                         /* Otherwise, the authentication looked good */
180                         _pam_log(LOG_NOTICE, "user '%s' password changed", user);
181                 } else { 
182                         /* Otherwise, the authentication looked good */
183                         _pam_log(LOG_NOTICE, "user '%s' OK", user);
184                 }
185                 return retval;
186         default:
187                 /* we don't know anything about this return value */
188                 _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
189                          retval, user);
190                 return retval;
191         }
192 }
193
194 /* talk to winbindd */
195 static int winbind_auth_request(const char *user, const char *pass, int ctrl)
196 {
197         struct winbindd_request request;
198         struct winbindd_response response;
199
200         ZERO_STRUCT(request);
201
202         strncpy(request.data.auth.user, user, 
203                 sizeof(request.data.auth.user)-1);
204
205         strncpy(request.data.auth.pass, pass, 
206                 sizeof(request.data.auth.pass)-1);
207         
208         
209         return pam_winbind_request_log(WINBINDD_PAM_AUTH, &request, &response, ctrl, user);
210 }
211
212 /* talk to winbindd */
213 static int winbind_chauthtok_request(const char *user, const char *oldpass,
214                                      const char *newpass, int ctrl)
215 {
216         struct winbindd_request request;
217         struct winbindd_response response;
218
219         ZERO_STRUCT(request);
220
221         if (request.data.chauthtok.user == NULL) return -2;
222
223         strncpy(request.data.chauthtok.user, user, 
224                 sizeof(request.data.chauthtok.user) - 1);
225
226         if (oldpass != NULL) {
227             strncpy(request.data.chauthtok.oldpass, oldpass, 
228                     sizeof(request.data.chauthtok.oldpass) - 1);
229         } else {
230             request.data.chauthtok.oldpass[0] = '\0';
231         }
232         
233         if (newpass != NULL) {
234             strncpy(request.data.chauthtok.newpass, newpass, 
235                     sizeof(request.data.chauthtok.newpass) - 1);
236         } else {
237             request.data.chauthtok.newpass[0] = '\0';
238         }
239         
240         return pam_winbind_request_log(WINBINDD_PAM_CHAUTHTOK, &request, &response, ctrl, user);
241 }
242
243 /*
244  * Checks if a user has an account
245  *
246  * return values:
247  *       1  = User not found
248  *       0  = OK
249  *      -1  = System error
250  */
251 static int valid_user(const char *user)
252 {
253         if (getpwnam(user)) return 0;
254         return 1;
255 }
256
257 static char *_pam_delete(register char *xx)
258 {
259     _pam_overwrite(xx);
260     _pam_drop(xx);
261     return NULL;
262 }
263
264 /*
265  * obtain a password from the user
266  */
267
268 static int _winbind_read_password(pam_handle_t * pamh
269                                   ,unsigned int ctrl
270                                   ,const char *comment
271                                   ,const char *prompt1
272                                   ,const char *prompt2
273                                   ,const char **pass)
274 {
275         int authtok_flag;
276         int retval;
277         const char *item;
278         char *token;
279
280         /*
281          * make sure nothing inappropriate gets returned
282          */
283
284         *pass = token = NULL;
285
286         /*
287          * which authentication token are we getting?
288          */
289
290         authtok_flag = on(WINBIND__OLD_PASSWORD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
291
292         /*
293          * should we obtain the password from a PAM item ?
294          */
295
296         if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
297                 retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
298                 if (retval != PAM_SUCCESS) {
299                         /* very strange. */
300                         _pam_log(LOG_ALERT, 
301                                  "pam_get_item returned error to unix-read-password"
302                             );
303                         return retval;
304                 } else if (item != NULL) {      /* we have a password! */
305                         *pass = item;
306                         item = NULL;
307                         return PAM_SUCCESS;
308                 } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
309                         return PAM_AUTHTOK_RECOVER_ERR;         /* didn't work */
310                 } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
311                            && off(WINBIND__OLD_PASSWORD, ctrl)) {
312                         return PAM_AUTHTOK_RECOVER_ERR;
313                 }
314         }
315         /*
316          * getting here implies we will have to get the password from the
317          * user directly.
318          */
319
320         {
321                 struct pam_message msg[3], *pmsg[3];
322                 struct pam_response *resp;
323                 int i, replies;
324
325                 /* prepare to converse */
326
327                 if (comment != NULL) {
328                         pmsg[0] = &msg[0];
329                         msg[0].msg_style = PAM_TEXT_INFO;
330                         msg[0].msg = comment;
331                         i = 1;
332                 } else {
333                         i = 0;
334                 }
335
336                 pmsg[i] = &msg[i];
337                 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
338                 msg[i++].msg = prompt1;
339                 replies = 1;
340
341                 if (prompt2 != NULL) {
342                         pmsg[i] = &msg[i];
343                         msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
344                         msg[i++].msg = prompt2;
345                         ++replies;
346                 }
347                 /* so call the conversation expecting i responses */
348                 resp = NULL;
349                 retval = converse(pamh, i, pmsg, &resp);
350
351                 if (resp != NULL) {
352
353                         /* interpret the response */
354
355                         if (retval == PAM_SUCCESS) {    /* a good conversation */
356
357                                 token = x_strdup(resp[i - replies].resp);
358                                 if (token != NULL) {
359                                         if (replies == 2) {
360
361                                                 /* verify that password entered correctly */
362                                                 if (!resp[i - 1].resp
363                                                     || strcmp(token, resp[i - 1].resp)) {
364                                                         _pam_delete(token);     /* mistyped */
365                                                         retval = PAM_AUTHTOK_RECOVER_ERR;
366                                                         _make_remark(pamh                                                                   ,PAM_ERROR_MSG, MISTYPED_PASS);
367                                                 }
368                                         }
369                                 } else {
370                                         _pam_log(LOG_NOTICE
371                                                  ,"could not recover authentication token");
372                                 }
373
374                         }
375                         /*
376                          * tidy up the conversation (resp_retcode) is ignored
377                          * -- what is it for anyway? AGM
378                          */
379
380                         _pam_drop_reply(resp, i);
381
382                 } else {
383                         retval = (retval == PAM_SUCCESS)
384                             ? PAM_AUTHTOK_RECOVER_ERR : retval;
385                 }
386         }
387
388         if (retval != PAM_SUCCESS) {
389                 if (on(WINBIND_DEBUG_ARG, ctrl))
390                         _pam_log(LOG_DEBUG,
391                                  "unable to obtain a password");
392                 return retval;
393         }
394         /* 'token' is the entered password */
395
396         /* we store this password as an item */
397         
398         retval = pam_set_item(pamh, authtok_flag, token);
399         _pam_delete(token);     /* clean it up */
400         if (retval != PAM_SUCCESS
401             || (retval = pam_get_item(pamh, authtok_flag
402                                       ,(const void **) &item))
403             != PAM_SUCCESS) {
404                 
405                 _pam_log(LOG_CRIT, "error manipulating password");
406                 return retval;
407                 
408         }
409
410         *pass = item;
411         item = NULL;            /* break link to password */
412
413         return PAM_SUCCESS;
414 }
415
416 PAM_EXTERN
417 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
418                         int argc, const char **argv)
419 {
420      const char *username;
421      const char *password;
422      int retval = PAM_AUTH_ERR;
423     
424      /* parse arguments */
425      int ctrl = _pam_parse(argc, argv);
426
427      /* Get the username */
428      retval = pam_get_user(pamh, &username, NULL);
429      if ((retval != PAM_SUCCESS) || (!username)) {
430         if (ctrl & WINBIND_DEBUG_ARG)
431             _pam_log(LOG_DEBUG,"can not get the username");
432         return PAM_SERVICE_ERR;
433      }
434      
435      retval = _winbind_read_password(pamh, ctrl, NULL, 
436                                      "Password: ", NULL,
437                                      &password);
438      
439      if (retval != PAM_SUCCESS) {
440          _pam_log(LOG_ERR, "Could not retrieve user's password");
441          return PAM_AUTHTOK_ERR;
442      }
443      
444      if (ctrl & WINBIND_DEBUG_ARG) {
445
446              /* Let's not give too much away in the log file */
447
448 #ifdef DEBUG_PASSWORD
449          _pam_log(LOG_INFO, "Verify user `%s' with password `%s'",
450                   username, password);
451 #else
452          _pam_log(LOG_INFO, "Verify user `%s'", username);
453 #endif
454      }
455
456      /* Now use the username to look up password */
457      return winbind_auth_request(username, password, ctrl);
458 }
459
460 PAM_EXTERN
461 int pam_sm_setcred(pam_handle_t *pamh, int flags,
462                    int argc, const char **argv)
463 {
464     return PAM_SUCCESS;
465 }
466
467 /*
468  * Account management. We want to verify that the account exists 
469  * before returning PAM_SUCCESS
470  */
471 PAM_EXTERN
472 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
473                    int argc, const char **argv)
474 {
475     const char *username;
476     int retval = PAM_USER_UNKNOWN;
477
478     /* parse arguments */
479     int ctrl = _pam_parse(argc, argv);
480
481     /* Get the username */
482     retval = pam_get_user(pamh, &username, NULL);
483     if ((retval != PAM_SUCCESS) || (!username)) {
484         if (ctrl & WINBIND_DEBUG_ARG)
485             _pam_log(LOG_DEBUG,"can not get the username");
486         return PAM_SERVICE_ERR;
487     }
488
489     /* Verify the username */
490     retval = valid_user(username);
491     switch (retval) {
492         case -1:
493             /* some sort of system error. The log was already printed */
494             return PAM_SERVICE_ERR;
495         case 1:
496             /* the user does not exist */
497             if (ctrl & WINBIND_DEBUG_ARG)
498                 _pam_log(LOG_NOTICE, "user `%s' not found",
499                          username);
500             if (ctrl & WINBIND_UNKNOWN_OK_ARG)
501                 return PAM_IGNORE;
502             return PAM_USER_UNKNOWN;
503         case 0:
504             /* Otherwise, the authentication looked good */
505             _pam_log(LOG_NOTICE, "user '%s' granted acces", username);
506             return PAM_SUCCESS;
507         default:
508             /* we don't know anything about this return value */
509             _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
510                      retval, username);
511             return PAM_SERVICE_ERR;
512     }
513     
514     /* should not be reached */
515     return PAM_IGNORE;
516 }
517 PAM_EXTERN
518 int pam_sm_open_session(pam_handle_t *pamh, int flags,
519                 int argc, const char **argv)
520 {
521         /* parse arguments */
522         int ctrl = _pam_parse(argc, argv);
523         if (ctrl & WINBIND_DEBUG_ARG)
524               _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_open_session handler");
525         return PAM_SUCCESS;
526 }
527 PAM_EXTERN
528 int pam_sm_close_session(pam_handle_t *pamh, int flags,
529                 int argc, const char **argv)
530 {
531         /* parse arguments */
532         int ctrl = _pam_parse(argc, argv);
533         if (ctrl & WINBIND_DEBUG_ARG)
534               _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_close_session handler");
535         return PAM_SUCCESS;
536 }
537
538
539
540 PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
541                                 int argc, const char **argv)
542 {
543         unsigned int lctrl;
544         int retval;
545         unsigned int ctrl = _pam_parse(argc, argv);
546
547         /* <DO NOT free() THESE> */
548         const char *user;
549         char *pass_old, *pass_new;
550         /* </DO NOT free() THESE> */
551
552         char *Announce;
553         
554         int retry = 0;
555
556         /*
557          * First get the name of a user
558          */
559         retval = pam_get_user(pamh, &user, "Username: ");
560         if (retval == PAM_SUCCESS) {
561                 if (user == NULL) {
562                         _pam_log(LOG_ERR, "username was NULL!");
563                         return PAM_USER_UNKNOWN;
564                 }
565                 if (retval == PAM_SUCCESS && on(WINBIND_DEBUG_ARG, ctrl))
566                         _pam_log(LOG_DEBUG, "username [%s] obtained",
567                                  user);
568         } else {
569                 if (on(WINBIND_DEBUG_ARG, ctrl))
570                         _pam_log(LOG_DEBUG,
571                                  "password - could not identify user");
572                 return retval;
573         }
574
575         /*
576          * obtain and verify the current password (OLDAUTHTOK) for
577          * the user.
578          */
579
580         if (flags & PAM_PRELIM_CHECK) {
581                 
582                 /* instruct user what is happening */
583 #define greeting "Changing password for "
584                 Announce = (char *) malloc(sizeof(greeting) + strlen(user));
585                 if (Announce == NULL) {
586                 _pam_log(LOG_CRIT, 
587                          "password - out of memory");
588                 return PAM_BUF_ERR;
589                 }
590                 (void) strcpy(Announce, greeting);
591                 (void) strcpy(Announce + sizeof(greeting) - 1, user);
592 #undef greeting
593                 
594                 lctrl = ctrl | WINBIND__OLD_PASSWORD;
595                 retval = _winbind_read_password(pamh, lctrl
596                                                 ,Announce
597                                                 ,"(current) NT password: "
598                                                 ,NULL
599                                                 ,(const char **) &pass_old);
600                 free(Announce);
601                 
602                 if (retval != PAM_SUCCESS) {
603                         _pam_log(LOG_NOTICE
604                                  ,"password - (old) token not obtained");
605                         return retval;
606                 }
607                 /* verify that this is the password for this user */
608                 
609                 retval = winbind_auth_request(user, pass_old, ctrl);
610                 
611                 if (retval != PAM_ACCT_EXPIRED 
612                     && retval != PAM_AUTHTOK_EXPIRED
613                     && retval != PAM_NEW_AUTHTOK_REQD 
614                     && retval != PAM_SUCCESS) {
615                         pass_old = NULL;
616                         return retval;
617                 }
618                 
619                 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
620                 pass_old = NULL;
621                 if (retval != PAM_SUCCESS) {
622                         _pam_log(LOG_CRIT, 
623                                  "failed to set PAM_OLDAUTHTOK");
624                 }
625         } else if (flags & PAM_UPDATE_AUTHTOK) {
626         
627                 /*
628                  * obtain the proposed password
629                  */
630                 
631                 /*
632                  * get the old token back. 
633                  */
634                 
635                 retval = pam_get_item(pamh, PAM_OLDAUTHTOK
636                                       ,(const void **) &pass_old);
637                 
638                 if (retval != PAM_SUCCESS) {
639                         _pam_log(LOG_NOTICE, "user not authenticated");
640                         return retval;
641                 }
642                 
643                 lctrl = ctrl;
644                 
645                 if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
646                         ctrl = WINBIND_USE_FIRST_PASS_ARG | lctrl;
647                 }
648                 retry = 0;
649                 retval = PAM_AUTHTOK_ERR;
650                 while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
651                         /*
652                          * use_authtok is to force the use of a previously entered
653                          * password -- needed for pluggable password strength checking
654                          */
655                         
656                         retval = _winbind_read_password(pamh, lctrl
657                                                         ,NULL
658                                                         ,"Enter new NT password: "
659                                                         ,"Retype new NT password: "
660                                                         ,(const char **) &pass_new);
661                         
662                         if (retval != PAM_SUCCESS) {
663                                 if (on(WINBIND_DEBUG_ARG, ctrl)) {
664                                         _pam_log(LOG_ALERT
665                                                  ,"password - new password not obtained");
666                                 }
667                                 pass_old = NULL;/* tidy up */
668                                 return retval;
669                         }
670
671                         /*
672                          * At this point we know who the user is and what they
673                          * propose as their new password. Verify that the new
674                          * password is acceptable.
675                          */
676                         
677                         if (pass_new[0] == '\0') {/* "\0" password = NULL */
678                                 pass_new = NULL;
679                         }
680                 }
681                 
682                 /*
683                  * By reaching here we have approved the passwords and must now
684                  * rebuild the password database file.
685                  */
686
687                 retval = winbind_chauthtok_request(user, pass_old, pass_new, ctrl);
688                 _pam_overwrite(pass_new);
689                 _pam_overwrite(pass_old);
690                 pass_old = pass_new = NULL;
691         } else {
692                 retval = PAM_SERVICE_ERR;
693         }
694         
695         return retval;
696 }
697
698 #ifdef PAM_STATIC
699
700 /* static module data */
701
702 struct pam_module _pam_winbind_modstruct = {
703      MODULE_NAME,
704      pam_sm_authenticate,
705      pam_sm_setcred,
706      pam_sm_acct_mgmt,
707      pam_sm_open_session,
708      pam_sm_close_session,
709      pam_sm_chauthtok
710 };
711
712 #endif
713
714 /*
715  * Copyright (c) Andrew Tridgell  <tridge@samba.org>   2000
716  * Copyright (c) Tim Potter       <tpot@samba.org>     2000
717  * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
718  * Copyright (c) Jan Rêkorajski 1999.
719  * Copyright (c) Andrew G. Morgan 1996-8.
720  * Copyright (c) Alex O. Yuriev, 1996.
721  * Copyright (c) Cristian Gafton 1996.
722  * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software. 
723  *
724  * Redistribution and use in source and binary forms, with or without
725  * modification, are permitted provided that the following conditions
726  * are met:
727  * 1. Redistributions of source code must retain the above copyright
728  *    notice, and the entire permission notice in its entirety,
729  *    including the disclaimer of warranties.
730  * 2. Redistributions in binary form must reproduce the above copyright
731  *    notice, this list of conditions and the following disclaimer in the
732  *    documentation and/or other materials provided with the distribution.
733  * 3. The name of the author may not be used to endorse or promote
734  *    products derived from this software without specific prior
735  *    written permission.
736  *
737  * ALTERNATIVELY, this product may be distributed under the terms of
738  * the GNU Public License, in which case the provisions of the GPL are
739  * required INSTEAD OF the above restrictions.  (This clause is
740  * necessary due to a potential bad interaction between the GPL and
741  * the restrictions contained in a BSD-style copyright.)
742  *
743  * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
744  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
745  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
746  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
747  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
748  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
749  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
750  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
751  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
752  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
753  * OF THE POSSIBILITY OF SUCH DAMAGE.
754  */