4 * Copyright information at end of file.
22 #include <rpcsvc/ypclnt.h>
24 #include <security/_pam_macros.h>
25 #include <security/pam_modules.h>
26 #include <security/_pam_modutil.h>
31 extern char *crypt(const char *key, const char *salt);
32 extern char *bigcrypt(const char *key, const char *salt);
34 /* syslogging function for errors and other information */
36 void _log_err(int err, pam_handle_t *pamh, const char *format,...)
42 pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
44 strncpy(logname, service, sizeof(logname));
45 logname[sizeof(logname) - 1 - strlen("(pam_unix)")] = '\0';
46 strncat(logname, "(pam_unix)", strlen("(pam_unix)"));
48 strncpy(logname, "pam_unix", sizeof(logname) - 1);
51 va_start(args, format);
52 openlog(logname, LOG_CONS | LOG_PID, LOG_AUTH);
53 vsyslog(err, format, args);
58 /* this is a front-end for module-application conversations */
60 static int converse(pam_handle_t * pamh, int ctrl, int nargs
61 ,struct pam_message **message
62 ,struct pam_response **response)
65 struct pam_conv *conv;
67 D(("begin to converse"));
69 retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
70 if (retval == PAM_SUCCESS) {
72 retval = conv->conv(nargs, (const struct pam_message **) message
73 ,response, conv->appdata_ptr);
75 D(("returned from application's conversation function"));
77 if (retval != PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) {
78 _log_err(LOG_DEBUG, pamh, "conversation failure [%s]"
79 ,pam_strerror(pamh, retval));
81 } else if (retval != PAM_CONV_AGAIN) {
82 _log_err(LOG_ERR, pamh
83 ,"couldn't obtain coversation function [%s]"
84 ,pam_strerror(pamh, retval));
86 D(("ready to return from module conversation"));
88 return retval; /* propagate error status */
91 int _make_remark(pam_handle_t * pamh, unsigned int ctrl
92 ,int type, const char *text)
94 int retval = PAM_SUCCESS;
96 if (off(UNIX__QUIET, ctrl)) {
97 struct pam_message *pmsg[1], msg[1];
98 struct pam_response *resp;
102 msg[0].msg_style = type;
105 retval = converse(pamh, ctrl, 1, pmsg, &resp);
108 _pam_drop_reply(resp, 1);
115 * set the control flags for the UNIX module.
118 int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc,
125 ctrl = UNIX_DEFAULTS; /* the default selection of options */
127 /* set some flags manually */
129 if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
131 set(UNIX__IAMROOT, ctrl);
133 if (flags & PAM_UPDATE_AUTHTOK) {
134 D(("UPDATE_AUTHTOK"));
135 set(UNIX__UPDATE, ctrl);
137 if (flags & PAM_PRELIM_CHECK) {
139 set(UNIX__PRELIM, ctrl);
141 if (flags & PAM_SILENT) {
143 set(UNIX__QUIET, ctrl);
145 /* now parse the arguments to this module */
150 D(("pam_unix arg: %s", *argv));
152 for (j = 0; j < UNIX_CTRLS_; ++j) {
153 if (unix_args[j].token
154 && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) {
159 if (j >= UNIX_CTRLS_) {
160 _log_err(LOG_ERR, pamh,
161 "unrecognized option [%s]", *argv);
163 ctrl &= unix_args[j].mask; /* for turning things off */
164 ctrl |= unix_args[j].flag; /* for turning things on */
166 if (remember != NULL) {
167 if (j == UNIX_REMEMBER_PASSWD) {
168 *remember = strtol(*argv + 9, NULL, 10);
169 if ((*remember == INT_MIN) || (*remember == INT_MAX))
177 ++argv; /* step to next argument */
180 if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
181 D(("DISALLOW_NULL_AUTHTOK"));
182 set(UNIX__NONULL, ctrl);
185 /* auditing is a more sensitive version of debug */
187 if (on(UNIX_AUDIT, ctrl)) {
188 set(UNIX_DEBUG, ctrl);
190 /* return the set of flags */
196 static void _cleanup(pam_handle_t * pamh, void *x, int error_status)
201 /* ************************************************************** *
202 * Useful non-trivial functions *
203 * ************************************************************** */
206 * the following is used to keep track of the number of times a user fails
207 * to authenticate themself.
210 #define FAIL_PREFIX "-UN*X-FAIL-"
211 #define UNIX_MAX_RETRIES 3
213 struct _pam_failed_auth {
214 char *user; /* user that's failed to be authenticated */
215 char *name; /* attempt from user with name */
216 int uid; /* uid of calling user */
217 int euid; /* euid of calling process */
218 int count; /* number of failures so far */
221 #ifndef PAM_DATA_REPLACE
222 #error "Need to get an updated libpam 0.52 or better"
225 static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
228 const char *service = NULL;
229 const char *ruser = NULL;
230 const char *rhost = NULL;
231 const char *tty = NULL;
232 struct _pam_failed_auth *failure;
236 quiet = err & PAM_DATA_SILENT; /* should we log something? */
237 err &= PAM_DATA_REPLACE; /* are we just replacing data? */
238 failure = (struct _pam_failed_auth *) fl;
240 if (failure != NULL) {
242 if (!quiet && !err) { /* under advisement from Sun,may go away */
244 /* log the number of authentication failures */
245 if (failure->count > 1) {
246 (void) pam_get_item(pamh, PAM_SERVICE,
247 (const void **)&service);
248 (void) pam_get_item(pamh, PAM_RUSER,
249 (const void **)&ruser);
250 (void) pam_get_item(pamh, PAM_RHOST,
251 (const void **)&rhost);
252 (void) pam_get_item(pamh, PAM_TTY,
253 (const void **)&tty);
254 _log_err(LOG_NOTICE, pamh,
255 "%d more authentication failure%s; "
256 "logname=%s uid=%d euid=%d "
257 "tty=%s ruser=%s rhost=%s "
259 failure->count - 1, failure->count == 2 ? "" : "s",
260 failure->name, failure->uid, failure->euid,
261 tty ? tty : "", ruser ? ruser : "",
263 (failure->user && failure->user[0] != '\0')
264 ? " user=" : "", failure->user
267 if (failure->count > UNIX_MAX_RETRIES) {
268 _log_err(LOG_ALERT, pamh
269 ,"service(%s) ignoring max retries; %d > %d"
270 ,service == NULL ? "**unknown**" : service
276 _pam_delete(failure->user); /* tidy up */
277 _pam_delete(failure->name); /* tidy up */
283 * _unix_getpwnam() searches only /etc/passwd and NIS to find user information
285 static void _unix_cleanup(pam_handle_t *pamh, void *data, int error_status)
290 int _unix_getpwnam(pam_handle_t *pamh, const char *name,
291 int files, int nis, struct passwd **ret)
295 int matched = 0, buflen;
296 char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p;
298 memset(buf, 0, sizeof(buf));
300 if (!matched && files) {
301 int userlen = strlen(name);
302 passwd = fopen("/etc/passwd", "r");
303 if (passwd != NULL) {
304 while (fgets(buf, sizeof(buf), passwd) != NULL) {
305 if ((buf[userlen] == ':') &&
306 (strncmp(name, buf, userlen) == 0)) {
307 p = buf + strlen(buf) - 1;
308 while (isspace(*p) && (p >= buf)) {
319 if (!matched && nis) {
320 char *userinfo = NULL, *domain = NULL;
322 len = yp_get_default_domain(&domain);
323 if (len == YPERR_SUCCESS) {
324 len = yp_bind(domain);
326 if (len == YPERR_SUCCESS) {
327 i = yp_match(domain, "passwd.byname", name,
328 strlen(name), &userinfo, &len);
330 if ((i == YPERR_SUCCESS) && (len < sizeof(buf))) {
331 strncpy(buf, userinfo, sizeof(buf) - 1);
332 buf[sizeof(buf) - 1] = '\0';
338 if (matched && (ret != NULL)) {
343 spasswd = strchr(slogin, ':');
344 if (spasswd == NULL) {
349 suid = strchr(spasswd, ':');
355 sgid = strchr(suid, ':');
361 sgecos = strchr(sgid, ':');
362 if (sgecos == NULL) {
367 shome = strchr(sgecos, ':');
373 sshell = strchr(shome, ':');
374 if (sshell == NULL) {
379 buflen = sizeof(struct passwd) +
381 strlen(spasswd) + 1 +
387 *ret = malloc(buflen);
391 memset(*ret, '\0', buflen);
393 (*ret)->pw_uid = strtol(suid, &p, 10);
394 if ((strlen(sgid) == 0) || (*p != '\0')) {
400 (*ret)->pw_gid = strtol(sgid, &p, 10);
401 if ((strlen(sgid) == 0) || (*p != '\0')) {
407 p = ((char*)(*ret)) + sizeof(struct passwd);
408 (*ret)->pw_name = strcpy(p, slogin);
410 (*ret)->pw_passwd = strcpy(p, spasswd);
412 (*ret)->pw_gecos = strcpy(p, sgecos);
414 (*ret)->pw_dir = strcpy(p, shome);
416 (*ret)->pw_shell = strcpy(p, sshell);
418 snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name);
420 if (pam_set_data(pamh, buf,
421 *ret, _unix_cleanup) != PAM_SUCCESS) {
431 * _unix_comsefromsource() is a quick check to see if information about a given
432 * user comes from a particular source (just files and nis for now)
435 int _unix_comesfromsource(pam_handle_t *pamh,
436 const char *name, int files, int nis)
438 return _unix_getpwnam(pamh, name, files, nis, NULL);
442 * _unix_blankpasswd() is a quick check for a blank password
444 * returns TRUE if user does not have a password
445 * - to avoid prompting for one in such cases (CG)
449 _unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
451 struct passwd *pwd = NULL;
452 struct spwd *spwdent = NULL;
459 * This function does not have to be too smart if something goes
460 * wrong, return FALSE and let this case to be treated somewhere
464 if (on(UNIX__NONULL, ctrl))
465 return 0; /* will fail but don't let on yet */
467 /* UNIX passwords area */
469 /* Get password file entry... */
470 pwd = _pammodutil_getpwnam (pamh, name);
473 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
475 uid_t save_euid, save_uid;
477 save_euid = geteuid();
479 if (save_uid == pwd->pw_uid)
480 setreuid( save_euid, save_uid );
483 if (setreuid( -1, pwd->pw_uid ) == -1) {
486 if(setreuid( -1, pwd->pw_uid ) == -1)
487 /* Will fail elsewhere. */
492 spwdent = _pammodutil_getspnam (pamh, name);
493 if (save_uid == pwd->pw_uid)
494 setreuid( save_uid, save_euid );
496 if (setreuid( -1, 0 ) == -1)
497 setreuid( save_uid, -1 );
498 setreuid( -1, save_euid );
500 } else if (_unix_shadowed(pwd)) {
502 * ...and shadow password file entry for this user,
503 * if shadowing is enabled
505 spwdent = _pammodutil_getspnam(pamh, name);
508 salt = x_strdup(spwdent->sp_pwdp);
510 salt = x_strdup(pwd->pw_passwd);
512 /* Does this user have a password? */
516 if (strlen(salt) == 0)
531 * verify the password of a user
534 #include <sys/types.h>
535 #include <sys/wait.h>
537 static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
538 unsigned int ctrl, const char *user)
540 int retval, child, fds[2];
541 void (*sighandler)(int) = NULL;
544 /* create a pipe for the password */
545 if (pipe(fds) != 0) {
546 D(("could not make pipe"));
550 if (off(UNIX_NOREAP, ctrl)) {
552 * This code arranges that the demise of the child does not cause
553 * the application to receive a signal it is not expecting - which
554 * may kill the application or worse.
556 * The "noreap" module argument is provided so that the admin can
557 * override this behavior.
559 sighandler = signal(SIGCHLD, SIG_DFL);
565 static char *envp[] = { NULL };
566 char *args[] = { NULL, NULL, NULL };
568 /* XXX - should really tidy up PAM here too */
570 /* reopen stdin as pipe */
572 dup2(fds[0], STDIN_FILENO);
574 /* exec binary helper */
575 args[0] = x_strdup(CHKPWD_HELPER);
576 args[1] = x_strdup(user);
578 execve(CHKPWD_HELPER, args, envp);
580 /* should not get here: exit with error */
581 D(("helper binary is not available"));
582 exit(PAM_AUTHINFO_UNAVAIL);
583 } else if (child > 0) {
585 /* if the stored password is NULL */
586 if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
587 write(fds[1], "nullok\0\0", 8);
589 write(fds[1], "nonull\0\0", 8);
591 if (passwd != NULL) { /* send the password to the child */
592 write(fds[1], passwd, strlen(passwd)+1);
595 write(fds[1], "", 1); /* blank password */
597 close(fds[0]); /* close here to avoid possible SIGPIPE above */
599 (void) waitpid(child, &retval, 0); /* wait for helper to complete */
600 retval = (retval == 0) ? PAM_SUCCESS:PAM_AUTH_ERR;
603 retval = PAM_AUTH_ERR;
606 if (sighandler != NULL) {
607 (void) signal(SIGCHLD, sighandler); /* restore old signal handler */
610 D(("returning %d", retval));
614 int _unix_verify_password(pam_handle_t * pamh, const char *name
615 ,const char *p, unsigned int ctrl)
617 struct passwd *pwd = NULL;
618 struct spwd *spwdent = NULL;
626 #ifdef HAVE_PAM_FAIL_DELAY
627 if (off(UNIX_NODELAY, ctrl)) {
628 D(("setting delay"));
629 (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
633 /* locate the entry for this user */
635 D(("locating user's record"));
637 /* UNIX passwords area */
638 pwd = _pammodutil_getpwnam (pamh, name); /* Get password file entry... */
641 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
643 uid_t save_euid, save_uid;
645 save_euid = geteuid();
647 if (save_uid == pwd->pw_uid)
648 setreuid( save_euid, save_uid );
651 if (setreuid( -1, pwd->pw_uid ) == -1) {
654 if(setreuid( -1, pwd->pw_uid ) == -1)
655 return PAM_CRED_INSUFFICIENT;
659 spwdent = _pammodutil_getspnam (pamh, name);
660 if (save_uid == pwd->pw_uid)
661 setreuid( save_uid, save_euid );
663 if (setreuid( -1, 0 ) == -1)
664 setreuid( save_uid, -1 );
665 setreuid( -1, save_euid );
667 } else if (_unix_shadowed(pwd)) {
669 * ...and shadow password file entry for this user,
670 * if shadowing is enabled
672 spwdent = _pammodutil_getspnam (pamh, name);
675 salt = x_strdup(spwdent->sp_pwdp);
677 salt = x_strdup(pwd->pw_passwd);
680 data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
681 if (data_name == NULL) {
682 _log_err(LOG_CRIT, pamh, "no memory for data-name");
684 strcpy(data_name, FAIL_PREFIX);
685 strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
688 retval = PAM_SUCCESS;
689 if (pwd == NULL || salt == NULL || !strcmp(salt, "x") || ((salt[0] == '#') && (salt[1] == '#') && !strcmp(salt + 2, name))) {
691 /* we are not root perhaps this is the reason? Run helper */
692 D(("running helper binary"));
693 retval = _unix_run_helper_binary(pamh, p, ctrl, name);
694 if (pwd == NULL && !on(UNIX_AUDIT,ctrl)
695 && retval != PAM_SUCCESS)
700 D(("user's record unavailable"));
702 retval = PAM_AUTHINFO_UNAVAIL;
703 if (on(UNIX_AUDIT, ctrl)) {
704 /* this might be a typo and the user has given a password
705 instead of a username. Careful with this. */
706 _log_err(LOG_ALERT, pamh,
707 "check pass; user (%s) unknown", name);
710 if (on(UNIX_DEBUG, ctrl) || pwd == NULL) {
711 _log_err(LOG_ALERT, pamh,
712 "check pass; user unknown");
714 /* don't log failure as another pam module can succeed */
720 int salt_len = strlen(salt);
722 /* the stored password is NULL */
723 if (off(UNIX__NONULL, ctrl)) {/* this means we've succeeded */
724 D(("user has empty password - access granted"));
725 retval = PAM_SUCCESS;
727 D(("user has empty password - access denied"));
728 retval = PAM_AUTH_ERR;
730 } else if (!p || (*salt == '*') || (salt_len < 13)) {
731 retval = PAM_AUTH_ERR;
733 if (!strncmp(salt, "$1$", 3)) {
734 pp = Goodcrypt_md5(p, salt);
735 if (strcmp(pp, salt) != 0) {
737 pp = Brokencrypt_md5(p, salt);
740 pp = bigcrypt(p, salt);
742 p = NULL; /* no longer needed here */
744 /* the moment of truth -- do we agree with the password? */
745 D(("comparing state of pp[%s] and salt[%s]", pp, salt));
748 * Note, we are comparing the bigcrypt of the password with
749 * the contents of the password field. If the latter was
750 * encrypted with regular crypt (and not bigcrypt) it will
751 * have been truncated for storage relative to the output
752 * of bigcrypt here. As such we need to compare only the
753 * stored string with the subset of bigcrypt's result.
754 * Bug 521314: The strncmp comparison is for legacy support.
756 if (strncmp(pp, salt, salt_len) == 0) {
757 retval = PAM_SUCCESS;
759 retval = PAM_AUTH_ERR;
764 if (retval == PAM_SUCCESS) {
765 if (data_name) /* reset failures */
766 pam_set_data(pamh, data_name, NULL, _cleanup_failures);
768 if (data_name != NULL) {
769 struct _pam_failed_auth *new = NULL;
770 const struct _pam_failed_auth *old = NULL;
772 /* get a failure recorder */
774 new = (struct _pam_failed_auth *)
775 malloc(sizeof(struct _pam_failed_auth));
779 const char *login_name;
781 login_name = _pammodutil_getlogin(pamh);
782 if (login_name == NULL) {
786 new->user = x_strdup(name ? name : "");
788 new->euid = geteuid();
789 new->name = x_strdup(login_name);
791 /* any previous failures for this user ? */
792 pam_get_data(pamh, data_name, (const void **) &old);
795 new->count = old->count + 1;
796 if (new->count >= UNIX_MAX_RETRIES) {
797 retval = PAM_MAXTRIES;
800 const char *service=NULL;
801 const char *ruser=NULL;
802 const char *rhost=NULL;
803 const char *tty=NULL;
805 (void) pam_get_item(pamh, PAM_SERVICE,
806 (const void **)&service);
807 (void) pam_get_item(pamh, PAM_RUSER,
808 (const void **)&ruser);
809 (void) pam_get_item(pamh, PAM_RHOST,
810 (const void **)&rhost);
811 (void) pam_get_item(pamh, PAM_TTY,
812 (const void **)&tty);
814 _log_err(LOG_NOTICE, pamh,
815 "authentication failure; "
816 "logname=%s uid=%d euid=%d "
817 "tty=%s ruser=%s rhost=%s "
819 new->name, new->uid, new->euid,
823 (new->user && new->user[0] != '\0')
830 pam_set_data(pamh, data_name, new, _cleanup_failures);
833 _log_err(LOG_CRIT, pamh,
834 "no memory for failure recorder");
841 _pam_delete(data_name);
847 D(("done [%d].", retval));
853 * obtain a password from the user
856 int _unix_read_password(pam_handle_t * pamh
861 ,const char *data_name
871 * make sure nothing inappropriate gets returned
874 *pass = token = NULL;
877 * which authentication token are we getting?
880 authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
883 * should we obtain the password from a PAM item ?
886 if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
887 retval = pam_get_item(pamh, authtok_flag, (const void **) pass);
888 if (retval != PAM_SUCCESS) {
890 _log_err(LOG_ALERT, pamh
891 ,"pam_get_item returned error to unix-read-password"
894 } else if (*pass != NULL) { /* we have a password! */
896 } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
897 return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
898 } else if (on(UNIX_USE_AUTHTOK, ctrl)
899 && off(UNIX__OLD_PASSWD, ctrl)) {
900 return PAM_AUTHTOK_ERR;
904 * getting here implies we will have to get the password from the
909 struct pam_message msg[3], *pmsg[3];
910 struct pam_response *resp;
913 /* prepare to converse */
915 if (comment != NULL && off(UNIX__QUIET, ctrl)) {
917 msg[0].msg_style = PAM_TEXT_INFO;
918 msg[0].msg = comment;
925 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
926 msg[i++].msg = prompt1;
929 if (prompt2 != NULL) {
931 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
932 msg[i++].msg = prompt2;
935 /* so call the conversation expecting i responses */
937 retval = converse(pamh, ctrl, i, pmsg, &resp);
941 /* interpret the response */
943 if (retval == PAM_SUCCESS) { /* a good conversation */
945 token = x_strdup(resp[i - replies].resp);
949 /* verify that password entered correctly */
950 if (!resp[i - 1].resp
951 || strcmp(token, resp[i - 1].resp)) {
952 _pam_delete(token); /* mistyped */
953 retval = PAM_AUTHTOK_RECOVER_ERR;
954 _make_remark(pamh, ctrl
955 ,PAM_ERROR_MSG, MISTYPED_PASS);
959 _log_err(LOG_NOTICE, pamh
960 ,"could not recover authentication token");
965 * tidy up the conversation (resp_retcode) is ignored
966 * -- what is it for anyway? AGM
969 _pam_drop_reply(resp, i);
972 retval = (retval == PAM_SUCCESS)
973 ? PAM_AUTHTOK_RECOVER_ERR : retval;
977 if (retval != PAM_SUCCESS) {
978 if (on(UNIX_DEBUG, ctrl))
979 _log_err(LOG_DEBUG, pamh,
980 "unable to obtain a password");
983 /* 'token' is the entered password */
985 if (off(UNIX_NOT_SET_PASS, ctrl)) {
987 /* we store this password as an item */
989 retval = pam_set_item(pamh, authtok_flag, token);
990 _pam_delete(token); /* clean it up */
991 if (retval != PAM_SUCCESS
992 || (retval = pam_get_item(pamh, authtok_flag
993 ,(const void **) pass))
997 _log_err(LOG_CRIT, pamh, "error manipulating password");
1003 * then store it as data specific to this module. pam_end()
1004 * will arrange to clean it up.
1007 retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
1008 if (retval != PAM_SUCCESS) {
1009 _log_err(LOG_CRIT, pamh
1010 ,"error manipulating password data [%s]"
1011 ,pam_strerror(pamh, retval));
1016 token = NULL; /* break link to password */
1022 int _unix_shadowed(const struct passwd *pwd)
1025 if (strcmp(pwd->pw_passwd, "x") == 0) {
1028 if ((pwd->pw_passwd[0] == '#') &&
1029 (pwd->pw_passwd[1] == '#') &&
1030 (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) {
1037 /* ****************************************************************** *
1038 * Copyright (c) Jan Rêkorajski 1999.
1039 * Copyright (c) Andrew G. Morgan 1996-8.
1040 * Copyright (c) Alex O. Yuriev, 1996.
1041 * Copyright (c) Cristian Gafton 1996.
1043 * Redistribution and use in source and binary forms, with or without
1044 * modification, are permitted provided that the following conditions
1046 * 1. Redistributions of source code must retain the above copyright
1047 * notice, and the entire permission notice in its entirety,
1048 * including the disclaimer of warranties.
1049 * 2. Redistributions in binary form must reproduce the above copyright
1050 * notice, this list of conditions and the following disclaimer in the
1051 * documentation and/or other materials provided with the distribution.
1052 * 3. The name of the author may not be used to endorse or promote
1053 * products derived from this software without specific prior
1054 * written permission.
1056 * ALTERNATIVELY, this product may be distributed under the terms of
1057 * the GNU Public License, in which case the provisions of the GPL are
1058 * required INSTEAD OF the above restrictions. (This clause is
1059 * necessary due to a potential bad interaction between the GPL and
1060 * the restrictions contained in a BSD-style copyright.)
1062 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
1063 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1064 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1065 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1066 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1067 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1068 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1069 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
1070 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1071 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
1072 * OF THE POSSIBILITY OF SUCH DAMAGE.