4 * Copyright information at end of file.
22 #include <sys/resource.h>
23 #include <rpcsvc/ypclnt.h>
25 #include <security/_pam_macros.h>
26 #include <security/pam_modules.h>
27 #include <security/_pam_modutil.h>
32 #include <selinux/selinux.h>
33 #define SELINUX_ENABLED is_selinux_enabled()>0
35 #define SELINUX_ENABLED 0
37 extern char *crypt(const char *key, const char *salt);
38 extern char *bigcrypt(const char *key, const char *salt);
40 /* syslogging function for errors and other information */
42 void _log_err(int err, pam_handle_t *pamh, const char *format,...)
44 const void *service = NULL;
48 pam_get_item(pamh, PAM_SERVICE, &service);
50 strncpy(logname, service, sizeof(logname));
51 logname[sizeof(logname) - 1 - strlen("(pam_unix)")] = '\0';
52 strncat(logname, "(pam_unix)", strlen("(pam_unix)"));
54 strncpy(logname, "pam_unix", sizeof(logname) - 1);
57 va_start(args, format);
58 openlog(logname, LOG_CONS | LOG_PID, LOG_AUTH);
59 vsyslog(err, format, args);
64 /* this is a front-end for module-application conversations */
66 static int converse(pam_handle_t * pamh, int ctrl, int nargs
67 ,struct pam_message **message
68 ,struct pam_response **response)
71 const void *void_conv;
72 const struct pam_conv *conv;
74 D(("begin to converse"));
76 retval = pam_get_item(pamh, PAM_CONV, &void_conv);
78 if (retval == PAM_SUCCESS) {
80 retval = conv->conv(nargs, (const struct pam_message **) message
81 ,response, conv->appdata_ptr);
83 D(("returned from application's conversation function"));
85 if (retval != PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) {
86 _log_err(LOG_DEBUG, pamh, "conversation failure [%s]"
87 ,pam_strerror(pamh, retval));
89 } else if (retval != PAM_CONV_AGAIN) {
90 _log_err(LOG_ERR, pamh
91 ,"couldn't obtain coversation function [%s]"
92 ,pam_strerror(pamh, retval));
94 D(("ready to return from module conversation"));
96 return retval; /* propagate error status */
99 int _make_remark(pam_handle_t * pamh, unsigned int ctrl
100 ,int type, const char *text)
102 int retval = PAM_SUCCESS;
104 if (off(UNIX__QUIET, ctrl)) {
105 struct pam_message *pmsg[1], msg[1];
106 struct pam_response *resp;
110 msg[0].msg_style = type;
113 retval = converse(pamh, ctrl, 1, pmsg, &resp);
116 _pam_drop_reply(resp, 1);
123 * set the control flags for the UNIX module.
126 int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc,
133 ctrl = UNIX_DEFAULTS; /* the default selection of options */
135 /* set some flags manually */
137 if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
139 set(UNIX__IAMROOT, ctrl);
141 if (flags & PAM_UPDATE_AUTHTOK) {
142 D(("UPDATE_AUTHTOK"));
143 set(UNIX__UPDATE, ctrl);
145 if (flags & PAM_PRELIM_CHECK) {
147 set(UNIX__PRELIM, ctrl);
149 if (flags & PAM_SILENT) {
151 set(UNIX__QUIET, ctrl);
153 /* now parse the arguments to this module */
158 D(("pam_unix arg: %s", *argv));
160 for (j = 0; j < UNIX_CTRLS_; ++j) {
161 if (unix_args[j].token
162 && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) {
167 if (j >= UNIX_CTRLS_) {
168 _log_err(LOG_ERR, pamh,
169 "unrecognized option [%s]", *argv);
171 ctrl &= unix_args[j].mask; /* for turning things off */
172 ctrl |= unix_args[j].flag; /* for turning things on */
174 if (remember != NULL) {
175 if (j == UNIX_REMEMBER_PASSWD) {
176 *remember = strtol(*argv + 9, NULL, 10);
177 if ((*remember == INT_MIN) || (*remember == INT_MAX))
185 ++argv; /* step to next argument */
188 if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
189 D(("DISALLOW_NULL_AUTHTOK"));
190 set(UNIX__NONULL, ctrl);
193 /* auditing is a more sensitive version of debug */
195 if (on(UNIX_AUDIT, ctrl)) {
196 set(UNIX_DEBUG, ctrl);
198 /* return the set of flags */
204 static void _cleanup(pam_handle_t * pamh, void *x, int error_status)
209 /* ************************************************************** *
210 * Useful non-trivial functions *
211 * ************************************************************** */
214 * the following is used to keep track of the number of times a user fails
215 * to authenticate themself.
218 #define FAIL_PREFIX "-UN*X-FAIL-"
219 #define UNIX_MAX_RETRIES 3
221 struct _pam_failed_auth {
222 char *user; /* user that's failed to be authenticated */
223 char *name; /* attempt from user with name */
224 int uid; /* uid of calling user */
225 int euid; /* euid of calling process */
226 int count; /* number of failures so far */
229 #ifndef PAM_DATA_REPLACE
230 #error "Need to get an updated libpam 0.52 or better"
233 static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
236 const void *service = NULL;
237 const void *ruser = NULL;
238 const void *rhost = NULL;
239 const void *tty = NULL;
240 struct _pam_failed_auth *failure;
244 quiet = err & PAM_DATA_SILENT; /* should we log something? */
245 err &= PAM_DATA_REPLACE; /* are we just replacing data? */
246 failure = (struct _pam_failed_auth *) fl;
248 if (failure != NULL) {
250 if (!quiet && !err) { /* under advisement from Sun,may go away */
252 /* log the number of authentication failures */
253 if (failure->count > 1) {
254 (void) pam_get_item(pamh, PAM_SERVICE,
256 (void) pam_get_item(pamh, PAM_RUSER,
258 (void) pam_get_item(pamh, PAM_RHOST,
260 (void) pam_get_item(pamh, PAM_TTY,
262 _log_err(LOG_NOTICE, pamh,
263 "%d more authentication failure%s; "
264 "logname=%s uid=%d euid=%d "
265 "tty=%s ruser=%s rhost=%s "
267 failure->count - 1, failure->count == 2 ? "" : "s",
268 failure->name, failure->uid, failure->euid,
269 tty ? tty : "", ruser ? ruser : "",
271 (failure->user && failure->user[0] != '\0')
272 ? " user=" : "", failure->user
275 if (failure->count > UNIX_MAX_RETRIES) {
276 _log_err(LOG_ALERT, pamh
277 ,"service(%s) ignoring max retries; %d > %d"
278 ,service == NULL ? "**unknown**" : service
284 _pam_delete(failure->user); /* tidy up */
285 _pam_delete(failure->name); /* tidy up */
291 * _unix_getpwnam() searches only /etc/passwd and NIS to find user information
293 static void _unix_cleanup(pam_handle_t *pamh, void *data, int error_status)
298 int _unix_getpwnam(pam_handle_t *pamh, const char *name,
299 int files, int nis, struct passwd **ret)
303 int matched = 0, buflen;
304 char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p;
306 memset(buf, 0, sizeof(buf));
308 if (!matched && files) {
309 int userlen = strlen(name);
310 passwd = fopen("/etc/passwd", "r");
311 if (passwd != NULL) {
312 while (fgets(buf, sizeof(buf), passwd) != NULL) {
313 if ((buf[userlen] == ':') &&
314 (strncmp(name, buf, userlen) == 0)) {
315 p = buf + strlen(buf) - 1;
316 while (isspace(*p) && (p >= buf)) {
327 if (!matched && nis) {
328 char *userinfo = NULL, *domain = NULL;
330 len = yp_get_default_domain(&domain);
331 if (len == YPERR_SUCCESS) {
332 len = yp_bind(domain);
334 if (len == YPERR_SUCCESS) {
335 i = yp_match(domain, "passwd.byname", name,
336 strlen(name), &userinfo, &len);
338 if ((i == YPERR_SUCCESS) && (len < sizeof(buf))) {
339 strncpy(buf, userinfo, sizeof(buf) - 1);
340 buf[sizeof(buf) - 1] = '\0';
346 if (matched && (ret != NULL)) {
351 spasswd = strchr(slogin, ':');
352 if (spasswd == NULL) {
357 suid = strchr(spasswd, ':');
363 sgid = strchr(suid, ':');
369 sgecos = strchr(sgid, ':');
370 if (sgecos == NULL) {
375 shome = strchr(sgecos, ':');
381 sshell = strchr(shome, ':');
382 if (sshell == NULL) {
387 buflen = sizeof(struct passwd) +
389 strlen(spasswd) + 1 +
395 *ret = malloc(buflen);
399 memset(*ret, '\0', buflen);
401 (*ret)->pw_uid = strtol(suid, &p, 10);
402 if ((strlen(sgid) == 0) || (*p != '\0')) {
408 (*ret)->pw_gid = strtol(sgid, &p, 10);
409 if ((strlen(sgid) == 0) || (*p != '\0')) {
415 p = ((char*)(*ret)) + sizeof(struct passwd);
416 (*ret)->pw_name = strcpy(p, slogin);
418 (*ret)->pw_passwd = strcpy(p, spasswd);
420 (*ret)->pw_gecos = strcpy(p, sgecos);
422 (*ret)->pw_dir = strcpy(p, shome);
424 (*ret)->pw_shell = strcpy(p, sshell);
426 snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name);
428 if (pam_set_data(pamh, buf,
429 *ret, _unix_cleanup) != PAM_SUCCESS) {
439 * _unix_comsefromsource() is a quick check to see if information about a given
440 * user comes from a particular source (just files and nis for now)
443 int _unix_comesfromsource(pam_handle_t *pamh,
444 const char *name, int files, int nis)
446 return _unix_getpwnam(pamh, name, files, nis, NULL);
450 * _unix_blankpasswd() is a quick check for a blank password
452 * returns TRUE if user does not have a password
453 * - to avoid prompting for one in such cases (CG)
457 _unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
459 struct passwd *pwd = NULL;
460 struct spwd *spwdent = NULL;
467 * This function does not have to be too smart if something goes
468 * wrong, return FALSE and let this case to be treated somewhere
472 if (on(UNIX__NONULL, ctrl))
473 return 0; /* will fail but don't let on yet */
475 /* UNIX passwords area */
477 /* Get password file entry... */
478 pwd = _pammodutil_getpwnam (pamh, name);
481 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
483 uid_t save_euid, save_uid;
485 save_euid = geteuid();
487 if (save_uid == pwd->pw_uid)
488 setreuid( save_euid, save_uid );
491 if (setreuid( -1, pwd->pw_uid ) == -1) {
494 if(setreuid( -1, pwd->pw_uid ) == -1)
495 /* Will fail elsewhere. */
500 spwdent = _pammodutil_getspnam (pamh, name);
501 if (save_uid == pwd->pw_uid)
502 setreuid( save_uid, save_euid );
504 if (setreuid( -1, 0 ) == -1)
505 setreuid( save_uid, -1 );
506 setreuid( -1, save_euid );
508 } else if (_unix_shadowed(pwd)) {
510 * ...and shadow password file entry for this user,
511 * if shadowing is enabled
513 spwdent = _pammodutil_getspnam(pamh, name);
516 salt = x_strdup(spwdent->sp_pwdp);
518 salt = x_strdup(pwd->pw_passwd);
520 /* Does this user have a password? */
524 if (strlen(salt) == 0)
539 * verify the password of a user
542 #include <sys/types.h>
543 #include <sys/wait.h>
545 static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
546 unsigned int ctrl, const char *user)
548 int retval, child, fds[2];
549 void (*sighandler)(int) = NULL;
552 /* create a pipe for the password */
553 if (pipe(fds) != 0) {
554 D(("could not make pipe"));
558 if (off(UNIX_NOREAP, ctrl)) {
560 * This code arranges that the demise of the child does not cause
561 * the application to receive a signal it is not expecting - which
562 * may kill the application or worse.
564 * The "noreap" module argument is provided so that the admin can
565 * override this behavior.
567 sighandler = signal(SIGCHLD, SIG_DFL);
575 static char *envp[] = { NULL };
576 char *args[] = { NULL, NULL, NULL, NULL };
578 /* XXX - should really tidy up PAM here too */
581 /* reopen stdin as pipe */
583 dup2(fds[0], STDIN_FILENO);
585 if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
586 for (i=2; i < rlim.rlim_max; i++) {
591 /* exec binary helper */
592 args[0] = x_strdup(CHKPWD_HELPER);
593 args[1] = x_strdup(user);
594 if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
595 args[2]=x_strdup("nullok");
597 args[2]=x_strdup("nonull");
600 execve(CHKPWD_HELPER, args, envp);
602 /* should not get here: exit with error */
603 D(("helper binary is not available"));
604 exit(PAM_AUTHINFO_UNAVAIL);
605 } else if (child > 0) {
607 /* if the stored password is NULL */
609 if (passwd != NULL) { /* send the password to the child */
610 write(fds[1], passwd, strlen(passwd)+1);
613 write(fds[1], "", 1); /* blank password */
615 close(fds[0]); /* close here to avoid possible SIGPIPE above */
617 rc=waitpid(child, &retval, 0); /* wait for helper to complete */
619 _log_err(LOG_ERR, pamh, "unix_chkpwd waitpid returned %d: %s", rc, strerror(errno));
620 retval = PAM_AUTH_ERR;
622 retval = WEXITSTATUS(retval);
628 retval = PAM_AUTH_ERR;
631 if (sighandler != NULL) {
632 (void) signal(SIGCHLD, sighandler); /* restore old signal handler */
635 D(("returning %d", retval));
639 int _unix_verify_password(pam_handle_t * pamh, const char *name
640 ,const char *p, unsigned int ctrl)
642 struct passwd *pwd = NULL;
643 struct spwd *spwdent = NULL;
652 #ifdef HAVE_PAM_FAIL_DELAY
653 if (off(UNIX_NODELAY, ctrl)) {
654 D(("setting delay"));
655 (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
659 /* locate the entry for this user */
661 D(("locating user's record"));
663 /* UNIX passwords area */
664 pwd = _pammodutil_getpwnam (pamh, name); /* Get password file entry... */
667 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
669 uid_t save_euid, save_uid;
671 save_euid = geteuid();
673 if (save_uid == pwd->pw_uid)
674 setreuid( save_euid, save_uid );
677 if (setreuid( -1, pwd->pw_uid ) == -1) {
680 if(setreuid( -1, pwd->pw_uid ) == -1)
681 return PAM_CRED_INSUFFICIENT;
685 spwdent = _pammodutil_getspnam (pamh, name);
686 if (save_uid == pwd->pw_uid)
687 setreuid( save_uid, save_euid );
689 if (setreuid( -1, 0 ) == -1)
690 setreuid( save_uid, -1 );
691 setreuid( -1, save_euid );
693 } else if (_unix_shadowed(pwd)) {
695 * ...and shadow password file entry for this user,
696 * if shadowing is enabled
698 spwdent = _pammodutil_getspnam (pamh, name);
701 salt = x_strdup(spwdent->sp_pwdp);
703 salt = x_strdup(pwd->pw_passwd);
706 data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
707 if (data_name == NULL) {
708 _log_err(LOG_CRIT, pamh, "no memory for data-name");
710 strcpy(data_name, FAIL_PREFIX);
711 strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
714 retval = PAM_SUCCESS;
715 if (pwd == NULL || salt == NULL || !strcmp(salt, "x") || ((salt[0] == '#') && (salt[1] == '#') && !strcmp(salt + 2, name))) {
717 if (pwd != NULL && (geteuid() || SELINUX_ENABLED)) {
718 /* we are not root perhaps this is the reason? Run helper */
719 D(("running helper binary"));
720 retval = _unix_run_helper_binary(pamh, p, ctrl, name);
722 D(("user's record unavailable"));
725 retval = PAM_USER_UNKNOWN;
727 retval = PAM_AUTHINFO_UNAVAIL;
728 if (on(UNIX_AUDIT, ctrl)) {
729 /* this might be a typo and the user has given a password
730 instead of a username. Careful with this. */
731 _log_err(LOG_ALERT, pamh,
732 "check pass; user (%s) unknown", name);
735 if (on(UNIX_DEBUG, ctrl) || pwd == NULL) {
736 _log_err(LOG_ALERT, pamh,
737 "check pass; user unknown");
739 /* don't log failure as another pam module can succeed */
745 int salt_len = strlen(salt);
747 /* the stored password is NULL */
748 if (off(UNIX__NONULL, ctrl)) {/* this means we've succeeded */
749 D(("user has empty password - access granted"));
750 retval = PAM_SUCCESS;
752 D(("user has empty password - access denied"));
753 retval = PAM_AUTH_ERR;
755 } else if (!p || (*salt == '*') || (salt_len < 13)) {
756 retval = PAM_AUTH_ERR;
758 if (!strncmp(salt, "$1$", 3)) {
759 pp = Goodcrypt_md5(p, salt);
760 if (strcmp(pp, salt) != 0) {
762 pp = Brokencrypt_md5(p, salt);
765 pp = bigcrypt(p, salt);
767 p = NULL; /* no longer needed here */
769 /* the moment of truth -- do we agree with the password? */
770 D(("comparing state of pp[%s] and salt[%s]", pp, salt));
773 * Note, we are comparing the bigcrypt of the password with
774 * the contents of the password field. If the latter was
775 * encrypted with regular crypt (and not bigcrypt) it will
776 * have been truncated for storage relative to the output
777 * of bigcrypt here. As such we need to compare only the
778 * stored string with the subset of bigcrypt's result.
779 * Bug 521314: The strncmp comparison is for legacy support.
781 if (strncmp(pp, salt, salt_len) == 0) {
782 retval = PAM_SUCCESS;
784 retval = PAM_AUTH_ERR;
789 if (retval == PAM_SUCCESS) {
790 if (data_name) /* reset failures */
791 pam_set_data(pamh, data_name, NULL, _cleanup_failures);
793 if (data_name != NULL) {
794 struct _pam_failed_auth *new = NULL;
795 const struct _pam_failed_auth *old = NULL;
797 /* get a failure recorder */
799 new = (struct _pam_failed_auth *)
800 malloc(sizeof(struct _pam_failed_auth));
804 const char *login_name;
805 const void *void_old;
808 login_name = _pammodutil_getlogin(pamh);
809 if (login_name == NULL) {
813 new->user = x_strdup(name ? name : "");
815 new->euid = geteuid();
816 new->name = x_strdup(login_name);
818 /* any previous failures for this user ? */
819 if (pam_get_data(pamh, data_name, &void_old)
826 new->count = old->count + 1;
827 if (new->count >= UNIX_MAX_RETRIES) {
828 retval = PAM_MAXTRIES;
831 const void *service=NULL;
832 const void *ruser=NULL;
833 const void *rhost=NULL;
834 const void *tty=NULL;
836 (void) pam_get_item(pamh, PAM_SERVICE,
838 (void) pam_get_item(pamh, PAM_RUSER,
840 (void) pam_get_item(pamh, PAM_RHOST,
842 (void) pam_get_item(pamh, PAM_TTY,
845 _log_err(LOG_NOTICE, pamh,
846 "authentication failure; "
847 "logname=%s uid=%d euid=%d "
848 "tty=%s ruser=%s rhost=%s "
850 new->name, new->uid, new->euid,
854 (new->user && new->user[0] != '\0')
861 pam_set_data(pamh, data_name, new, _cleanup_failures);
864 _log_err(LOG_CRIT, pamh,
865 "no memory for failure recorder");
872 _pam_delete(data_name);
878 D(("done [%d].", retval));
884 * obtain a password from the user
887 int _unix_read_password(pam_handle_t * pamh
892 ,const char *data_name
902 * make sure nothing inappropriate gets returned
905 *pass = token = NULL;
908 * which authentication token are we getting?
911 authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
914 * should we obtain the password from a PAM item ?
917 if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
918 retval = pam_get_item(pamh, authtok_flag, pass);
919 if (retval != PAM_SUCCESS) {
921 _log_err(LOG_ALERT, pamh
922 ,"pam_get_item returned error to unix-read-password"
925 } else if (*pass != NULL) { /* we have a password! */
927 } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
928 return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
929 } else if (on(UNIX_USE_AUTHTOK, ctrl)
930 && off(UNIX__OLD_PASSWD, ctrl)) {
931 return PAM_AUTHTOK_ERR;
935 * getting here implies we will have to get the password from the
940 struct pam_message msg[3], *pmsg[3];
941 struct pam_response *resp;
944 /* prepare to converse */
946 if (comment != NULL && off(UNIX__QUIET, ctrl)) {
948 msg[0].msg_style = PAM_TEXT_INFO;
949 msg[0].msg = comment;
956 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
957 msg[i++].msg = prompt1;
960 if (prompt2 != NULL) {
962 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
963 msg[i++].msg = prompt2;
966 /* so call the conversation expecting i responses */
968 retval = converse(pamh, ctrl, i, pmsg, &resp);
972 /* interpret the response */
974 if (retval == PAM_SUCCESS) { /* a good conversation */
976 token = x_strdup(resp[i - replies].resp);
980 /* verify that password entered correctly */
981 if (!resp[i - 1].resp
982 || strcmp(token, resp[i - 1].resp)) {
983 _pam_delete(token); /* mistyped */
984 retval = PAM_AUTHTOK_RECOVER_ERR;
985 _make_remark(pamh, ctrl
986 ,PAM_ERROR_MSG, MISTYPED_PASS);
990 _log_err(LOG_NOTICE, pamh
991 ,"could not recover authentication token");
996 * tidy up the conversation (resp_retcode) is ignored
997 * -- what is it for anyway? AGM
1000 _pam_drop_reply(resp, i);
1003 retval = (retval == PAM_SUCCESS)
1004 ? PAM_AUTHTOK_RECOVER_ERR : retval;
1008 if (retval != PAM_SUCCESS) {
1009 if (on(UNIX_DEBUG, ctrl))
1010 _log_err(LOG_DEBUG, pamh,
1011 "unable to obtain a password");
1014 /* 'token' is the entered password */
1016 if (off(UNIX_NOT_SET_PASS, ctrl)) {
1018 /* we store this password as an item */
1020 retval = pam_set_item(pamh, authtok_flag, token);
1021 _pam_delete(token); /* clean it up */
1022 if (retval != PAM_SUCCESS
1023 || (retval = pam_get_item(pamh, authtok_flag, pass))
1027 _log_err(LOG_CRIT, pamh, "error manipulating password");
1033 * then store it as data specific to this module. pam_end()
1034 * will arrange to clean it up.
1037 retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
1038 if (retval != PAM_SUCCESS) {
1039 _log_err(LOG_CRIT, pamh
1040 ,"error manipulating password data [%s]"
1041 ,pam_strerror(pamh, retval));
1046 token = NULL; /* break link to password */
1052 int _unix_shadowed(const struct passwd *pwd)
1055 if (strcmp(pwd->pw_passwd, "x") == 0) {
1058 if ((pwd->pw_passwd[0] == '#') &&
1059 (pwd->pw_passwd[1] == '#') &&
1060 (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) {
1067 /* ****************************************************************** *
1068 * Copyright (c) Jan Rêkorajski 1999.
1069 * Copyright (c) Andrew G. Morgan 1996-8.
1070 * Copyright (c) Alex O. Yuriev, 1996.
1071 * Copyright (c) Cristian Gafton 1996.
1073 * Redistribution and use in source and binary forms, with or without
1074 * modification, are permitted provided that the following conditions
1076 * 1. Redistributions of source code must retain the above copyright
1077 * notice, and the entire permission notice in its entirety,
1078 * including the disclaimer of warranties.
1079 * 2. Redistributions in binary form must reproduce the above copyright
1080 * notice, this list of conditions and the following disclaimer in the
1081 * documentation and/or other materials provided with the distribution.
1082 * 3. The name of the author may not be used to endorse or promote
1083 * products derived from this software without specific prior
1084 * written permission.
1086 * ALTERNATIVELY, this product may be distributed under the terms of
1087 * the GNU Public License, in which case the provisions of the GPL are
1088 * required INSTEAD OF the above restrictions. (This clause is
1089 * necessary due to a potential bad interaction between the GPL and
1090 * the restrictions contained in a BSD-style copyright.)
1092 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
1093 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1094 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1095 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1096 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1097 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1098 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1099 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
1100 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1101 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
1102 * OF THE POSSIBILITY OF SUCH DAMAGE.