1 /* $XConsortium: validate.c /main/4 1995/10/27 16:19:47 rswiston $ */
2 /************************************<+>*************************************
3 ****************************************************************************
7 ** Project: HP Visual User Environment (DT)
9 ** Description: Dtgreet BLS user authentication routines
11 ** These routines validate the user; checking name, password,
12 ** number of users on the system, password aging, etc.
15 ** (c) Copyright 1987, 1988, 1989 by Hewlett-Packard Company
18 ** Conditional compiles:
21 ** HP-UX 7.0/7.03 restricted license counting algorithms
22 ** are used. Otherwise HP-UX 8.0 and beyond is used
24 ** BLS HP BLS B1 simple authentication.
26 ** __hpux HP-UX OS only
28 ****************************************************************************
29 ************************************<+>*************************************/
33 /***************************************************************************
37 ***************************************************************************/
48 /***************************************************************************
50 * HP-UX BLS authentication routines
52 ***************************************************************************/
54 #include <sys/param.h> /* for MAXUID macro */
55 #include <sys/types.h>
56 #include <sys/utsname.h>
63 /* BLS only headers */
64 # include <sys/security.h>
69 #define how_to_count ut_exit.e_exit
72 static int num_users[] = { 2, 32767 };
73 # define MIN_VERSION 'A'
74 # define UNLIMITED 'B'
76 static int num_users[] = { 2, 16, 32, 64 , 8 };
77 # define MIN_VERSION 'A'
78 # define UNLIMITED 'U'
81 /* Maximum number of users allowed with restricted license */
82 #if OSMAJORVERSION < 8
83 # define MAX_STRICT_USERS 2
85 # define MAX_STRICT_USERS 8
88 #define NUM_VERSIONS (sizeof(num_users)/sizeof(num_users[0])) - 1
92 /***************************************************************************
94 * External declarations
96 ***************************************************************************/
98 extern Widget focusWidget; /* login or password text field */
100 extern long groups[NGROUPS];
103 /***************************************************************************
105 * Procedure declarations
107 ***************************************************************************/
109 static int CheckPassword( char *name, char *passwd );
110 static int CountUsers( int added_users) ;
111 static int CountUsersStrict( char *new_user) ;
112 static void WriteBtmp( char *name) ;
117 /***************************************************************************
121 ***************************************************************************/
124 struct pr_passwd *b1_pwd;
125 struct verify_info verify_data;
126 struct verify_info *verify = &verify_data;
127 struct greet_info greet_data;
128 struct greet_info *greet = &greet_data;
129 static int UserHasPassword = 1;
132 /***************************************************************************
136 * see if new user has exceeded the maximum.
137 ***************************************************************************/
142 CountUsers( int added_users )
144 int count[NCOUNT], nusers, i;
147 for (i=0; i<NCOUNT; i++)
150 count[added_users]++;
152 while ( (entry = getutent()) != NULL) {
153 if (entry->ut_type == USER_PROCESS) {
154 i = entry->how_to_count;
155 if (i < 0 || i >= NCOUNT)
156 i = 1; /* if out of range, then count */
157 /* as ordinary user */
165 * [0] does not count at all
166 * [1] counts as real user
167 * [2] logins via a pty which have not gone trough login. These
168 * collectively count as 1 user IF count[3] is 0, otherwise,
169 * they are not counted. Starting with HP-UX 8.0 they are
170 * no longer counted at all.
171 * [3] logins via a pty which have been logged through login (i.e.
172 * rlogin and telnet). these count as 1 "real" user per
174 * [4-15] may be used for groups of users which collectively
179 #if OSMAJORVERSION < 8
180 for (i=2; i<NCOUNT; i++)
182 for (i=3; i<NCOUNT; i++)
193 /***************************************************************************
197 * see if new user has exceeded the maximum.
198 ***************************************************************************/
201 CountUsersStrict( char *new_user )
203 char pty_users[MAX_STRICT_USERS][8];
204 int count[NCOUNT], nusers, i, cnt, pty_off = -1, uname_off;
208 * Initialize count array...
210 for (i = 0; i < NCOUNT; i++)
214 * Add in the new user (we know it's not a pty)...
218 while ( (entry = getutent()) != NULL ) {
219 if (entry->ut_type == USER_PROCESS) {
220 i = entry->how_to_count;
222 /* if out of range, then count as ordinary user logged in
224 if (i == 1 || (i < 0 || i >= NCOUNT))
226 /* See if it is a pty login granted by login program */
229 /* See if user is already logged in via login pty */
231 for (cnt = 0; cnt <= pty_off; cnt++)
232 if (strncmp(pty_users[cnt], entry->ut_user, 8) == 0)
235 if (uname_off == -1) { /* user is not logged in via pty yet */
237 if (pty_off >= MAX_STRICT_USERS) /* cannot add any
239 return(MAX_STRICT_USERS + 1);
240 /* add the user name to the array of pty users */
242 strncpy(pty_users[++pty_off], entry->ut_user, 8);
244 } /* end if (i == 3) */
247 } /* end if entry->ut_type == USER_PROCESS */
248 } /* end while (entry = getutent()) */
253 * [0] does not count at all
254 * [1] counts as "real" user
255 * [2] logins via a pty which have not gone trough login. These
256 * collectively count as 1 user IF count[3] is 0, otherwise,
257 * they are not counted. Starting with HP-UX 8.0 they are
258 * no longer counted at all.
259 * [3] logins via a pty which have been logged through login (i.e.
260 * rlogin and telnet). these count as 1 "real" user per
262 * [4-15] may be used for groups of users which collectively count
266 nusers = pty_off + 1 + count[1]; /* Current number of users is sum of
267 users logged in via tty + the
268 number of unique users logged in
269 via pty which have gone through
272 #if OSMAJORVERSION < 8
273 if ((count[3] == 0) && (count[2] != 0))
274 nusers++; /* Add 1 user for all pty logins IF
275 none of pty logins have been
276 granted by the login program */
279 * Don't count any hpterm logins (exit status of 2). We already
280 * counted all pty logins granted by the login program.
284 for (i = 4; i < NCOUNT; i++)
292 /***************************************************************************
296 * Check validity of user password.
298 ***************************************************************************/
301 CheckPassword( char *name, char *passwd )
309 * HP BLS B1 password authentication...
313 b1_pwd = getprpwnam(name);
315 if ( b1_pwd == NULL || strlen(name) == 0 ) {
316 Debug("unknown user '%s'\n", name);
317 audit_login((struct pr_passwd *)0, (struct passwd *)0,
318 dpyinfo.name, "No entry in protected password db",
324 * look up user's regular account information...
329 if ( p == NULL || strlen(name) == 0 ) {
330 Debug("unknown user '%s'\n", name);
331 audit_login((struct pr_passwd *)0, (struct passwd *)0,
332 dpyinfo.name, "No entry in password file",
337 /* verify_info has become a catchall for info needed later */
338 verify->user_name = name;
339 verify->prpwd = b1_pwd;
341 strncpy(verify->terminal, dpyinfo.name, 15);
342 verify->terminal[15]='\0';
346 Debug("Verify %s \n",name);
348 /* if the password doesn't exists, we can't check it, but
349 * the user will be forced to change it later */
350 if ( (UserHasPassword = password_exists(verify)) != 0 )
351 if ( strcmp(bigcrypt(passwd,b1_pwd->ufld.fd_encrypt),
352 b1_pwd->ufld.fd_encrypt) ) {
353 Debug("verify failed\n");
354 audit_login( b1_pwd, p ,dpyinfo.name,
355 "Password incorrect",
359 Debug ("username/password verify succeeded\n");
363 * all password checks failed...
373 /***************************************************************************
379 * return codes indicate authentication results.
380 ***************************************************************************/
382 #define MAXATTEMPTS 3
384 static struct passwd nouser = {"", "nope"}; /* invalid user password struct */
387 BLS_Verify( char *name, char *passwd )
390 static int login_attempts = 0; /* # failed authentications */
392 struct passwd *p; /* password structure */
393 struct pr_passwd *prpwd;
395 struct utsname utsnam;
400 * Desparate maneuvre to give dtgreet the privledges it needs
402 if ( login_attempts == 0 ) {
403 Debug("Setting luid for dtgreet\n");
404 if ( getluid() == -1 )
409 * validate password...
412 if ( CheckPassword(name, passwd) == FALSE) {
414 if ( focusWidget == passwd_text ) {
418 if ((++login_attempts % MAXATTEMPTS) == 0 ) {
420 if (p->pw_name == NULL )
423 audit_login( b1_pwd, p ,dpyinfo.name,
424 "Failed login(bailout)",
428 } else if ( !UserHasPassword ) {
430 * The user has not password -- this must be the initial login for this
431 * user. Treat it like an expired password. This should invoke the
432 * password program on behalf of the user.
435 return VF_PASSWD_AGED;
440 prpwd = verify->prpwd;
443 /* check that the uid of both passwd and pr_passwd struct's agree */
445 if (uid != prpwd->ufld.fd_uid) {
446 audit_login(prpwd, p, verify->terminal,
447 "User id's inconsistent across password database\n",
449 Debug("login failed - uid's do not match\n");
454 /* check if user's account is locked
455 * This can be by dead password (lifetime exceeded),
456 * fd_lock is set, or fd_max_tries is exceeded.
457 * locked_out is from libsec, but is poorly documented.
459 if (locked_out(prpwd)) {
460 Debug("Account locked\n");
461 audit_login(prpwd, p, verify->terminal,
462 "Account locked", ES_LOGIN_FAILED);
465 /* can user log in at this time?
466 * time_lock is in libsec, but poorly documented
468 if (time_lock(prpwd)) {
469 Debug("Account time-locked\n");
470 audit_login(prpwd, p, verify->terminal,
471 "Account time-locked", ES_LOGIN_FAILED);
475 /****************************************************
476 xdm checks the security level here using
478 We do it later from the dtgreet callback rountine
479 VerifySensitivityLevel()
480 ****************************************************/
484 * check restricted license...
486 * Note: This only applies to local displays. Foreign displays
487 * (i.e. X-terminals) apparently do not count.
490 /* Get the version info via uname. If it doesn't look right,
491 * assume the smallest user configuration
494 if (getenv(LOCATION) != NULL) {
495 if (uname(&utsnam) < 0)
496 utsnam.version[0] = MIN_VERSION;
508 if ((!strncmp(utsnam.machine, "9000/834", UTSLEN)) ||
509 (!strncmp(utsnam.machine, "9000/844", UTSLEN)) ||
510 (!strncmp(utsnam.machine, "9000/836", UTSLEN)) ||
511 (!strncmp(utsnam.machine, "9000/846", UTSLEN)) ||
512 (!strncmp(utsnam.machine, "9000/843", UTSLEN)) ||
513 (!strncmp(utsnam.machine, "9000/853", UTSLEN))) {
515 /* strict_count = 1;*/
516 if (CountUsersStrict(name) > MAX_STRICT_USERS) {
517 audit_login( b1_pwd, p ,dpyinfo.name,
518 "Attempted to login - too many users on the system",
520 return(VF_MAX_USERS);
524 if (utsnam.version[0] != UNLIMITED) {
525 if ((utsnam.version[0]-'A' < 0) ||
526 (utsnam.version[0]-'A' > NUM_VERSIONS))
527 utsnam.version[0] = MIN_VERSION;
529 n = (int) utsnam.version[0] - 'A';
530 if (CountUsers(1) > num_users[n]) {
531 audit_login( b1_pwd, p ,dpyinfo.name,
532 "Attempted to login - too many users on the system",
534 return(VF_MAX_USERS);
543 * check password aging...
546 if ( passwordExpired(verify)) {
547 audit_login( b1_pwd, p ,dpyinfo.name,
550 return(VF_PASSWD_AGED);
555 * verify home directory exists...
558 if(chdir(p->pw_dir) < 0) {
559 Debug("Attempted to login -- no home directory\n");
560 audit_login( b1_pwd, p ,dpyinfo.name,
561 " Attempted to login - no home directory",
567 * validate uid and gid...
570 getGroups(greet->name, verify, p->pw_gid);
572 verify->gid = pwd->pw_gid;
574 if ((p->pw_gid < 0) ||
575 (p->pw_gid > MAXUID) ||
576 (setgid(p->pw_gid) == -1)) {
578 Debug("Attempted to login -- bad group id");
579 audit_login( b1_pwd, p ,dpyinfo.name,
580 "Attempted to login - bad group id",
586 if ((p->pw_uid < 0) ||
587 (p->pw_uid > MAXUID) ||
588 (setresuid(p->pw_uid, p->pw_uid, 0) == -1)) {
590 Debug("Attempted to login -- bad user id\n");
591 audit_login( b1_pwd, p ,dpyinfo.name,
592 "Attempted to login - bad user id",
602 Debug ("Successful login\n");
603 audit_login( b1_pwd, p ,dpyinfo.name,
612 /***************************************************************************
616 * log bad login attempts
618 ***************************************************************************/
621 WriteBtmp( char *name )
624 struct utmp utmp, *u;
628 bzero(&utmp, sizeof(struct utmp));
630 utmp.ut_pid = getppid();
631 while ((u = getutent()) != NULL) {
632 if ( (u->ut_type == INIT_PROCESS ||
633 u->ut_type == LOGIN_PROCESS ||
634 u->ut_type == USER_PROCESS) &&
635 u->ut_pid == utmp.ut_pid ) {
644 * if no utmp entry, this may be an X-terminal. Construct a utmp
649 strncpy(utmp.ut_id, "??", sizeof(utmp.ut_id));
650 strncpy(utmp.ut_line, dpyinfo.name, sizeof(utmp.ut_line));
651 utmp.ut_type = LOGIN_PROCESS;
652 strncpy(utmp.ut_host, dpyinfo.name, sizeof(utmp.ut_host));
658 * If btmp exists, then record the bad attempt
660 if ( (fd = open(BTMP_FILE,O_WRONLY|O_APPEND)) >= 0) {
661 strncpy(u->ut_user, name, sizeof(u->ut_user));
662 (void) time(&u->ut_time);
663 write(fd, (char *)u, sizeof(utmp));
667 endutent(); /* Close utmp file */
671 /***************************************************************************
673 * VerifySensitivityLevel
675 * verify B1 Sensitivity Level
676 **************************************************************************/
677 extern char *sensitivityLevel;
680 VerifySensitivityLevel( void)
685 greet->b1security = sensitivityLevel =
686 (char *) XmTextFieldGetString(passwd_text);
688 /* new functions: (side effects: auditing, change verify) */
689 if (verify_user_seclevel(verify, sensitivityLevel)
690 && verify_sec_xterm(verify, sensitivityLevel)) {
692 Debug("verify_user_seclevel succeeded.\n");
696 Debug("verify_user_seclevel failed\n");
697 return (VF_BAD_SEN_LEVEL);
702 groupMember ( char *name, char **members )
705 if (!strcmp (name, *members))
712 getGroups ( char *name, struct verify_info *verify, int gid)
719 verify->groups[ngroups++] = gid;
721 while (g = getgrent()) {
723 * make the list unique
725 for (i = 0; i < ngroups; i++)
726 if (verify->groups[i] == g->gr_gid)
730 if (groupMember (name, g->gr_mem)) {
731 if (ngroups >= NGROUPS)
732 LogError ("%s belongs to more than %d groups, %s ignored\n",
733 name, NGROUPS, g->gr_name);
735 verify->groups[ngroups++] = g->gr_gid;
738 verify->ngroups = ngroups;
743 /* check whether the password has expired or not.
744 * return 1 means that the password has expired.
747 passwordExpired( struct verify_info *verify)
749 register struct pr_passwd *pr;
750 register time_t expiration;
751 register time_t last_change;
752 time_t expiration_time;
754 register int passwd_status;
755 struct pr_passwd save_data;
756 struct pr_default *df;
763 * If null password, do not check expiration.
766 if (!pr->uflg.fg_encrypt || (pr->ufld.fd_encrypt[0] == '\0'))
769 now = time((long *) 0);
771 if (pr->uflg.fg_schange)
772 last_change = pr->ufld.fd_schange;
774 last_change = (time_t) 0;
776 if (pr->uflg.fg_expire)
777 expiration = pr->ufld.fd_expire;
778 else if (pr->sflg.fg_expire)
779 expiration = pr->sfld.fd_expire;
781 expiration = (time_t) 0;
783 df = getprdfnam(AUTH_DEFAULT);
786 * A 0 or missing expiration field means there is no
789 expiration_time = expiration ? last_change + expiration : 0;
791 if (expiration_time && now > expiration_time ) {
793 * The password has expired
795 Debug("The password is expired\n");
799 Debug("The password is not expired\n");
804 /***************************************************************************
806 * end HP-UX authentication routines
808 ***************************************************************************/