Patches from pkgsrc-WIP
[oweals/cde.git] / cde / programs / dtlogin / bls / get_level.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: get_level.c /main/4 1995/10/27 16:19:39 rswiston $ */
24 /*
25  * get_level.c 
26  * last modified by: 
27  * David Dolson June 7/92
28  *      - rewrote most of B1 security routines.  Much of it is based on
29  *        parallel routines in login.
30  * Ron Voll     July 7/92
31  *      - rolled the xdm version of this file into dtlogin.
32  */
33
34 #ifdef BLS      /* Brackets entire file */
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <signal.h>
38 #include <pwd.h>
39 #include <grp.h>
40 #include <stdio.h>
41 #include <termio.h>
42 #include <errno.h>
43 #ifdef SEC_NET_TTY
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <netdb.h>
47 #endif
48
49 #include <sys/security.h>
50 #include <sys/audit.h>
51 #include <prot.h>
52 #include <protcmd.h>
53 #if defined(TAC3) && !defined(TPLOGIN)
54 #include <sys/secpolicy.h>
55 #include <mandatory.h>
56 #include <fcntl.h>
57 #endif
58 #include <stdlib.h>
59 #include <time.h>
60 #include <sys/ioctl.h>
61 #include <unistd.h>
62 #include <string.h>
63 #if 0
64 #include <strings.h>
65 #endif
66 #include <sys/wait.h>
67 #include <grp.h>
68
69 /*
70  * Local include file for bls specific definitions.
71  * Also defines some of the structures from dm.h for bls usage.
72  */
73 #include "bls.h"
74
75 /* drop those privs from the base set which are not needed by xdm */
76 void
77 drop_privs(void)
78 {
79         priv_t privs[SEC_SPRIVVEC_SIZE];
80
81         getpriv(SEC_BASE_PRIV, privs);
82         RMBIT(privs, SEC_ALLOWNETACCESS);
83         RMBIT(privs, SEC_NETPRIVSESSION);
84         RMBIT(privs, SEC_NETNOAUTH);
85         RMBIT(privs, SEC_MULTILEVELDIR);
86         setpriv(SEC_BASE_PRIV, privs);
87         return;
88 }
89
90 /* stuff to do at the start */
91 void
92 init_security(void)
93 {
94         /* set default file creation mode to be restrictive */
95         umask(~SEC_DEFAULT_MODE);
96         drop_privs();
97 }
98
99 /* check that the requested security level is valid for this user,
100  * return 1 = success, return 0 is fail(fatal)
101  */
102 int
103 verify_user_seclevel( struct verify_info verify, char *desired_label)
104 {
105         int uid;
106         mand_ir_t *dl_ir, *clearance_ir;
107         struct pr_passwd *prpwd;
108         struct passwd *pwd;
109
110         prpwd = verify->prpwd;
111         pwd = verify->pwd;
112         uid = verify->uid;
113
114         /* check that desired_label falls within user's range */
115         dl_ir = mand_er_to_ir(desired_label);
116         if (dl_ir ==NULL) {
117             audit_login(prpwd, pwd, verify->terminal,
118                 "Unknown clearance level", ES_LOGIN_FAILED);
119             Debug("unable to translate clearance\n");
120             return 0;
121         }
122         /* get user clearance from prpwd database */
123         if (prpwd->uflg.fg_clearance)   
124                 clearance_ir = &prpwd->ufld.fd_clearance;
125         else if (prpwd->sflg.fg_clearance) 
126                 clearance_ir = &prpwd->sfld.fd_clearance; 
127         else 
128                 clearance_ir = mand_syslo;
129
130         /* make sure clearance dominates or equals desired_label */
131         switch(mand_ir_relationship(/* subject */ dl_ir, 
132                                 /* object */ clearance_ir)) {
133             case MAND_ODOM:
134             case MAND_EQUAL:
135                 /* Within range */
136                 break;
137             default:
138                 audit_login(prpwd, pwd, verify->terminal,
139                     "Security label out of range", ES_LOGIN_FAILED);
140                 Debug("Invalid clearance for this user\n");
141                 mand_free_ir(dl_ir);
142                 return 0;
143         }
144         verify->clearance_ir = clearance_ir;
145         verify->sec_label_ir = dl_ir;
146
147         return 1;
148 }
149
150 /* check the proper structures to determine if the user has a password.
151  * If the nullpw field is set, the user does not need one, and this
152  * overrides the rest of the checking.
153  * return 1 means that a password exists (or is not needed)
154  */
155 int 
156 password_exists(struct verify_info *verify)
157 {
158     struct pr_passwd *prpwd;
159     BOOL nocheck;
160
161     Debug("password_exists()\n");
162     prpwd = verify->prpwd;
163     if (prpwd->uflg.fg_nullpw)
164         nocheck=prpwd->ufld.fd_nullpw;
165     else if (prpwd->sflg.fg_nullpw)
166         nocheck=prpwd->sfld.fd_nullpw;
167     else
168         nocheck=FALSE;
169
170     if (!nocheck) {     /* user needs password */
171         Debug("password required for user\n");
172         if (!prpwd->uflg.fg_encrypt ||
173             prpwd->ufld.fd_encrypt[0] == '\0' ) {
174                 return 0;
175         }
176     }
177     return 1;
178 }
179
180
181 /* check that the requested security level can be used on this X terminal,
182  * and that it is not locked.
183  * Currently there is no support for locking xterms like there is for 
184  * /dev/tty* terminals.
185  */
186 int
187 verify_sec_xterm(struct verify_info *verify, char *desired_label)
188 {
189         return 1;
190 }
191
192
193 /* set clearance and label for the user.  Audit all failures.
194  * return 0=fail, 1=pass
195  */
196 int
197 set_sec_label(struct verify_info *verify)
198 {
199         struct pr_passwd *prpwd;
200         struct passwd *pwd;
201         /* set clearance */
202         prpwd = verify->prpwd;
203         pwd = verify->pwd;
204
205         if (setclrnce(verify->sec_label_ir)==-1) {
206           switch(errno) {
207             case EPERM:
208                 audit_login(prpwd, pwd, verify->terminal,
209                     "Insufficient privs to set clearance", ES_LOGIN_FAILED);
210                 Debug ("login failed: EPERM on setclrnce()\n");
211                 break;
212             case EFAULT:
213                 /* audit:login failed: xdm memory fault */
214             default:
215                 audit_login(prpwd, pwd, verify->terminal,
216                     "Unable to set clearance", ES_LOGIN_FAILED);
217                 Debug ("setclrnce failed: error: %d\n", errno);
218                 break;
219           }
220           return 0;
221         }
222         /* set label */
223         if (setslabel(verify->sec_label_ir)==-1) {
224           switch(errno) {
225             case EPERM:
226                 audit_login(prpwd, pwd, verify->terminal,
227                     "Insufficient privs to set sec label", ES_LOGIN_FAILED);
228                 Debug ("login failed: insufficient priv. to setslabel()\n");
229                 break;
230             case EFAULT:
231                 /* audit:login failed: xdm memory fault */
232             default:
233                 audit_login(prpwd, pwd, verify->terminal,
234                     "Unable to set sec label", ES_LOGIN_FAILED);
235                 Debug ("setslabel() failed: error: %d\n", errno);
236                 break;
237           }
238           return 0;
239         }
240         return 1;
241 }
242
243 /* set the effective, base, and maximum priv vectors for the 
244  * new process, based on values from the pr_passwd entry.
245  * Inability to find either user priv's or default priv's
246  * results in failure.  One or the other must be there.
247  * Function returns 1 for success, 0 for failure.
248  * A failure of this function should be considered fatal.
249  */
250 int
251 set_sec_privs(struct verify_info *verify)
252 {
253
254         priv_t *maxprivs, *bprivs;
255         priv_t curr_bprivs[SEC_SPRIVVEC_SIZE];
256         priv_t curr_sprivs[SEC_SPRIVVEC_SIZE];
257
258         struct pr_passwd *prpwd;
259         struct passwd *pwd;
260         int bit;
261
262         prpwd = verify->prpwd;
263         pwd = verify->pwd;
264
265         /* kernal authorizations */
266         if (prpwd->uflg.fg_sprivs) {
267             maxprivs = &prpwd->ufld.fd_sprivs[0];
268         }else if(prpwd->sflg.fg_sprivs) { 
269             maxprivs = &prpwd->sfld.fd_sprivs[0];
270             Debug("Using default kernel priv's\n");
271         }else {
272             audit_login(prpwd, pwd, verify->terminal, 
273                 "Unable to find kernal priv set for user",
274                 ES_LOGIN_FAILED);
275             Debug("Can't find max. priv set for user-quitting\n");
276             return 0;
277         }
278
279         /* base priv's and initial effective priv's */
280         if (verify->prpwd->uflg.fg_bprivs) {
281             bprivs = &verify->prpwd->ufld.fd_bprivs[0];
282         }else if (verify->prpwd->sflg.fg_bprivs) {  /* use system defaults */
283             bprivs = &verify->prpwd->sfld.fd_bprivs[0];
284             Debug("Using default base priv's\n");
285         }else{
286             audit_login(prpwd, pwd, verify->terminal, 
287                 "Unable to find base priv set for user",
288                 ES_LOGIN_FAILED);
289             Debug("Can't find base priv set for user-quitting\n");
290             return 0;
291         }
292
293         getpriv(SEC_MAXIMUM_PRIV, curr_sprivs);
294         getpriv(SEC_BASE_PRIV, curr_bprivs);
295
296         /* remove those privs which the current process does not have,
297          * to avoid any error in the following system calls 
298          */
299         for (bit=0; bit<=SEC_MAX_SPRIV; bit++) {
300                 if (!ISBITSET(curr_sprivs, bit)) 
301                         RMBIT(maxprivs, bit);
302                 if (!ISBITSET(curr_bprivs, bit))
303                         RMBIT(bprivs, bit);
304         }
305         
306         /* login removes those bits from maxprivs which the current process
307          * does not have. - This program assumes the system config
308          * utilities will enforce the rules for setpriv(3).  Any failure
309          * of setpriv will indicate a corrupt database.
310          */
311
312         if (setpriv(SEC_MAXIMUM_PRIV, maxprivs)==-1) {
313           switch(errno) {
314             case EPERM:
315                 Debug("setpriv (max) failed: EPERM\n");
316                 break;
317             case EINVAL:
318                 Debug("setpriv (max) failed: EINVAL\n");
319                 break;
320             case EFAULT:
321                 Debug("setpriv (max) failed: EFAULT\n");
322                 break;
323             default:
324                 Debug("setpriv (max) failed for unknown error: %d\n",errno);
325                 break;
326           }
327           audit_login(prpwd, pwd, verify->terminal,
328                 "Unable to set Kernal privs", ES_LOGIN_FAILED);
329           Debug("Unable to set Kernal privs (error %d): aborting\n",errno);
330           return 0;
331         }
332
333         if (setpriv(SEC_BASE_PRIV, bprivs)==-1) {
334           switch(errno) {
335             case EPERM:
336                 Debug("setpriv (base) failed: EPERM\n");
337                 break;
338             case EINVAL:
339                 Debug("setpriv (base) failed: EINVAL\n");
340                 break;
341             case EFAULT:
342                 Debug("setpriv (base) failed: EFAULT\n");
343                 break;
344             default:
345                 Debug("setpriv (base) failed for unknown error: %d\n",errno);
346                 break;
347           }
348           audit_login(prpwd, pwd, verify->terminal,
349                 "Unable to set base privs", ES_LOGIN_FAILED);
350           return 0;
351         }
352
353         if (setpriv(SEC_EFFECTIVE_PRIV, bprivs)==-1) {
354           switch(errno) {
355             case EPERM:
356                 Debug("setpriv (effective) failed: EPERM\n");
357                 break;
358             case EINVAL:
359                 Debug("setpriv (effective) failed: EINVAL\n");
360                 break;
361             case EFAULT:
362                 Debug("setpriv (effective) failed: EFAULT\n");
363                 break;
364             default:
365                 Debug("setpriv (effective) failed for unknown error: %d\n",
366                         errno);
367                 break;
368           }
369           audit_login(prpwd, pwd, verify->terminal,
370                 "Unable to set effective privs", ES_LOGIN_FAILED);
371           Debug("Unable to set effective privs (error %d): aborting\n",errno);
372           return 0;
373         }
374         return 1;
375
376 }
377
378
379 /* change the current process over to be owned by the user verify->uid.
380  * Also properly set the privs, sec label, etc.
381  * Also audits failures.
382  * return=1 for success, 0 for fail.  A failure should be considered fatal.
383  */
384 int 
385 change_to_user(struct verify_info *verify)
386 {
387         struct pr_passwd *prpwd;
388         struct passwd *pwd;
389         int new_nice;
390
391         prpwd = verify->prpwd;
392         pwd = verify->pwd;
393
394         Debug("change_to_user()\n");
395         /* 1. set the login user id - settable only once */
396         if (setluid(verify->uid)==-1) {
397           switch(errno) {
398             case EPERM:
399                 Debug("Unable to set luid - EPERM\n");
400                 audit_login(prpwd, pwd, verify->terminal, 
401                     "Unable to set luid - insufficient privs", 
402                     ES_LOGIN_FAILED);
403                 break;
404             case EINVAL:
405                 Debug("Unable to set luid - suspicious of pwd db.\n");
406                 audit_login(prpwd, pwd, verify->terminal, 
407                     "Unable to set luid - out of range", ES_LOGIN_FAILED);
408                 break;
409             default:
410                 Debug("Can't set luid-Unknown error %d\n",errno);
411                 audit_login(prpwd, pwd, verify->terminal, 
412                     "Unable to set luid-unknown error", ES_LOGIN_FAILED);
413                 break;
414           }
415           return 0;
416         }
417
418         /*
419          * Set the 'nice' priority if necessary.  Since the return value
420          * of nice(2) can normally be -1 from the documentation, and
421          * -1 is the error condition, we key off of errno, not the
422          * return value to find if the change were successful.
423          * Note we must do this before the setuid(2) below.
424          */
425         errno = 0;
426         prpwd = verify->prpwd;
427         if (prpwd->uflg.fg_nice)
428                 new_nice = prpwd->ufld.fd_nice;
429         else if (prpwd->sflg.fg_nice)
430                 new_nice = prpwd->sfld.fd_nice;
431
432         if (prpwd->uflg.fg_nice || prpwd->sflg.fg_nice)  {
433                 (void) nice(new_nice);
434                 if (errno != 0)  {
435                         audit_login(prpwd, verify->pwd, NULL,
436                              "bad 'nice' setting", ES_LOGIN_FAILED);
437                         Debug("Bad priority setting.\n");
438                         return 0;
439                 }
440         }
441
442
443         /*  2. set the group(s) id and
444          *  3. set the regular user id */
445
446 #ifdef NGROUPS
447
448         /* setgroups (verify->ngroups, verify->groups);
449         */
450         if(setgid (verify->groups[0])) {
451           switch(errno) {
452                 case EPERM: 
453                         Debug("setgid EPERM\n");
454                         break;
455                 case EINVAL: 
456                         Debug("setgid EINVAL\n");
457                         break;
458                 default: 
459                         Debug("setgid unknown error: %d\n",errno);
460                         break;
461           }
462           return 0;
463         }
464         initgroups(verify->user_name, verify->groups[0]);
465 #else
466         if(setgid (verify->gid)) {
467           switch(errno) {
468                 case EPERM: Debug("setgid EPERM\n");break;
469                 case EINVAL: Debug("setgid EINVAL\n");break;
470                 default: Debug("setgid unknown error\n");break;
471           }
472           return 0;
473         }
474 #endif
475
476         if(setuid (verify->uid)) {
477           switch(errno) {
478                 case EPERM: Debug("setgid EPERM\n");break;
479                 case EINVAL: Debug("setgid EINVAL\n");break;
480                 default: Debug("setgid unknown error\n");break;
481           }
482           return 0;
483         }
484
485         /* 4. set security clearance and label for the new process */
486         if (!set_sec_label(verify)) 
487                 return 0;
488
489         /* 5. set audit parameters */
490         audit_adjust_mask(prpwd);
491
492         /* 6. set privlege levels - maximum, base, and effective */
493         if (!set_sec_privs(verify))
494                 return 0;
495
496         return 1;
497 }
498
499
500 /* 
501  * Try to read back everything, and print it.  If a fatal error occurs,
502  * return code is 0.  1=success.
503  */
504 int
505 dump_sec_debug_info(struct verify_info *verify)
506 {
507         mand_ir_t *level_ir;
508         priv_t privs[SEC_SPRIVVEC_SIZE];
509
510         Debug ("luid: %d, real uid: %d, effective uid:%d,\n",
511                 getluid(),getuid(),geteuid());
512         Debug ("real gid:%d, effective gid: %d\n", getgid(),getegid());
513         level_ir = mand_alloc_ir();
514         if (getclrnce(level_ir)==-1) {
515           switch(errno) {
516                 case EFAULT: Debug("getclrnce EFAULT\n");break;
517                 case EINVAL: Debug("getclrnce EINVAL\n");break;
518                 default: Debug("getclrnce unknown error:%d\n",errno);break;
519           }
520           return 0;
521         }else Debug ("Clearance: %s\n", mand_ir_to_er(level_ir) );
522         if (getslabel(level_ir)==-1) {
523           switch(errno) {
524                 case EFAULT: Debug("getslabel EFAULT\n");break;
525                 case EINVAL: Debug("getslabel EINVAL\n");break;
526                 default: Debug("getslabel unknown error:%d\n",errno);break;
527           }
528           return 0;
529         }else Debug ("Level: %s\n", mand_ir_to_er(level_ir));
530         mand_free_ir(level_ir);
531         if(getpriv(SEC_MAXIMUM_PRIV, privs)==-1) {
532           switch(errno) {
533                 case EFAULT: Debug("getpriv max EFAULT\n");break;
534                 case EINVAL: Debug("getpriv max EINVAL\n");break;
535                 default: Debug("getpriv max unknown error:%d\n",errno);
536                         break;
537           }
538           return 0;
539         }else Debug ("max priv: %x.%x\n", privs[0],privs[1]);
540         if(getpriv(SEC_EFFECTIVE_PRIV, privs)==-1) {
541           switch(errno) {
542                 case EFAULT: Debug("getpriv eff EFAULT\n");break;
543                 case EINVAL: Debug("getpriv eff EINVAL\n");break;
544                 default: Debug("getpriv eff unknown error:%d\n",errno);
545                         break;
546           }
547           return 0;
548         }else Debug ("eff priv: %x.%x\n", privs[0],privs[1]);
549         if(getpriv(SEC_BASE_PRIV, privs)==-1) {
550           switch(errno) {
551                 case EFAULT: Debug("getpriv base EFAULT\n");break;
552                 case EINVAL: Debug("getpriv base EINVAL\n");break;
553                 default: Debug("getpriv base unknown error:%d\n",errno);
554                         break;
555           }
556           return 0;
557         }else Debug ("base priv: %x.%x\n", privs[0],privs[1]);
558         return 1;
559 }
560
561 /*
562  * writeLoginInfo
563  *      Input:  file name string (ex. $HOME/.dtlogininfo)
564  *              verify structure with password stuff
565  *  Write login information to a file to be displayed later, after a
566  *  successful login.
567  *
568  *  Xsession will need to be modified something like this...
569  *     DTHELLO="$DTDIR/bin/dthello -f /etc/copyright -f $HOME/.dtlogininfo"
570  */
571    
572 int
573 writeLoginInfo( char *filename, struct verify_info *verify)
574 {
575         char *s1="Last   successful login: %s";
576         char *s2="Last unsuccessful login: %s";
577         char *s3;
578         char s[80];
579         char term[15];
580         char *label;
581         char *message="Sensitivity level for process: ";
582         int i;
583         int nl;
584         time_t slogin, ulogin;
585         char *slabel;
586         char *uterminal, *sterminal;
587         
588         FILE *fp;
589         
590         Debug("Writing login info\n");
591         if ((fp = fopen (filename, "w")) == 0 )
592                 return 0;
593
594         if (verify->prpwd->uflg.fg_slogin)
595                 slogin=verify->prpwd->ufld.fd_slogin;
596         else
597                 slogin=(time_t)0;
598
599         if (verify->prpwd->uflg.fg_ulogin)
600                 ulogin=verify->prpwd->ufld.fd_ulogin;
601         else
602                 ulogin=(time_t)0;
603
604         if (verify->prpwd->uflg.fg_suctty)
605                 sterminal=verify->prpwd->ufld.fd_suctty;
606         else
607                 sterminal="UNKNOWN";
608
609         if (verify->prpwd->uflg.fg_unsuctty)
610                 uterminal=verify->prpwd->ufld.fd_unsuctty;
611         else
612                 uterminal="UNKNOWN";
613
614         slabel = mand_ir_to_er(verify->sec_label_ir);
615
616         fprintf(fp,"-----------------------------------\n");
617         fprintf(fp,"\nPrevious login information:\n\n");
618
619         /* tricky formatting */
620         if (slogin != 0) {
621                 sprintf(s, s1, ctime(&slogin));
622                 nl=strlen(s)-1;
623                 s[nl]='\0';  /* remove new-line */
624         }else{
625                 sprintf(s,s1,"NEVER");
626         }
627         strcat(s, " from ");
628         strncpy(term, sterminal, 14);
629         term[14]='\0';
630         strcat(s, term);
631         fprintf(fp,"%s\n",s);
632
633         if (ulogin != 0) {
634                 sprintf(s, s2, ctime(&ulogin));
635                 nl=strlen(s)-1;
636                 s[nl]='\0';  /* remove new-line */
637         }else{
638                 sprintf(s,s2,"NEVER");
639         }
640         strcat(s, " from ");
641         strncpy(term, uterminal, 14);
642         term[14]='\0';
643         strcat(s, term);
644         fprintf(fp,"%s\n",s);
645
646         label = (char*)malloc(strlen(message)+strlen(slabel)+1);
647         sprintf(label, "%s%s", message, slabel);
648         if (strlen (label) > 77) {
649             for(i=75; label[i]!=',' && i>0; i--);
650             if (i==0) for(i=75; label[i]!=' ' && i>0; i--);
651             if (i==0) i=75;
652             strncpy(s, label, i+1);
653             s[i+1]='\0';
654             fprintf(fp,"%s\n",s);
655             strncpy(s, &label[i+1], 75);
656             s[75]='\0';
657             fprintf(fp,"%s\n",s);
658         }else{
659             fprintf(fp,"%s\n",label);
660         }
661         
662         fclose(fp);
663         return 1;
664 }
665
666 #endif  /* BLS */