r10400: commit merge patch from jra
[Samba/gbeck.git] / source / nsswitch / pam_winbind.c
bloba87ccb4972e18dd7a36bdfc2a3afd9e6b8c4bf3d
1 /* pam_winbind module
3 Copyright Andrew Tridgell <tridge@samba.org> 2000
4 Copyright Tim Potter <tpot@samba.org> 2000
5 Copyright Andrew Bartlett <abartlet@samba.org> 2002
7 largely based on pam_userdb by Cristian 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)
12 #include "pam_winbind.h"
14 /* data tokens */
16 #define MAX_PASSWD_TRIES 3
18 /* some syslogging */
19 static void _pam_log(int err, const char *format, ...)
21 va_list args;
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();
30 static int _pam_parse(int argc, const char **argv)
32 int ctrl;
33 /* step through arguments */
34 for (ctrl = 0; argc-- > 0; ++argv) {
36 /* generic options */
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 if (!strncasecmp(*argv, "require_membership_of", strlen("require_membership_of")))
49 ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
50 else if (!strncasecmp(*argv, "require-membership-of", strlen("require-membership-of")))
51 ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
52 else {
53 _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
57 return ctrl;
60 /* --- authentication management functions --- */
62 /* Attempt a conversation */
64 static int converse(pam_handle_t *pamh, int nargs,
65 struct pam_message **message,
66 struct pam_response **response)
68 int retval;
69 struct pam_conv *conv;
71 retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv ) ;
72 if (retval == PAM_SUCCESS) {
73 retval = conv->conv(nargs, (const struct pam_message **)message,
74 response, conv->appdata_ptr);
77 return retval; /* propagate error status */
81 static int _make_remark(pam_handle_t * pamh, int type, const char *text)
83 int retval = PAM_SUCCESS;
85 struct pam_message *pmsg[1], msg[1];
86 struct pam_response *resp;
88 pmsg[0] = &msg[0];
89 msg[0].msg = text;
90 msg[0].msg_style = type;
92 resp = NULL;
93 retval = converse(pamh, 1, pmsg, &resp);
95 if (resp) {
96 _pam_drop_reply(resp, 1);
98 return retval;
101 static int pam_winbind_request(enum winbindd_cmd req_type,
102 struct winbindd_request *request,
103 struct winbindd_response *response)
106 /* Fill in request and send down pipe */
107 init_request(request, req_type);
109 if (write_sock(request, sizeof(*request), 0) == -1) {
110 _pam_log(LOG_ERR, "write to socket failed!");
111 close_sock();
112 return PAM_SERVICE_ERR;
115 /* Wait for reply */
116 if (read_reply(response) == -1) {
117 _pam_log(LOG_ERR, "read from socket failed!");
118 close_sock();
119 return PAM_SERVICE_ERR;
122 /* We are done with the socket - close it and avoid mischeif */
123 close_sock();
125 /* Copy reply data from socket */
126 if (response->result != WINBINDD_OK) {
127 if (response->data.auth.pam_error != PAM_SUCCESS) {
128 _pam_log(LOG_ERR, "request failed: %s, PAM error was %d, NT error was %s",
129 response->data.auth.error_string,
130 response->data.auth.pam_error,
131 response->data.auth.nt_status_string);
132 return response->data.auth.pam_error;
133 } else {
134 _pam_log(LOG_ERR, "request failed, but PAM error 0!");
135 return PAM_SERVICE_ERR;
139 return PAM_SUCCESS;
142 static int pam_winbind_request_log(enum winbindd_cmd req_type,
143 struct winbindd_request *request,
144 struct winbindd_response *response,
145 int ctrl,
146 const char *user)
148 int retval;
150 retval = pam_winbind_request(req_type, request, response);
152 switch (retval) {
153 case PAM_AUTH_ERR:
154 /* incorrect password */
155 _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password or invalid membership)", user);
156 return retval;
157 case PAM_ACCT_EXPIRED:
158 /* account expired */
159 _pam_log(LOG_WARNING, "user `%s' account expired", user);
160 return retval;
161 case PAM_AUTHTOK_EXPIRED:
162 /* password expired */
163 _pam_log(LOG_WARNING, "user `%s' password expired", user);
164 return retval;
165 case PAM_NEW_AUTHTOK_REQD:
166 /* password expired */
167 _pam_log(LOG_WARNING, "user `%s' new password required", user);
168 return retval;
169 case PAM_USER_UNKNOWN:
170 /* the user does not exist */
171 if (ctrl & WINBIND_DEBUG_ARG)
172 _pam_log(LOG_NOTICE, "user `%s' not found",
173 user);
174 if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
175 return PAM_IGNORE;
177 return retval;
178 case PAM_SUCCESS:
179 if (req_type == WINBINDD_PAM_AUTH) {
180 /* Otherwise, the authentication looked good */
181 _pam_log(LOG_NOTICE, "user '%s' granted access", user);
182 } else if (req_type == WINBINDD_PAM_CHAUTHTOK) {
183 /* Otherwise, the authentication looked good */
184 _pam_log(LOG_NOTICE, "user '%s' password changed", user);
185 } else {
186 /* Otherwise, the authentication looked good */
187 _pam_log(LOG_NOTICE, "user '%s' OK", user);
189 return retval;
190 default:
191 /* we don't know anything about this return value */
192 _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s')",
193 retval, user);
194 return retval;
198 /* talk to winbindd */
199 static int winbind_auth_request(const char *user, const char *pass, const char *member, int ctrl)
201 struct winbindd_request request;
202 struct winbindd_response response;
204 ZERO_STRUCT(request);
206 strncpy(request.data.auth.user, user,
207 sizeof(request.data.auth.user)-1);
209 strncpy(request.data.auth.pass, pass,
210 sizeof(request.data.auth.pass)-1);
212 if (member == NULL )
213 return pam_winbind_request_log(WINBINDD_PAM_AUTH, &request, &response, ctrl, user);
215 /* lookup name? */
216 if (!strncmp("S-", member, 2) == 0) {
218 struct winbindd_request sid_request;
219 struct winbindd_response sid_response;
221 ZERO_STRUCT(sid_request);
222 ZERO_STRUCT(sid_response);
224 if (ctrl & WINBIND_DEBUG_ARG)
225 _pam_log(LOG_DEBUG, "no sid given, looking up: %s\n", member);
227 /* fortunatly winbindd can handle non-separated names */
228 strcpy(sid_request.data.name.name, member);
230 if (pam_winbind_request_log(WINBINDD_LOOKUPNAME, &sid_request, &sid_response, ctrl, user)) {
231 _pam_log(LOG_INFO, "could not lookup name: %s\n", member);
232 return PAM_AUTH_ERR;
235 member = sid_response.data.sid.sid;
238 strncpy(request.data.auth.require_membership_of_sid, member,
239 sizeof(request.data.auth.require_membership_of_sid)-1);
241 return pam_winbind_request_log(WINBINDD_PAM_AUTH, &request, &response, ctrl, user);
244 /* talk to winbindd */
245 static int winbind_chauthtok_request(const char *user, const char *oldpass,
246 const char *newpass, int ctrl)
248 struct winbindd_request request;
249 struct winbindd_response response;
251 ZERO_STRUCT(request);
253 if (request.data.chauthtok.user == NULL) return -2;
255 strncpy(request.data.chauthtok.user, user,
256 sizeof(request.data.chauthtok.user) - 1);
258 if (oldpass != NULL) {
259 strncpy(request.data.chauthtok.oldpass, oldpass,
260 sizeof(request.data.chauthtok.oldpass) - 1);
261 } else {
262 request.data.chauthtok.oldpass[0] = '\0';
265 if (newpass != NULL) {
266 strncpy(request.data.chauthtok.newpass, newpass,
267 sizeof(request.data.chauthtok.newpass) - 1);
268 } else {
269 request.data.chauthtok.newpass[0] = '\0';
272 return pam_winbind_request_log(WINBINDD_PAM_CHAUTHTOK, &request, &response, ctrl, user);
276 * Checks if a user has an account
278 * return values:
279 * 1 = User not found
280 * 0 = OK
281 * -1 = System error
283 static int valid_user(const char *user)
285 if (getpwnam(user)) return 0;
286 return 1;
289 static char *_pam_delete(register char *xx)
291 _pam_overwrite(xx);
292 _pam_drop(xx);
293 return NULL;
297 * obtain a password from the user
300 static int _winbind_read_password(pam_handle_t * pamh
301 ,unsigned int ctrl
302 ,const char *comment
303 ,const char *prompt1
304 ,const char *prompt2
305 ,const char **pass)
307 int authtok_flag;
308 int retval;
309 const char *item;
310 char *token;
313 * make sure nothing inappropriate gets returned
316 *pass = token = NULL;
319 * which authentication token are we getting?
322 authtok_flag = on(WINBIND__OLD_PASSWORD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
325 * should we obtain the password from a PAM item ?
328 if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
329 retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
330 if (retval != PAM_SUCCESS) {
331 /* very strange. */
332 _pam_log(LOG_ALERT,
333 "pam_get_item returned error to unix-read-password"
335 return retval;
336 } else if (item != NULL) { /* we have a password! */
337 *pass = item;
338 item = NULL;
339 return PAM_SUCCESS;
340 } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
341 return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
342 } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
343 && off(WINBIND__OLD_PASSWORD, ctrl)) {
344 return PAM_AUTHTOK_RECOVER_ERR;
348 * getting here implies we will have to get the password from the
349 * user directly.
353 struct pam_message msg[3], *pmsg[3];
354 struct pam_response *resp;
355 int i, replies;
357 /* prepare to converse */
359 if (comment != NULL) {
360 pmsg[0] = &msg[0];
361 msg[0].msg_style = PAM_TEXT_INFO;
362 msg[0].msg = comment;
363 i = 1;
364 } else {
365 i = 0;
368 pmsg[i] = &msg[i];
369 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
370 msg[i++].msg = prompt1;
371 replies = 1;
373 if (prompt2 != NULL) {
374 pmsg[i] = &msg[i];
375 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
376 msg[i++].msg = prompt2;
377 ++replies;
379 /* so call the conversation expecting i responses */
380 resp = NULL;
381 retval = converse(pamh, i, pmsg, &resp);
383 if (resp != NULL) {
385 /* interpret the response */
387 if (retval == PAM_SUCCESS) { /* a good conversation */
389 token = x_strdup(resp[i - replies].resp);
390 if (token != NULL) {
391 if (replies == 2) {
393 /* verify that password entered correctly */
394 if (!resp[i - 1].resp
395 || strcmp(token, resp[i - 1].resp)) {
396 _pam_delete(token); /* mistyped */
397 retval = PAM_AUTHTOK_RECOVER_ERR;
398 _make_remark(pamh ,PAM_ERROR_MSG, MISTYPED_PASS);
401 } else {
402 _pam_log(LOG_NOTICE
403 ,"could not recover authentication token");
408 * tidy up the conversation (resp_retcode) is ignored
409 * -- what is it for anyway? AGM
412 _pam_drop_reply(resp, i);
414 } else {
415 retval = (retval == PAM_SUCCESS)
416 ? PAM_AUTHTOK_RECOVER_ERR : retval;
420 if (retval != PAM_SUCCESS) {
421 if (on(WINBIND_DEBUG_ARG, ctrl))
422 _pam_log(LOG_DEBUG,
423 "unable to obtain a password");
424 return retval;
426 /* 'token' is the entered password */
428 /* we store this password as an item */
430 retval = pam_set_item(pamh, authtok_flag, token);
431 _pam_delete(token); /* clean it up */
432 if (retval != PAM_SUCCESS
433 || (retval = pam_get_item(pamh, authtok_flag
434 ,(const void **) &item))
435 != PAM_SUCCESS) {
437 _pam_log(LOG_CRIT, "error manipulating password");
438 return retval;
442 *pass = item;
443 item = NULL; /* break link to password */
445 return PAM_SUCCESS;
448 PAM_EXTERN
449 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
450 int argc, const char **argv)
452 const char *username;
453 const char *password;
454 const char *member = NULL;
455 int retval = PAM_AUTH_ERR;
456 int i;
458 /* parse arguments */
459 int ctrl = _pam_parse(argc, argv);
461 /* Get the username */
462 retval = pam_get_user(pamh, &username, NULL);
463 if ((retval != PAM_SUCCESS) || (!username)) {
464 if (ctrl & WINBIND_DEBUG_ARG)
465 _pam_log(LOG_DEBUG,"can not get the username");
466 return PAM_SERVICE_ERR;
469 retval = _winbind_read_password(pamh, ctrl, NULL,
470 "Password: ", NULL,
471 &password);
473 if (retval != PAM_SUCCESS) {
474 _pam_log(LOG_ERR, "Could not retrieve user's password");
475 return PAM_AUTHTOK_ERR;
478 if (ctrl & WINBIND_DEBUG_ARG) {
480 /* Let's not give too much away in the log file */
482 #ifdef DEBUG_PASSWORD
483 _pam_log(LOG_INFO, "Verify user `%s' with password `%s'",
484 username, password);
485 #else
486 _pam_log(LOG_INFO, "Verify user `%s'", username);
487 #endif
490 if (ctrl & WINBIND_REQUIRED_MEMBERSHIP) {
492 for ( i=0; i<argc; i++ ) {
494 if ((strncmp(argv[i], "require_membership_of", strlen("require_membership_of")) == 0) ||
495 (strncmp(argv[i], "require-membership-of", strlen("require-membership-of")) == 0)) {
497 char *p;
498 char *parm = strdup(argv[i]);
500 if ( (p = strchr( parm, '=' )) == NULL) {
501 _pam_log(LOG_INFO, "no \"=\" delimiter for \"require_membership_of\" found\n");
502 break;
505 member = strdup(p+1);
510 /* Now use the username to look up password */
511 return winbind_auth_request(username, password, member, ctrl);
514 PAM_EXTERN
515 int pam_sm_setcred(pam_handle_t *pamh, int flags,
516 int argc, const char **argv)
518 return PAM_SUCCESS;
522 * Account management. We want to verify that the account exists
523 * before returning PAM_SUCCESS
525 PAM_EXTERN
526 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
527 int argc, const char **argv)
529 const char *username;
530 int retval = PAM_USER_UNKNOWN;
532 /* parse arguments */
533 int ctrl = _pam_parse(argc, argv);
535 /* Get the username */
536 retval = pam_get_user(pamh, &username, NULL);
537 if ((retval != PAM_SUCCESS) || (!username)) {
538 if (ctrl & WINBIND_DEBUG_ARG)
539 _pam_log(LOG_DEBUG,"can not get the username");
540 return PAM_SERVICE_ERR;
543 /* Verify the username */
544 retval = valid_user(username);
545 switch (retval) {
546 case -1:
547 /* some sort of system error. The log was already printed */
548 return PAM_SERVICE_ERR;
549 case 1:
550 /* the user does not exist */
551 if (ctrl & WINBIND_DEBUG_ARG)
552 _pam_log(LOG_NOTICE, "user `%s' not found",
553 username);
554 if (ctrl & WINBIND_UNKNOWN_OK_ARG)
555 return PAM_IGNORE;
556 return PAM_USER_UNKNOWN;
557 case 0:
558 /* Otherwise, the authentication looked good */
559 _pam_log(LOG_NOTICE, "user '%s' granted access", username);
560 return PAM_SUCCESS;
561 default:
562 /* we don't know anything about this return value */
563 _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
564 retval, username);
565 return PAM_SERVICE_ERR;
568 /* should not be reached */
569 return PAM_IGNORE;
571 PAM_EXTERN
572 int pam_sm_open_session(pam_handle_t *pamh, int flags,
573 int argc, const char **argv)
575 /* parse arguments */
576 int ctrl = _pam_parse(argc, argv);
577 if (ctrl & WINBIND_DEBUG_ARG)
578 _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_open_session handler");
579 return PAM_SUCCESS;
581 PAM_EXTERN
582 int pam_sm_close_session(pam_handle_t *pamh, int flags,
583 int argc, const char **argv)
585 /* parse arguments */
586 int ctrl = _pam_parse(argc, argv);
587 if (ctrl & WINBIND_DEBUG_ARG)
588 _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_close_session handler");
589 return PAM_SUCCESS;
594 PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
595 int argc, const char **argv)
597 unsigned int lctrl;
598 int retval;
599 unsigned int ctrl = _pam_parse(argc, argv);
601 /* <DO NOT free() THESE> */
602 const char *user;
603 const char *member = NULL;
604 char *pass_old, *pass_new;
605 /* </DO NOT free() THESE> */
607 char *Announce;
609 int retry = 0;
612 * First get the name of a user
614 retval = pam_get_user(pamh, &user, "Username: ");
615 if (retval == PAM_SUCCESS) {
616 if (user == NULL) {
617 _pam_log(LOG_ERR, "username was NULL!");
618 return PAM_USER_UNKNOWN;
620 if (retval == PAM_SUCCESS && on(WINBIND_DEBUG_ARG, ctrl))
621 _pam_log(LOG_DEBUG, "username [%s] obtained",
622 user);
623 } else {
624 if (on(WINBIND_DEBUG_ARG, ctrl))
625 _pam_log(LOG_DEBUG,
626 "password - could not identify user");
627 return retval;
631 * obtain and verify the current password (OLDAUTHTOK) for
632 * the user.
635 if (flags & PAM_PRELIM_CHECK) {
637 /* instruct user what is happening */
638 #define greeting "Changing password for "
639 Announce = (char *) malloc(sizeof(greeting) + strlen(user));
640 if (Announce == NULL) {
641 _pam_log(LOG_CRIT,
642 "password - out of memory");
643 return PAM_BUF_ERR;
645 (void) strcpy(Announce, greeting);
646 (void) strcpy(Announce + sizeof(greeting) - 1, user);
647 #undef greeting
649 lctrl = ctrl | WINBIND__OLD_PASSWORD;
650 retval = _winbind_read_password(pamh, lctrl
651 ,Announce
652 ,"(current) NT password: "
653 ,NULL
654 ,(const char **) &pass_old);
655 free(Announce);
657 if (retval != PAM_SUCCESS) {
658 _pam_log(LOG_NOTICE
659 ,"password - (old) token not obtained");
660 return retval;
662 /* verify that this is the password for this user */
664 retval = winbind_auth_request(user, pass_old, member, ctrl);
666 if (retval != PAM_ACCT_EXPIRED
667 && retval != PAM_AUTHTOK_EXPIRED
668 && retval != PAM_NEW_AUTHTOK_REQD
669 && retval != PAM_SUCCESS) {
670 pass_old = NULL;
671 return retval;
674 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
675 pass_old = NULL;
676 if (retval != PAM_SUCCESS) {
677 _pam_log(LOG_CRIT,
678 "failed to set PAM_OLDAUTHTOK");
680 } else if (flags & PAM_UPDATE_AUTHTOK) {
683 * obtain the proposed password
687 * get the old token back.
690 retval = pam_get_item(pamh, PAM_OLDAUTHTOK
691 ,(const void **) &pass_old);
693 if (retval != PAM_SUCCESS) {
694 _pam_log(LOG_NOTICE, "user not authenticated");
695 return retval;
698 lctrl = ctrl;
700 if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
701 lctrl |= WINBIND_USE_FIRST_PASS_ARG;
703 retry = 0;
704 retval = PAM_AUTHTOK_ERR;
705 while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
707 * use_authtok is to force the use of a previously entered
708 * password -- needed for pluggable password strength checking
711 retval = _winbind_read_password(pamh, lctrl
712 ,NULL
713 ,"Enter new NT password: "
714 ,"Retype new NT password: "
715 ,(const char **) &pass_new);
717 if (retval != PAM_SUCCESS) {
718 if (on(WINBIND_DEBUG_ARG, ctrl)) {
719 _pam_log(LOG_ALERT
720 ,"password - new password not obtained");
722 pass_old = NULL;/* tidy up */
723 return retval;
727 * At this point we know who the user is and what they
728 * propose as their new password. Verify that the new
729 * password is acceptable.
732 if (pass_new[0] == '\0') {/* "\0" password = NULL */
733 pass_new = NULL;
738 * By reaching here we have approved the passwords and must now
739 * rebuild the password database file.
742 retval = winbind_chauthtok_request(user, pass_old, pass_new, ctrl);
743 _pam_overwrite(pass_new);
744 _pam_overwrite(pass_old);
745 pass_old = pass_new = NULL;
746 } else {
747 retval = PAM_SERVICE_ERR;
750 return retval;
753 #ifdef PAM_STATIC
755 /* static module data */
757 struct pam_module _pam_winbind_modstruct = {
758 MODULE_NAME,
759 pam_sm_authenticate,
760 pam_sm_setcred,
761 pam_sm_acct_mgmt,
762 pam_sm_open_session,
763 pam_sm_close_session,
764 pam_sm_chauthtok
767 #endif
770 * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
771 * Copyright (c) Tim Potter <tpot@samba.org> 2000
772 * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
773 * Copyright (c) Jan Rêkorajski 1999.
774 * Copyright (c) Andrew G. Morgan 1996-8.
775 * Copyright (c) Alex O. Yuriev, 1996.
776 * Copyright (c) Cristian Gafton 1996.
777 * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
779 * Redistribution and use in source and binary forms, with or without
780 * modification, are permitted provided that the following conditions
781 * are met:
782 * 1. Redistributions of source code must retain the above copyright
783 * notice, and the entire permission notice in its entirety,
784 * including the disclaimer of warranties.
785 * 2. Redistributions in binary form must reproduce the above copyright
786 * notice, this list of conditions and the following disclaimer in the
787 * documentation and/or other materials provided with the distribution.
788 * 3. The name of the author may not be used to endorse or promote
789 * products derived from this software without specific prior
790 * written permission.
792 * ALTERNATIVELY, this product may be distributed under the terms of
793 * the GNU Public License, in which case the provisions of the GPL are
794 * required INSTEAD OF the above restrictions. (This clause is
795 * necessary due to a potential bad interaction between the GPL and
796 * the restrictions contained in a BSD-style copyright.)
798 * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
799 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
800 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
801 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
802 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
803 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
804 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
805 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
806 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
807 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
808 * OF THE POSSIBILITY OF SUCH DAMAGE.