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