Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtlogin / bls / validate.c
1 /* $XConsortium: validate.c /main/4 1995/10/27 16:19:47 rswiston $ */
2 /************************************<+>*************************************
3  ****************************************************************************
4  **
5  **   File:        validate.c
6  **
7  **   Project:     HP Visual User Environment (DT)
8  **
9  **   Description: Dtgreet BLS user authentication routines
10  **
11  **                These routines validate the user; checking name, password,
12  **                number of users on the system, password aging, etc.
13  **
14  **
15  **   (c) Copyright 1987, 1988, 1989 by Hewlett-Packard Company
16  **
17  **
18  **     Conditional compiles:
19  **
20  **     OSMAJORVERSION < 8
21  **                 HP-UX 7.0/7.03 restricted license counting algorithms
22  **                 are used. Otherwise HP-UX 8.0 and beyond is used
23  **
24  **     BLS         HP BLS B1 simple authentication.
25  **
26  **     __hpux      HP-UX OS only
27  **
28  ****************************************************************************
29  ************************************<+>*************************************/
30
31 #ifdef  BLS
32
33 /***************************************************************************
34  *
35  *  Includes & Defines
36  *
37  ***************************************************************************/
38
39 #include        <stdio.h>
40 #include        <fcntl.h>
41 #include        <stdlib.h>
42 #include        <pwd.h>
43
44 #include        "../vg.h"
45
46
47
48 /***************************************************************************
49  *
50  *  HP-UX BLS authentication routines
51  *
52  ***************************************************************************/
53
54 #include        <sys/param.h>     /* for MAXUID macro */
55 #include        <sys/types.h>
56 #include        <sys/utsname.h>
57 #include        <string.h>
58 #include        <utmp.h>
59 #include        <time.h>
60 #include        <grp.h>
61
62
63 /*  BLS only headers */
64 #    include <sys/security.h>
65 #    include <prot.h>
66 #    include "bls.h"
67
68
69 #define how_to_count ut_exit.e_exit
70
71 #ifdef __hp9000s300
72 static    int num_users[] = { 2, 32767 };
73 #   define MIN_VERSION     'A'
74 #   define UNLIMITED       'B'
75 #else
76 static    int num_users[] = { 2, 16, 32, 64 , 8 };
77 #   define MIN_VERSION  'A'
78 #   define UNLIMITED    'U'
79 #endif
80
81 /* Maximum number of users allowed with restricted license */
82 #if OSMAJORVERSION < 8
83 #    define MAX_STRICT_USERS 2  
84 #else
85 #    define MAX_STRICT_USERS 8
86 #endif
87
88 #define NUM_VERSIONS    (sizeof(num_users)/sizeof(num_users[0])) - 1
89
90
91
92 /***************************************************************************
93  *
94  *  External declarations
95  *
96  ***************************************************************************/
97
98 extern Widget focusWidget;              /* login or password text field    */
99
100 extern long     groups[NGROUPS];
101
102
103 /***************************************************************************
104  *
105  *  Procedure declarations
106  *
107  ***************************************************************************/
108
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) ;
113
114
115
116
117 /***************************************************************************
118  *
119  *  Global variables
120  *
121  ***************************************************************************/
122
123 /* BLS only data */
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;
130
131  
132 /***************************************************************************
133  *
134  *  CountUsers
135  *
136  *  see if new user has exceeded the maximum.
137  ***************************************************************************/
138
139 #define NCOUNT 16
140
141 static int 
142 CountUsers( int added_users )
143 {
144     int count[NCOUNT], nusers, i;
145     struct utmp *entry;
146
147     for (i=0; i<NCOUNT; i++)
148         count[i] = 0;
149
150     count[added_users]++;
151
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 */
158             count[i]++;
159         }
160     }
161     endutent();
162
163     /*
164      * KEY:
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
173      *          unique user name.
174      *  [4-15]  may be used for groups of users which collectively
175      *          count as 1
176      */
177     nusers = count[1];
178
179 #if OSMAJORVERSION < 8
180     for (i=2; i<NCOUNT; i++)
181 #else
182     for (i=3; i<NCOUNT; i++)
183 #endif
184         if (count[i] > 0)
185             nusers++;
186
187     return(nusers);
188 }
189
190
191
192
193 /***************************************************************************
194  *
195  *  CountUsersStrict
196  *
197  *  see if new user has exceeded the maximum.
198  ***************************************************************************/
199
200 static int 
201 CountUsersStrict( char *new_user )
202 {
203     char pty_users[MAX_STRICT_USERS][8];
204     int count[NCOUNT], nusers, i, cnt, pty_off = -1, uname_off;
205     struct utmp *entry;
206
207     /*
208      *  Initialize count array...
209      */
210     for (i = 0; i < NCOUNT; i++)
211         count[i] = 0;
212
213     /*
214      *  Add in the new user (we know it's not a pty)...
215      */
216     count[1]++;
217
218     while ( (entry = getutent()) != NULL ) {
219         if (entry->ut_type == USER_PROCESS) {
220             i = entry->how_to_count;
221
222             /* if out of range, then count as ordinary user logged in 
223                via a tty */
224             if (i == 1 || (i < 0 || i >= NCOUNT))
225                 count[1]++;
226             /* See if it is a pty login granted by login program */
227             else if (i == 3) {
228                 count[3]++;
229                 /* See if user is already logged in via login pty */
230                 uname_off = -1;
231                 for (cnt = 0; cnt <= pty_off; cnt++)
232                         if (strncmp(pty_users[cnt], entry->ut_user, 8) == 0)
233                                 uname_off = cnt;
234
235                 if (uname_off == -1) { /* user is not logged in via pty yet */
236                         
237                     if (pty_off >= MAX_STRICT_USERS)  /* cannot add any
238                                                          more users */
239                         return(MAX_STRICT_USERS + 1);
240                     /* add the user name to the array of pty users */
241                     else
242                         strncpy(pty_users[++pty_off], entry->ut_user, 8);
243                 }
244             } /* end if (i == 3) */
245             else
246                 count[i]++;
247         } /* end if entry->ut_type == USER_PROCESS */
248     } /* end while (entry = getutent()) */
249
250     endutent();
251     /*
252      * KEY:
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
261      *          unique user name.
262      *  [4-15]  may be used for groups of users which collectively count 
263      *          as 1
264      */
265
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
270                                           login */
271
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 */
277 #else
278     /*
279      * Don't count any hpterm logins (exit status of 2).  We already
280      * counted all pty logins granted by the login program.
281      */
282 #endif
283
284     for (i = 4; i < NCOUNT; i++)
285         if (count[i] > 0)
286             nusers++;
287     return(nusers);
288 }
289
290
291
292 /***************************************************************************
293  *
294  *  CheckPassword
295  *
296  *  Check validity of user password. 
297  *
298  ***************************************************************************/
299
300 static int
301 CheckPassword( char *name, char *passwd )
302 {
303
304     char                *crypt();
305     struct passwd       *p;
306     char                *reason;
307
308     /*
309      *  HP BLS B1 password authentication...
310      */
311
312     if ( ISSECURE ) {
313         b1_pwd = getprpwnam(name);
314     
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",
319                 ES_LOGIN_FAILED);
320             return(FALSE);
321         }
322     
323     /*
324      *  look up user's regular account information...
325      */
326
327     p = getpwnam(name);
328         
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",
333             ES_LOGIN_FAILED);
334         return(FALSE);
335     }
336                 
337     /*  verify_info has become a catchall for info needed later */
338         verify->user_name = name;
339         verify->prpwd = b1_pwd;
340         verify->pwd = p;
341         strncpy(verify->terminal, dpyinfo.name, 15);
342         verify->terminal[15]='\0';
343
344     }     
345
346     Debug("Verify %s \n",name);
347
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",
356                         ES_LOGIN_FAILED);
357             return(FALSE);
358         } else {
359             Debug ("username/password verify succeeded\n");
360             return(TRUE);
361         }    
362     /*
363      *  all password checks failed...
364      */
365      
366     return (FALSE);
367
368 }
369
370
371
372
373 /***************************************************************************
374  *
375  *  BLS_Verify
376  *
377  *  verify the user
378  *
379  *  return codes indicate authentication results.
380  ***************************************************************************/
381
382 #define MAXATTEMPTS     3
383
384 static struct  passwd nouser = {"", "nope"};    /* invalid user password struct    */
385
386 int 
387 BLS_Verify( char *name, char *passwd )
388 {
389
390     static int          login_attempts = 0; /* # failed authentications    */
391     
392     struct passwd       *p;             /* password structure */
393     struct pr_passwd *prpwd;
394
395     struct utsname      utsnam;
396     int                 n;
397     int                 uid;
398
399     /*
400      * Desparate maneuvre to give dtgreet the privledges it needs
401      */
402      if ( login_attempts == 0 ) {
403         Debug("Setting luid for dtgreet\n");
404         if ( getluid() == -1 )
405                 setluid(getuid());
406      }
407     
408     /*
409      *  validate password...
410      */
411     
412     if ( CheckPassword(name, passwd) == FALSE) {
413         p = verify->pwd;
414         if ( focusWidget == passwd_text ) {
415         
416             WriteBtmp(name);
417
418             if ((++login_attempts % MAXATTEMPTS) == 0 ) {
419
420                 if (p->pw_name == NULL )
421                     p = &nouser;
422
423                 audit_login( b1_pwd, p ,dpyinfo.name,
424                         "Failed login(bailout)",
425                         ES_LOGIN_FAILED);
426             }
427             
428         } else if ( !UserHasPassword ) {
429                 /*
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.
433                  */
434                 UserHasPassword = 1;
435                 return  VF_PASSWD_AGED;
436         }
437         
438         return(VF_INVALID);
439     }
440     prpwd = verify->prpwd;
441     p = verify->pwd;
442     
443     /* check that the uid of both passwd and pr_passwd struct's agree */
444     uid = p->pw_uid;
445     if (uid != prpwd->ufld.fd_uid) {
446         audit_login(prpwd, p, verify->terminal, 
447             "User id's inconsistent across password database\n",
448             ES_LOGIN_FAILED);
449         Debug("login failed - uid's do not match\n");
450         return VF_BAD_UID;
451     }
452     verify->uid = uid;
453
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.
458      */
459     if (locked_out(prpwd)) {
460         Debug("Account locked\n");
461         audit_login(prpwd, p, verify->terminal,
462             "Account locked", ES_LOGIN_FAILED);
463         return VF_INVALID;
464     }
465     /* can user log in at this time?
466      * time_lock is in libsec, but poorly documented
467      */
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);
472         return VF_INVALID;
473     }
474     
475     /****************************************************
476       xdm checks the security level here using
477       verify_sec_user
478       We do it later from the dtgreet callback rountine
479       VerifySensitivityLevel()
480     ****************************************************/
481
482 #if 0 
483     /*
484      *  check restricted license...
485      *
486      *  Note: This only applies to local displays. Foreign displays
487      *        (i.e. X-terminals) apparently do not count.
488      */
489
490     /* Get the version info via uname.  If it doesn't look right,
491      * assume the smallest user configuration
492      */
493
494     if (getenv(LOCATION) != NULL) {
495         if (uname(&utsnam) < 0)
496             utsnam.version[0] = MIN_VERSION;
497
498         /*
499          * Mappings:
500          *    834 -> 834
501          *    844 -> 844
502          *    836 -> 635
503          *    846 -> 645
504          *    843 -> 642
505          *    853 -> 652
506          */
507
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))) {
514
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",
519                         ES_LOGIN_FAILED);
520                 return(VF_MAX_USERS);
521             }
522         }
523         else {
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;
528
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",
533                                 ES_LOGIN_FAILED);
534                     return(VF_MAX_USERS);
535                 }
536             }
537         }
538     }
539
540 #endif  /* 0 */
541
542     /*
543      *  check password aging...
544      */
545
546     if ( passwordExpired(verify)) {
547         audit_login( b1_pwd, p ,dpyinfo.name,
548                 "Password expired",
549                 ES_LOGIN_FAILED);
550         return(VF_PASSWD_AGED);
551     }
552          
553
554     /*
555      *  verify home directory exists...
556      */
557
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",
562                 ES_LOGIN_FAILED);
563         return(VF_HOME);
564     }
565
566     /*
567      *  validate uid and gid...
568      */
569 #ifdef NGROUPS
570     getGroups(greet->name, verify, p->pw_gid);
571 #else
572     verify->gid = pwd->pw_gid;
573     
574     if ((p->pw_gid < 0)      || 
575         (p->pw_gid > MAXUID) ||
576         (setgid(p->pw_gid) == -1)) {
577
578         Debug("Attempted to login -- bad group id");
579         audit_login( b1_pwd, p ,dpyinfo.name,
580                 "Attempted to login - bad group id",
581                 ES_LOGIN_FAILED);
582         return(VF_BAD_GID);
583     }
584 #endif  /* NGROUPS */
585
586     if ((p->pw_uid < 0)      || 
587         (p->pw_uid > MAXUID) ||
588         (setresuid(p->pw_uid, p->pw_uid, 0) == -1)) {
589
590         Debug("Attempted to login -- bad user id\n");
591         audit_login( b1_pwd, p ,dpyinfo.name,
592                 "Attempted to login - bad user id",
593                 ES_LOGIN_FAILED);
594         return(VF_BAD_UID);
595     }
596
597
598     /*
599      * verify ok...
600      */
601
602     Debug ("Successful login\n");
603     audit_login( b1_pwd, p ,dpyinfo.name,
604         "Successful login",
605         ES_LOGIN_REMOTE);
606     return(VF_OK);
607 }
608
609
610
611
612 /***************************************************************************
613  *
614  *  WriteBtmp
615  *
616  *  log bad login attempts
617  *  
618  ***************************************************************************/
619
620 static void 
621 WriteBtmp( char *name )
622 {
623     int fd;
624     struct utmp utmp, *u;
625
626     Boolean found=FALSE;
627
628     bzero(&utmp, sizeof(struct utmp));
629
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 ) {
636
637                 found = TRUE;
638                 break;
639         }
640     }
641
642
643     /*
644      *  if no utmp entry, this may be an X-terminal. Construct a utmp
645      *  entry for it...
646      */
647
648     if ( ! found ) {
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));
653         u = &utmp;
654     }
655     
656     
657     /*
658      * If btmp exists, then record the bad attempt
659      */
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));
664         (void) close(fd);
665     }
666
667     endutent();         /* Close utmp file */
668 }
669
670
671 /***************************************************************************
672  *
673  *  VerifySensitivityLevel
674  *
675  *  verify B1 Sensitivity Level
676  **************************************************************************/
677 extern   char   *sensitivityLevel;
678
679 int
680 VerifySensitivityLevel( void)
681 {
682
683     int i;
684
685     greet->b1security = sensitivityLevel =
686         (char *) XmTextFieldGetString(passwd_text);
687
688     /* new functions: (side effects: auditing, change verify) */
689     if (verify_user_seclevel(verify, sensitivityLevel)
690             && verify_sec_xterm(verify, sensitivityLevel)) {
691
692             Debug("verify_user_seclevel succeeded.\n");
693             return VF_OK;
694     }
695
696     Debug("verify_user_seclevel failed\n");
697     return (VF_BAD_SEN_LEVEL);
698 }
699
700
701 #ifdef NGROUPS
702 groupMember ( char *name, char **members )
703 {
704         while (*members) {
705                 if (!strcmp (name, *members))
706                         return 1;
707                 ++members;
708         }
709         return 0;
710 }
711
712 getGroups ( char *name, struct verify_info *verify, int gid)
713 {
714         int             ngroups;
715         struct group    *g;
716         int             i;
717
718         ngroups = 0;
719         verify->groups[ngroups++] = gid;
720         setgrent ();
721         while (g = getgrent()) {
722                 /*
723                  * make the list unique
724                  */
725                 for (i = 0; i < ngroups; i++)
726                         if (verify->groups[i] == g->gr_gid)
727                                 break;
728                 if (i != ngroups)
729                         continue;
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);
734                         else
735                                 verify->groups[ngroups++] = g->gr_gid;
736                 }
737         }
738         verify->ngroups = ngroups;
739         endgrent ();
740 }
741 #endif
742
743 /* check whether the password has expired or not.
744  * return 1 means that the password has expired. 
745  */
746 int 
747 passwordExpired( struct verify_info *verify)
748 {
749         register struct pr_passwd *pr;
750         register time_t expiration;
751         register time_t last_change;
752         time_t expiration_time;
753         register time_t now;
754         register int passwd_status;
755         struct pr_passwd save_data;
756         struct pr_default *df;
757         char *ttime;
758         char ptime[64];
759
760         pr = verify->prpwd;
761
762         /* 
763          * If null password, do not check expiration.
764          */
765
766         if (!pr->uflg.fg_encrypt || (pr->ufld.fd_encrypt[0] == '\0'))
767                 return 0;
768
769         now = time((long *) 0);
770
771         if (pr->uflg.fg_schange)
772                 last_change = pr->ufld.fd_schange;
773         else
774                 last_change = (time_t) 0;
775
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;
780         else
781                 expiration = (time_t) 0;
782
783         df = getprdfnam(AUTH_DEFAULT);
784
785         /*
786          * A 0 or missing expiration field means there is no
787          * expiration.
788          */
789         expiration_time = expiration ? last_change + expiration : 0;
790         
791         if (expiration_time && now > expiration_time ) {
792                 /*
793                  * The password has expired
794                  */
795                 Debug("The password is expired\n");
796                 return 1;
797         }
798
799         Debug("The password is not expired\n");
800         return 0;
801 }
802
803
804 /***************************************************************************
805  *
806  *  end HP-UX authentication routines
807  *
808  ***************************************************************************/
809 #endif  /* BLS */