4 * Copyright information at end of file.
23 #include <sys/resource.h>
24 #include <rpcsvc/ypclnt.h>
26 #include <security/_pam_macros.h>
27 #include <security/pam_modules.h>
28 #include <security/pam_modutil.h>
33 #include <selinux/selinux.h>
34 #define SELINUX_ENABLED is_selinux_enabled()>0
36 #define SELINUX_ENABLED 0
38 extern char *crypt(const char *key, const char *salt);
39 extern char *bigcrypt(const char *key, const char *salt);
41 /* syslogging function for errors and other information */
43 void _log_err(int err, pam_handle_t *pamh, const char *format,...)
45 const void *service = NULL;
49 pam_get_item(pamh, PAM_SERVICE, &service);
51 strncpy(logname, service, sizeof(logname));
52 logname[sizeof(logname) - 1 - strlen("(pam_unix)")] = '\0';
53 strncat(logname, "(pam_unix)", strlen("(pam_unix)"));
55 strncpy(logname, "pam_unix", sizeof(logname) - 1);
58 va_start(args, format);
59 openlog(logname, LOG_CONS | LOG_PID, LOG_AUTH);
60 vsyslog(err, format, args);
65 /* this is a front-end for module-application conversations */
67 static int converse(pam_handle_t * pamh, int ctrl, int nargs
68 ,struct pam_message **message
69 ,struct pam_response **response)
72 const void *void_conv;
73 const struct pam_conv *conv;
75 D(("begin to converse"));
77 retval = pam_get_item(pamh, PAM_CONV, &void_conv);
79 if (retval == PAM_SUCCESS) {
81 retval = conv->conv(nargs, (const struct pam_message **) message
82 ,response, conv->appdata_ptr);
84 D(("returned from application's conversation function"));
86 if (retval != PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) {
87 _log_err(LOG_DEBUG, pamh, "conversation failure [%s]"
88 ,pam_strerror(pamh, retval));
90 } else if (retval != PAM_CONV_AGAIN) {
91 _log_err(LOG_ERR, pamh
92 ,"couldn't obtain coversation function [%s]"
93 ,pam_strerror(pamh, retval));
95 D(("ready to return from module conversation"));
97 return retval; /* propagate error status */
100 int _make_remark(pam_handle_t * pamh, unsigned int ctrl
101 ,int type, const char *text)
103 int retval = PAM_SUCCESS;
105 if (off(UNIX__QUIET, ctrl)) {
106 struct pam_message *pmsg[1], msg[1];
107 struct pam_response *resp;
111 msg[0].msg_style = type;
114 retval = converse(pamh, ctrl, 1, pmsg, &resp);
117 _pam_drop_reply(resp, 1);
124 * set the control flags for the UNIX module.
127 int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc,
134 ctrl = UNIX_DEFAULTS; /* the default selection of options */
136 /* set some flags manually */
138 if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
140 set(UNIX__IAMROOT, ctrl);
142 if (flags & PAM_UPDATE_AUTHTOK) {
143 D(("UPDATE_AUTHTOK"));
144 set(UNIX__UPDATE, ctrl);
146 if (flags & PAM_PRELIM_CHECK) {
148 set(UNIX__PRELIM, ctrl);
150 if (flags & PAM_SILENT) {
152 set(UNIX__QUIET, ctrl);
154 /* now parse the arguments to this module */
159 D(("pam_unix arg: %s", *argv));
161 for (j = 0; j < UNIX_CTRLS_; ++j) {
162 if (unix_args[j].token
163 && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) {
168 if (j >= UNIX_CTRLS_) {
169 _log_err(LOG_ERR, pamh,
170 "unrecognized option [%s]", *argv);
172 ctrl &= unix_args[j].mask; /* for turning things off */
173 ctrl |= unix_args[j].flag; /* for turning things on */
175 if (remember != NULL) {
176 if (j == UNIX_REMEMBER_PASSWD) {
177 *remember = strtol(*argv + 9, NULL, 10);
178 if ((*remember == INT_MIN) || (*remember == INT_MAX))
186 ++argv; /* step to next argument */
189 if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
190 D(("DISALLOW_NULL_AUTHTOK"));
191 set(UNIX__NONULL, ctrl);
194 /* auditing is a more sensitive version of debug */
196 if (on(UNIX_AUDIT, ctrl)) {
197 set(UNIX_DEBUG, ctrl);
199 /* return the set of flags */
205 static void _cleanup(pam_handle_t * pamh, void *x, int error_status)
210 /* ************************************************************** *
211 * Useful non-trivial functions *
212 * ************************************************************** */
215 * the following is used to keep track of the number of times a user fails
216 * to authenticate themself.
219 #define FAIL_PREFIX "-UN*X-FAIL-"
220 #define UNIX_MAX_RETRIES 3
222 struct _pam_failed_auth {
223 char *user; /* user that's failed to be authenticated */
224 char *name; /* attempt from user with name */
225 int uid; /* uid of calling user */
226 int euid; /* euid of calling process */
227 int count; /* number of failures so far */
230 #ifndef PAM_DATA_REPLACE
231 #error "Need to get an updated libpam 0.52 or better"
234 static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
237 const void *service = NULL;
238 const void *ruser = NULL;
239 const void *rhost = NULL;
240 const void *tty = NULL;
241 struct _pam_failed_auth *failure;
245 quiet = err & PAM_DATA_SILENT; /* should we log something? */
246 err &= PAM_DATA_REPLACE; /* are we just replacing data? */
247 failure = (struct _pam_failed_auth *) fl;
249 if (failure != NULL) {
251 if (!quiet && !err) { /* under advisement from Sun,may go away */
253 /* log the number of authentication failures */
254 if (failure->count > 1) {
255 (void) pam_get_item(pamh, PAM_SERVICE,
257 (void) pam_get_item(pamh, PAM_RUSER,
259 (void) pam_get_item(pamh, PAM_RHOST,
261 (void) pam_get_item(pamh, PAM_TTY,
263 _log_err(LOG_NOTICE, pamh,
264 "%d more authentication failure%s; "
265 "logname=%s uid=%d euid=%d "
266 "tty=%s ruser=%s rhost=%s "
268 failure->count - 1, failure->count == 2 ? "" : "s",
269 failure->name, failure->uid, failure->euid,
270 tty ? tty : "", ruser ? ruser : "",
272 (failure->user && failure->user[0] != '\0')
273 ? " user=" : "", failure->user
276 if (failure->count > UNIX_MAX_RETRIES) {
277 _log_err(LOG_ALERT, pamh
278 ,"service(%s) ignoring max retries; %d > %d"
279 ,service == NULL ? "**unknown**" : service
285 _pam_delete(failure->user); /* tidy up */
286 _pam_delete(failure->name); /* tidy up */
292 * _unix_getpwnam() searches only /etc/passwd and NIS to find user information
294 static void _unix_cleanup(pam_handle_t *pamh, void *data, int error_status)
299 int _unix_getpwnam(pam_handle_t *pamh, const char *name,
300 int files, int nis, struct passwd **ret)
304 int matched = 0, buflen;
305 char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p;
307 memset(buf, 0, sizeof(buf));
309 if (!matched && files) {
310 int userlen = strlen(name);
311 passwd = fopen("/etc/passwd", "r");
312 if (passwd != NULL) {
313 while (fgets(buf, sizeof(buf), passwd) != NULL) {
314 if ((buf[userlen] == ':') &&
315 (strncmp(name, buf, userlen) == 0)) {
316 p = buf + strlen(buf) - 1;
317 while (isspace(*p) && (p >= buf)) {
328 if (!matched && nis) {
329 char *userinfo = NULL, *domain = NULL;
331 len = yp_get_default_domain(&domain);
332 if (len == YPERR_SUCCESS) {
333 len = yp_bind(domain);
335 if (len == YPERR_SUCCESS) {
336 i = yp_match(domain, "passwd.byname", name,
337 strlen(name), &userinfo, &len);
339 if ((i == YPERR_SUCCESS) && (len < sizeof(buf))) {
340 strncpy(buf, userinfo, sizeof(buf) - 1);
341 buf[sizeof(buf) - 1] = '\0';
347 if (matched && (ret != NULL)) {
352 spasswd = strchr(slogin, ':');
353 if (spasswd == NULL) {
358 suid = strchr(spasswd, ':');
364 sgid = strchr(suid, ':');
370 sgecos = strchr(sgid, ':');
371 if (sgecos == NULL) {
376 shome = strchr(sgecos, ':');
382 sshell = strchr(shome, ':');
383 if (sshell == NULL) {
388 buflen = sizeof(struct passwd) +
390 strlen(spasswd) + 1 +
396 *ret = malloc(buflen);
400 memset(*ret, '\0', buflen);
402 (*ret)->pw_uid = strtol(suid, &p, 10);
403 if ((strlen(sgid) == 0) || (*p != '\0')) {
409 (*ret)->pw_gid = strtol(sgid, &p, 10);
410 if ((strlen(sgid) == 0) || (*p != '\0')) {
416 p = ((char*)(*ret)) + sizeof(struct passwd);
417 (*ret)->pw_name = strcpy(p, slogin);
419 (*ret)->pw_passwd = strcpy(p, spasswd);
421 (*ret)->pw_gecos = strcpy(p, sgecos);
423 (*ret)->pw_dir = strcpy(p, shome);
425 (*ret)->pw_shell = strcpy(p, sshell);
427 snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name);
429 if (pam_set_data(pamh, buf,
430 *ret, _unix_cleanup) != PAM_SUCCESS) {
440 * _unix_comsefromsource() is a quick check to see if information about a given
441 * user comes from a particular source (just files and nis for now)
444 int _unix_comesfromsource(pam_handle_t *pamh,
445 const char *name, int files, int nis)
447 return _unix_getpwnam(pamh, name, files, nis, NULL);
451 * _unix_blankpasswd() is a quick check for a blank password
453 * returns TRUE if user does not have a password
454 * - to avoid prompting for one in such cases (CG)
458 _unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
460 struct passwd *pwd = NULL;
461 struct spwd *spwdent = NULL;
468 * This function does not have to be too smart if something goes
469 * wrong, return FALSE and let this case to be treated somewhere
473 if (on(UNIX__NONULL, ctrl))
474 return 0; /* will fail but don't let on yet */
476 /* UNIX passwords area */
478 /* Get password file entry... */
479 pwd = pam_modutil_getpwnam (pamh, name);
482 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
484 uid_t save_euid, save_uid;
486 save_euid = geteuid();
488 if (save_uid == pwd->pw_uid)
489 setreuid( save_euid, save_uid );
492 if (setreuid( -1, pwd->pw_uid ) == -1) {
495 if(setreuid( -1, pwd->pw_uid ) == -1)
496 /* Will fail elsewhere. */
501 spwdent = pam_modutil_getspnam (pamh, name);
502 if (save_uid == pwd->pw_uid)
503 setreuid( save_uid, save_euid );
505 if (setreuid( -1, 0 ) == -1)
506 setreuid( save_uid, -1 );
507 setreuid( -1, save_euid );
509 } else if (_unix_shadowed(pwd)) {
511 * ...and shadow password file entry for this user,
512 * if shadowing is enabled
514 spwdent = pam_modutil_getspnam(pamh, name);
517 salt = x_strdup(spwdent->sp_pwdp);
519 salt = x_strdup(pwd->pw_passwd);
521 /* Does this user have a password? */
525 if (strlen(salt) == 0)
540 * verify the password of a user
543 #include <sys/types.h>
544 #include <sys/wait.h>
546 static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
547 unsigned int ctrl, const char *user)
549 int retval, child, fds[2];
550 void (*sighandler)(int) = NULL;
553 /* create a pipe for the password */
554 if (pipe(fds) != 0) {
555 D(("could not make pipe"));
559 if (off(UNIX_NOREAP, ctrl)) {
561 * This code arranges that the demise of the child does not cause
562 * the application to receive a signal it is not expecting - which
563 * may kill the application or worse.
565 * The "noreap" module argument is provided so that the admin can
566 * override this behavior.
568 sighandler = signal(SIGCHLD, SIG_DFL);
576 static char *envp[] = { NULL };
577 char *args[] = { NULL, NULL, NULL, NULL };
579 /* XXX - should really tidy up PAM here too */
582 /* reopen stdin as pipe */
584 dup2(fds[0], STDIN_FILENO);
586 if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
587 for (i=2; i < rlim.rlim_max; i++) {
592 /* exec binary helper */
593 args[0] = x_strdup(CHKPWD_HELPER);
594 args[1] = x_strdup(user);
595 if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
596 args[2]=x_strdup("nullok");
598 args[2]=x_strdup("nonull");
601 execve(CHKPWD_HELPER, args, envp);
603 /* should not get here: exit with error */
604 D(("helper binary is not available"));
605 exit(PAM_AUTHINFO_UNAVAIL);
606 } else if (child > 0) {
608 /* if the stored password is NULL */
610 if (passwd != NULL) { /* send the password to the child */
611 write(fds[1], passwd, strlen(passwd)+1);
614 write(fds[1], "", 1); /* blank password */
616 close(fds[0]); /* close here to avoid possible SIGPIPE above */
618 rc=waitpid(child, &retval, 0); /* wait for helper to complete */
620 _log_err(LOG_ERR, pamh, "unix_chkpwd waitpid returned %d: %s", rc, strerror(errno));
621 retval = PAM_AUTH_ERR;
623 retval = WEXITSTATUS(retval);
629 retval = PAM_AUTH_ERR;
632 if (sighandler != NULL) {
633 (void) signal(SIGCHLD, sighandler); /* restore old signal handler */
636 D(("returning %d", retval));
640 int _unix_verify_password(pam_handle_t * pamh, const char *name
641 ,const char *p, unsigned int ctrl)
643 struct passwd *pwd = NULL;
644 struct spwd *spwdent = NULL;
653 #ifdef HAVE_PAM_FAIL_DELAY
654 if (off(UNIX_NODELAY, ctrl)) {
655 D(("setting delay"));
656 (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
660 /* locate the entry for this user */
662 D(("locating user's record"));
664 /* UNIX passwords area */
665 pwd = pam_modutil_getpwnam (pamh, name); /* Get password file entry... */
668 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
670 uid_t save_euid, save_uid;
672 save_euid = geteuid();
674 if (save_uid == pwd->pw_uid)
675 setreuid( save_euid, save_uid );
678 if (setreuid( -1, pwd->pw_uid ) == -1) {
681 if(setreuid( -1, pwd->pw_uid ) == -1)
682 return PAM_CRED_INSUFFICIENT;
686 spwdent = pam_modutil_getspnam (pamh, name);
687 if (save_uid == pwd->pw_uid)
688 setreuid( save_uid, save_euid );
690 if (setreuid( -1, 0 ) == -1)
691 setreuid( save_uid, -1 );
692 setreuid( -1, save_euid );
694 } else if (_unix_shadowed(pwd)) {
696 * ...and shadow password file entry for this user,
697 * if shadowing is enabled
699 spwdent = pam_modutil_getspnam (pamh, name);
702 salt = x_strdup(spwdent->sp_pwdp);
704 salt = x_strdup(pwd->pw_passwd);
707 data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
708 if (data_name == NULL) {
709 _log_err(LOG_CRIT, pamh, "no memory for data-name");
711 strcpy(data_name, FAIL_PREFIX);
712 strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
715 retval = PAM_SUCCESS;
716 if (pwd == NULL || salt == NULL || !strcmp(salt, "x") || ((salt[0] == '#') && (salt[1] == '#') && !strcmp(salt + 2, name))) {
718 if (pwd != NULL && (geteuid() || SELINUX_ENABLED)) {
719 /* we are not root perhaps this is the reason? Run helper */
720 D(("running helper binary"));
721 retval = _unix_run_helper_binary(pamh, p, ctrl, name);
723 D(("user's record unavailable"));
726 retval = PAM_USER_UNKNOWN;
728 retval = PAM_AUTHINFO_UNAVAIL;
729 if (on(UNIX_AUDIT, ctrl)) {
730 /* this might be a typo and the user has given a password
731 instead of a username. Careful with this. */
732 _log_err(LOG_ALERT, pamh,
733 "check pass; user (%s) unknown", name);
736 if (on(UNIX_DEBUG, ctrl) || pwd == NULL) {
737 _log_err(LOG_ALERT, pamh,
738 "check pass; user unknown");
740 /* don't log failure as another pam module can succeed */
746 int salt_len = strlen(salt);
748 /* the stored password is NULL */
749 if (off(UNIX__NONULL, ctrl)) {/* this means we've succeeded */
750 D(("user has empty password - access granted"));
751 retval = PAM_SUCCESS;
753 D(("user has empty password - access denied"));
754 retval = PAM_AUTH_ERR;
756 } else if (!p || (*salt == '*') || (salt_len < 13)) {
757 retval = PAM_AUTH_ERR;
759 if (!strncmp(salt, "$1$", 3)) {
760 pp = Goodcrypt_md5(p, salt);
761 if (strcmp(pp, salt) != 0) {
763 pp = Brokencrypt_md5(p, salt);
766 pp = bigcrypt(p, salt);
768 p = NULL; /* no longer needed here */
770 /* the moment of truth -- do we agree with the password? */
771 D(("comparing state of pp[%s] and salt[%s]", pp, salt));
774 * Note, we are comparing the bigcrypt of the password with
775 * the contents of the password field. If the latter was
776 * encrypted with regular crypt (and not bigcrypt) it will
777 * have been truncated for storage relative to the output
778 * of bigcrypt here. As such we need to compare only the
779 * stored string with the subset of bigcrypt's result.
780 * Bug 521314: The strncmp comparison is for legacy support.
782 if (strncmp(pp, salt, salt_len) == 0) {
783 retval = PAM_SUCCESS;
785 retval = PAM_AUTH_ERR;
790 if (retval == PAM_SUCCESS) {
791 if (data_name) /* reset failures */
792 pam_set_data(pamh, data_name, NULL, _cleanup_failures);
794 if (data_name != NULL) {
795 struct _pam_failed_auth *new = NULL;
796 const struct _pam_failed_auth *old = NULL;
798 /* get a failure recorder */
800 new = (struct _pam_failed_auth *)
801 malloc(sizeof(struct _pam_failed_auth));
805 const char *login_name;
806 const void *void_old;
809 login_name = pam_modutil_getlogin(pamh);
810 if (login_name == NULL) {
814 new->user = x_strdup(name ? name : "");
816 new->euid = geteuid();
817 new->name = x_strdup(login_name);
819 /* any previous failures for this user ? */
820 if (pam_get_data(pamh, data_name, &void_old)
827 new->count = old->count + 1;
828 if (new->count >= UNIX_MAX_RETRIES) {
829 retval = PAM_MAXTRIES;
832 const void *service=NULL;
833 const void *ruser=NULL;
834 const void *rhost=NULL;
835 const void *tty=NULL;
837 (void) pam_get_item(pamh, PAM_SERVICE,
839 (void) pam_get_item(pamh, PAM_RUSER,
841 (void) pam_get_item(pamh, PAM_RHOST,
843 (void) pam_get_item(pamh, PAM_TTY,
846 _log_err(LOG_NOTICE, pamh,
847 "authentication failure; "
848 "logname=%s uid=%d euid=%d "
849 "tty=%s ruser=%s rhost=%s "
851 new->name, new->uid, new->euid,
855 (new->user && new->user[0] != '\0')
862 pam_set_data(pamh, data_name, new, _cleanup_failures);
865 _log_err(LOG_CRIT, pamh,
866 "no memory for failure recorder");
873 _pam_delete(data_name);
879 D(("done [%d].", retval));
885 * obtain a password from the user
888 int _unix_read_password(pam_handle_t * pamh
893 ,const char *data_name
903 * make sure nothing inappropriate gets returned
906 *pass = token = NULL;
909 * which authentication token are we getting?
912 authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
915 * should we obtain the password from a PAM item ?
918 if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
919 retval = pam_get_item(pamh, authtok_flag, pass);
920 if (retval != PAM_SUCCESS) {
922 _log_err(LOG_ALERT, pamh
923 ,"pam_get_item returned error to unix-read-password"
926 } else if (*pass != NULL) { /* we have a password! */
928 } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
929 return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
930 } else if (on(UNIX_USE_AUTHTOK, ctrl)
931 && off(UNIX__OLD_PASSWD, ctrl)) {
932 return PAM_AUTHTOK_ERR;
936 * getting here implies we will have to get the password from the
941 struct pam_message msg[3], *pmsg[3];
942 struct pam_response *resp;
945 /* prepare to converse */
947 if (comment != NULL && off(UNIX__QUIET, ctrl)) {
949 msg[0].msg_style = PAM_TEXT_INFO;
950 msg[0].msg = comment;
957 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
958 msg[i++].msg = prompt1;
961 if (prompt2 != NULL) {
963 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
964 msg[i++].msg = prompt2;
967 /* so call the conversation expecting i responses */
969 retval = converse(pamh, ctrl, i, pmsg, &resp);
973 /* interpret the response */
975 if (retval == PAM_SUCCESS) { /* a good conversation */
977 token = x_strdup(resp[i - replies].resp);
981 /* verify that password entered correctly */
982 if (!resp[i - 1].resp
983 || strcmp(token, resp[i - 1].resp)) {
984 _pam_delete(token); /* mistyped */
985 retval = PAM_AUTHTOK_RECOVER_ERR;
986 _make_remark(pamh, ctrl
987 ,PAM_ERROR_MSG, MISTYPED_PASS);
991 _log_err(LOG_NOTICE, pamh
992 ,"could not recover authentication token");
997 * tidy up the conversation (resp_retcode) is ignored
998 * -- what is it for anyway? AGM
1001 _pam_drop_reply(resp, i);
1004 retval = (retval == PAM_SUCCESS)
1005 ? PAM_AUTHTOK_RECOVER_ERR : retval;
1009 if (retval != PAM_SUCCESS) {
1010 if (on(UNIX_DEBUG, ctrl))
1011 _log_err(LOG_DEBUG, pamh,
1012 "unable to obtain a password");
1015 /* 'token' is the entered password */
1017 if (off(UNIX_NOT_SET_PASS, ctrl)) {
1019 /* we store this password as an item */
1021 retval = pam_set_item(pamh, authtok_flag, token);
1022 _pam_delete(token); /* clean it up */
1023 if (retval != PAM_SUCCESS
1024 || (retval = pam_get_item(pamh, authtok_flag, pass))
1028 _log_err(LOG_CRIT, pamh, "error manipulating password");
1034 * then store it as data specific to this module. pam_end()
1035 * will arrange to clean it up.
1038 retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
1039 if (retval != PAM_SUCCESS) {
1040 _log_err(LOG_CRIT, pamh
1041 ,"error manipulating password data [%s]"
1042 ,pam_strerror(pamh, retval));
1047 token = NULL; /* break link to password */
1053 int _unix_shadowed(const struct passwd *pwd)
1056 if (strcmp(pwd->pw_passwd, "x") == 0) {
1059 if ((pwd->pw_passwd[0] == '#') &&
1060 (pwd->pw_passwd[1] == '#') &&
1061 (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) {
1068 /* ****************************************************************** *
1069 * Copyright (c) Jan Rêkorajski 1999.
1070 * Copyright (c) Andrew G. Morgan 1996-8.
1071 * Copyright (c) Alex O. Yuriev, 1996.
1072 * Copyright (c) Cristian Gafton 1996.
1074 * Redistribution and use in source and binary forms, with or without
1075 * modification, are permitted provided that the following conditions
1077 * 1. Redistributions of source code must retain the above copyright
1078 * notice, and the entire permission notice in its entirety,
1079 * including the disclaimer of warranties.
1080 * 2. Redistributions in binary form must reproduce the above copyright
1081 * notice, this list of conditions and the following disclaimer in the
1082 * documentation and/or other materials provided with the distribution.
1083 * 3. The name of the author may not be used to endorse or promote
1084 * products derived from this software without specific prior
1085 * written permission.
1087 * ALTERNATIVELY, this product may be distributed under the terms of
1088 * the GNU Public License, in which case the provisions of the GPL are
1089 * required INSTEAD OF the above restrictions. (This clause is
1090 * necessary due to a potential bad interaction between the GPL and
1091 * the restrictions contained in a BSD-style copyright.)
1093 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
1094 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1095 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1096 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1097 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1098 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1099 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1100 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
1101 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1102 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
1103 * OF THE POSSIBILITY OF SUCH DAMAGE.