remove gcc34
[dragonfly.git] / crypto / heimdal-0.6.3 / appl / dceutils / k5dcecon.c
1 /*
2  * (c) Copyright 1995 HEWLETT-PACKARD COMPANY
3  * 
4  * To anyone who acknowledges that this file is provided 
5  * "AS IS" without any express or implied warranty:
6  * permission to use, copy, modify, and distribute this 
7  * file for any purpose is hereby granted without fee, 
8  * provided that the above copyright notice and this 
9  * notice appears in all copies, and that the name of 
10  * Hewlett-Packard Company not be used in advertising or 
11  * publicity pertaining to distribution of the software 
12  * without specific, written prior permission.  Hewlett-
13  * Packard Company makes no representations about the 
14  * suitability of this software for any purpose.
15  *
16  */
17 /*
18  * k5dcecon - Program to convert a K5 TGT to a DCE context,
19  * for use with DFS and its PAG.
20  * 
21  * The program is designed to be called as a sub process, 
22  * and return via stdout the name of the cache which implies 
23  * the PAG which should be used. This program itself does not 
24  * use the cache or PAG itself, so the PAG in the kernel for 
25  * this program may not be set. 
26  * 
27  * The calling program can then use the name of the cache
28  * to set the KRB5CCNAME and PAG for its self and its children. 
29  *
30  * If no ticket was passed, an attemplt to join an existing
31  * PAG will be made. 
32  * 
33  * If a forwarded K5 TGT is passed in, either a new DCE 
34  * context will be created, or an existing one will be updated.
35  * If the same ticket was already used to create an existing
36  * context, it will be joined instead. 
37  * 
38  * Parts of this program are based on k5dceauth,c which was
39  * given to me by HP and by the k5dcelogin.c which I developed. 
40  * A slightly different version of k5dcelogin.c, was added to
41  * DCE 1.2.2
42  * 
43  * D. E. Engert 6/17/97 ANL
44  */
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <fcntl.h>
49 #include <sys/types.h>
50 #include <dirent.h>
51 #include <sys/stat.h>
52 #include <locale.h>
53 #include <pwd.h>
54 #include <string.h>
55 #include <time.h>
56
57 #include <errno.h>
58 #include "k5dce.h"
59
60 #include <dce/sec_login.h>
61 #include <dce/dce_error.h>
62 #include <dce/passwd.h>
63
64 /* #define DEBUG */
65 #if defined(DEBUG)
66 #define DEEDEBUG(A) fprintf(stderr,A); fflush(stderr)
67 #define DEEDEBUG2(A,B) fprintf(stderr,A,B); fflush(stderr)
68 #else
69 #define DEEDEBUG(A)
70 #define DEEDEBUG2(A,B)
71 #endif
72
73 #ifdef __hpux
74 #define seteuid(A)              setresuid(-1,A,-1);
75 #endif
76
77
78 int k5dcecreate (uid_t, char *, char*, krb5_creds **);
79 int k5dcecon (uid_t, char *, char *);
80 int k5dcegettgt (krb5_ccache *, char *, char *, krb5_creds **);
81 int k5dcematch (uid_t, char *, char *, off_t *, krb5_creds **);
82 int k5dcesession (uid_t, char *, krb5_creds **, int *,krb5_flags);
83
84
85 char *progname = "k5dcecon";
86 static time_t now;
87
88 #ifdef notdef
89 #ifdef _AIX
90 /*---------------------------------------------*/
91  /* AIX with DCE 1.1 does not have the com_err in the libdce.a
92   * do a half hearted job of substituting for it. 
93   */ 
94 void com_err(char *p1, int code, ...) 
95 {
96     int lst;
97     dce_error_string_t  err_string;
98     dce_error_inq_text(code, err_string, &lst);
99     fprintf(stderr,"Error %d in %s: %s\n", code, p1, err_string );
100 }
101
102 /*---------------------------------------------*/
103 void krb5_init_ets()
104 {
105
106 }
107 #endif
108 #endif
109
110
111 /*------------------------------------------------*/
112 /* find a cache to use  for our new pag           */
113 /* Since there is no simple way to determine which
114  * caches are associated with a pag, we will have
115  * do look around and see what makes most sense on 
116  * different systems. 
117  * on a Solaris system, and in the DCE source, 
118  * the pags always start with a 41. 
119  * this is not true on the IBM, where there does not
120  * appear to be any pattern. 
121  * 
122  * But since we are always certifing our creds when
123  * they are received, we can us that fact, and look
124  * at the first word of the associated data file
125  * to see that it has a "5". If not don't use. 
126  */
127
128 int k5dcesession(luid, pname, tgt, ppag, tflags)
129   uid_t luid;
130   char *pname;
131   krb5_creds **tgt;
132   int *ppag;
133   krb5_flags tflags;
134 {
135   DIR *dirp;
136   struct dirent *direntp;
137   off_t size;
138   krb5_timestamp endtime;
139   int better = 0;
140   krb5_creds *xtgt;
141
142   char prev_name[17] = "";  
143   krb5_timestamp prev_endtime;
144   off_t prev_size;
145   u_long prev_pag = 0;
146
147   char ccname[64] = "FILE:/opt/dcelocal/var/security/creds/";
148  
149   error_status_t st;
150   sec_login_handle_t lcontext = 0;
151   dce_error_string_t  err_string;
152   int lst;
153
154   DEEDEBUG2("k5dcesession looking for flags %8.8x\n",tflags);
155
156   dirp = opendir("/opt/dcelocal/var/security/creds/");
157   if (dirp == NULL) {
158         return 1;
159   }
160
161   while ( (direntp = readdir( dirp )) != NULL ) {
162
163 /*  
164  * (but root has the ffffffff which we are not interested in)
165  */
166     if (!strncmp(direntp->d_name,"dcecred_",8)
167          && (strlen(direntp->d_name) == 16)) {
168
169       /* looks like a cache name, lets do the stat, etc */
170
171       strcpy(ccname+38,direntp->d_name);
172       if (!k5dcematch(luid, pname, ccname, &size, &xtgt))  {
173
174         /* its one of our caches, see if it is better  
175          * i.e. the endtime is farther, and if the endtimes
176          * are the same, take the larger, as he who has the 
177          * most tickets wins.
178          * it must also had the same set of flags at least
179          * i.e. if the forwarded TGT is forwardable, this one must 
180          * be as well.   
181          */
182
183         DEEDEBUG2("Cache:%s",direntp->d_name);
184         DEEDEBUG2(" size:%d",size);
185                 DEEDEBUG2(" flags:%8.8x",xtgt->ticket_flags);
186                 DEEDEBUG2(" %s",ctime((time_t *)&xtgt->times.endtime));
187  
188         if ((xtgt->ticket_flags & tflags) == tflags ) {
189           if (prev_name[0]) {
190             if (xtgt->times.endtime > prev_endtime) {
191               better = 1;
192             } else if ((xtgt->times.endtime = prev_endtime) 
193                   && (size > prev_size)){
194               better = 1;
195                 }
196           } else {   /* the first */
197             if (xtgt->times.endtime >= now) {
198             better = 1;
199                 }
200           }
201           if (better) {
202             strcpy(prev_name, direntp->d_name);
203                     prev_endtime = xtgt->times.endtime;
204             prev_size = size;
205             sscanf(prev_name+8,"%8X",&prev_pag);
206                         *tgt = xtgt;
207             better = 0;
208           }
209         }
210       } 
211     }
212   }
213   (void)closedir( dirp );
214
215   if (!prev_name[0])  
216          return 1; /* failed to find one */
217
218    DEEDEBUG2("Best: %s\n",prev_name);
219
220    if (ppag)
221       *ppag = prev_pag;
222
223    strcpy(ccname+38,prev_name);
224    setenv("KRB5CCNAME",ccname,1);
225  
226    return(0);
227 }
228
229
230 /*----------------------------------------------*/
231 /* see if this cache is for this this principal */
232
233 int k5dcematch(luid, pname, ccname, sizep, tgt) 
234   uid_t luid;
235   char *pname;
236   char *ccname;
237   off_t *sizep;  /* size of the file */
238   krb5_creds **tgt;
239 {
240
241   krb5_ccache cache;
242   struct stat stbuf;
243   char ccdata[256];
244   int fd;
245   int status;
246
247   /* DEEDEBUG2("k5dcematch called: cache=%s\n",ccname+38); */
248
249   if (!strncmp(ccname,"FILE:",5)) {
250
251     strcpy(ccdata,ccname+5);
252     strcat(ccdata,".data");
253
254     /* DEEDEBUG2("Checking the .data file for %s\n",ccdata); */
255
256     if (stat(ccdata, &stbuf))
257       return(1);
258  
259     if (stbuf.st_uid != luid)
260       return(1);
261
262     if ((fd = open(ccdata,O_RDONLY)) == -1)
263       return(1);
264     
265     if ((read(fd,&status,4)) != 4) {
266       close(fd);
267       return(1);
268     }
269     
270     /* DEEDEBUG2(".data file status = %d\n", status); */
271
272     if (status != 5)
273      return(1);
274
275     if (stat(ccname+5, &stbuf))
276       return(1);
277
278     if (stbuf.st_uid != luid)
279       return(1);
280
281     *sizep = stbuf.st_size;
282   }
283
284   return(k5dcegettgt(&cache, ccname, pname, tgt));
285 }
286
287
288 /*----------------------------------------*/
289 /* k5dcegettgt - get the tgt from a cache */
290
291 int k5dcegettgt(pcache, ccname, pname, tgt)
292   krb5_ccache *pcache;
293   char *ccname;
294   char *pname;
295   krb5_creds **tgt;
296
297 {
298   krb5_ccache cache;
299   krb5_cc_cursor cur;
300   krb5_creds creds;
301   int code;
302   int found = 1;
303   krb5_principal princ;
304   char *kusername;
305   krb5_flags flags;
306   char *sname, *realm, *tgtname = NULL;
307
308   /* Since DCE does not expose much of the Kerberos interface,
309    * we will have to use what we can. This means setting the 
310    * KRB5CCNAME for each file we want to test
311    * We will also not worry about freeing extra cache structures
312    * as this this routine is also not exposed, and this should not 
313    * effect this module. 
314    * We should also free the creds contents, but that is not exposed
315    * either. 
316    */
317
318   setenv("KRB5CCNAME",ccname,1);
319   cache = NULL;
320   *tgt = NULL;
321
322   if (code = krb5_cc_default(pcache)) {
323      com_err(progname, code, "while getting ccache");
324      goto return2;
325   }
326
327   DEEDEBUG("Got cache\n");
328   flags = 0;
329   if (code = krb5_cc_set_flags(*pcache, flags)) {
330     com_err(progname, code,"While setting flags"); 
331     goto return2;
332   }
333   DEEDEBUG("Set flags\n");
334   if (code = krb5_cc_get_principal(*pcache, &princ)) {
335         com_err(progname, code, "While getting princ");
336     goto return1;
337   }
338   DEEDEBUG("Got principal\n");
339   if (code = krb5_unparse_name(princ, &kusername)) {
340     com_err(progname, code, "While unparsing principal");
341     goto return1;
342   }
343
344   DEEDEBUG2("Unparsed to \"%s\"\n", kusername);
345   DEEDEBUG2("pname is \"%s\"\n", pname);
346   if (strcmp(kusername, pname)) {
347    DEEDEBUG("Principals not equal\n");
348    goto return1;
349   }
350   DEEDEBUG("Principals equal\n");
351
352   realm = strchr(pname,'@');
353   realm++;
354
355   if ((tgtname = malloc(9 + 2 * strlen(realm))) == 0) {
356        fprintf(stderr,"Malloc failed for tgtname\n");
357        goto return1;
358   }
359
360   strcpy(tgtname,"krbtgt/");
361   strcat(tgtname,realm);
362   strcat(tgtname,"@");
363   strcat(tgtname,realm);
364  
365   DEEDEBUG2("Getting tgt %s\n", tgtname);
366   if (code = krb5_cc_start_seq_get(*pcache, &cur)) {
367     com_err(progname, code, "while starting to retrieve tickets");
368     goto return1;
369   }
370
371   while (!(code = krb5_cc_next_cred(*pcache, &cur, &creds))) {
372     krb5_creds *cred = &creds;
373
374     if (code = krb5_unparse_name(cred->server, &sname)) {
375       com_err(progname, code, "while unparsing server name");
376       continue;
377     }
378
379     if (strncmp(sname, tgtname, strlen(tgtname)) == 0) {
380       DEEDEBUG("FOUND\n");
381       if (code = krb5_copy_creds(&creds, tgt)) {
382         com_err(progname, code, "while copying TGT");
383         goto return1;
384       }
385       found = 0;
386       break;
387     } 
388     /* we should do a krb5_free_cred_contents(creds); */
389   }
390
391   if (code = krb5_cc_end_seq_get(*pcache, &cur)) {
392     com_err(progname, code, "while finishing retrieval"); 
393     goto return2;
394   }
395
396 return1:
397   flags = KRB5_TC_OPENCLOSE; 
398   krb5_cc_set_flags(*pcache, flags); /* force a close */
399    
400 return2:
401   if (tgtname)
402     free(tgtname);
403
404   return(found);
405 }
406
407
408 /*------------------------------------------*/
409 /* Convert a forwarded TGT to a DCE context */
410 int k5dcecon(luid, luser, pname)
411   uid_t luid;
412   char *luser;
413   char *pname;
414 {
415
416   krb5_creds *ftgt = NULL;
417   krb5_creds *tgt = NULL;
418   unsigned32 dfspag;
419   boolean32 reset_passwd = 0;
420   int lst;
421   dce_error_string_t  err_string;
422   char *shell_prog;
423   krb5_ccache fcache;
424   char *ccname;
425   char *kusername;
426   char *urealm;
427   char *cp;
428   int pag;
429   int code;
430   krb5_timestamp endtime;
431
432
433   /* If there is no cache to be converted, we should not be here */
434
435   if ((ccname = getenv("KRB5CCNAME")) == NULL) {
436     DEEDEBUG("No KRB5CCNAME\n");
437     return(1);
438   }
439
440   if (k5dcegettgt(&fcache, ccname, pname, &ftgt)) {
441     fprintf(stderr, "%s: Did not find TGT\n", progname);
442     return(1);
443   }
444
445  
446   DEEDEBUG2("flags=%x\n",ftgt->ticket_flags);
447   if (!(ftgt->ticket_flags & TKT_FLG_FORWARDABLE)){
448     fprintf(stderr,"Ticket not forwardable\n");
449     return(0); /* but OK to continue */
450   }
451
452   setenv("KRB5CCNAME","",1);
453     
454 #define TKT_ACCEPTABLE (TKT_FLG_FORWARDABLE | TKT_FLG_PROXIABLE \
455          | TKT_FLG_MAY_POSTDATE | TKT_FLG_RENEWABLE | TKT_FLG_HW_AUTH \
456          | TKT_FLG_PRE_AUTH)
457
458   if (!k5dcesession(luid, pname, &tgt, &pag, 
459         (ftgt->ticket_flags & TKT_ACCEPTABLE))) {
460     if (ftgt->times.endtime > tgt->times.endtime) {
461       DEEDEBUG("Updating existing cache\n"); 
462       return(k5dceupdate(&ftgt, pag));
463     } else {
464       DEEDEBUG("Using existing cache\n");
465       return(0); /* use the original one */
466     }
467   } 
468     /* see if the tgts match up */
469
470   if ((code = k5dcecreate(luid, luser, pname, &ftgt))) {
471         return (code);
472   }
473
474   /*
475    * Destroy the Kerberos5 cred cache file.
476    * but dont care aout the return code. 
477    */
478
479   DEEDEBUG("Destroying the old cache\n");
480   if ((code = krb5_cc_destroy(fcache))) {
481     com_err(progname, code, "while destroying Kerberos5 ccache");
482   }
483   return (0);
484 }
485
486
487 /*--------------------------------------------------*/
488 /* k5dceupdate - update the cache with a new TGT    */
489 /* Assumed that the KRB5CCNAME has been set         */
490
491 int k5dceupdate(krbtgt, pag) 
492    krb5_creds **krbtgt;
493    int pag;
494 {
495  
496   krb5_ccache ccache;
497   int code;
498
499   if (code = krb5_cc_default(&ccache)) {
500     com_err(progname, code, "while opening cache for update");
501     return(2);
502    }
503
504   if (code = ccache->ops->init(ccache,(*krbtgt)->client)) {
505     com_err(progname, code, "while reinitilizing cache");
506     return(3);
507   } 
508
509     /* krb5_cc_store_cred */
510   if (code = ccache->ops->store(ccache, *krbtgt)) {
511     com_err(progname, code, "while updating cache");
512     return(2);
513   }
514
515   sec_login_pag_new_tgt(pag, (*krbtgt)->times.endtime);
516   return(0);
517 }
518 /*--------------------------------------------------*/
519 /* k5dcecreate - create a new DCE context           */
520
521 int k5dcecreate(luid, luser, pname, krbtgt)
522    uid_t luid;
523    char *luser;
524    char *pname;
525    krb5_creds **krbtgt;
526 {
527    
528     char *cp;
529     char *urealm;
530     char *username;
531     char *defrealm;
532     uid_t uid;
533
534     error_status_t st;
535     sec_login_handle_t lcontext = 0;
536     sec_login_auth_src_t auth_src = 0;
537     boolean32 reset_passwd = 0;
538     int lst;
539     dce_error_string_t  err_string;
540
541         setenv("KRB5CCNAME","",1); /* make sure it not misused */
542
543         uid = getuid();
544         DEEDEBUG2("uid=%d\n",uid);
545     
546         /* if run as root, change to user, so as to have the
547          * cache created for the local user even if cross-cell
548          * If run as a user, let standard file protection work.
549          */
550
551         if (uid == 0) {
552                 seteuid(luid);
553         }  
554
555         cp = strchr(pname,'@');
556         *cp = '\0';
557         urealm = ++cp;
558
559  DEEDEBUG2("basename=%s\n",cp);
560  DEEDEBUG2("realm=%s\n",urealm);
561
562     /* now build the username as a single string or a /.../cell/user
563      * if this is a cross cell
564      */
565
566         if ((username = malloc(7+strlen(pname)+strlen(urealm))) == 0) {
567          fprintf(stderr,"Malloc failed for username\n");
568          goto abort;
569     }
570     if (krb5_get_default_realm(&defrealm)) {
571         DEEDEBUG("krb5_get_default_realm failed\n");
572         goto abort;
573     }
574
575
576     if (!strcmp(urealm,defrealm)) {
577         strcpy(username,pname);
578     } else {
579         strcpy(username,"/.../");
580         strcat(username,urealm);
581         strcat(username,"/");
582         strcat(username,pname);
583     }
584
585     /*
586      * Setup a DCE login context
587      */
588
589     if (sec_login_setup_identity((unsigned_char_p_t)username, 
590                                  (sec_login_external_tgt|sec_login_proxy_cred),
591                                  &lcontext, &st)) {
592         /*
593          * Add our TGT.
594          */
595           DEEDEBUG("Adding our new TGT\n");
596           sec_login_krb5_add_cred(lcontext, *krbtgt, &st);
597           if (st) {
598             dce_error_inq_text(st, err_string, &lst);
599             fprintf(stderr,
600                                 "Error while adding credentials for %s because %s\n", 
601                                 username, err_string);
602             goto abort;
603           }     
604           DEEDEBUG("validating and certifying\n");
605           /*
606            * Now "validate" and certify the identity,
607            *  usually we would pass a password here, but...
608            * sec_login_valid_and_cert_ident
609            * sec_login_validate_identity
610            */
611
612           if (sec_login_validate_identity(lcontext, 0, &reset_passwd,
613                  &auth_src, &st)) {
614             DEEDEBUG2("validate_identity st=%d\n",st);
615             if (st) {
616                   dce_error_inq_text(st, err_string, &lst);
617                   fprintf(stderr, "Validation error for %s because %s\n",
618                                  username, err_string);
619                   goto abort;
620             }
621                 if (!sec_login_certify_identity(lcontext,&st)) {
622                         dce_error_inq_text(st, err_string, &lst);
623                         fprintf(stderr,
624                         "Credentials not certified because %s\n",err_string);
625                 }
626             if (reset_passwd) {
627                  fprintf(stderr,
628                 "Password must be changed for %s\n", username);
629             }
630             if (auth_src == sec_login_auth_src_local) {
631                 fprintf(stderr,
632                          "Credentials obtained from local registry for %s\n", 
633                          username);
634             }
635             if (auth_src == sec_login_auth_src_overridden) {
636                   fprintf(stderr, "Validated %s from local override entry, no network credentials obtained\n", username);
637                   goto abort; 
638
639             }
640             /*
641              * Actually create the cred files.
642              */
643                 DEEDEBUG("Ceating new cred files.\n");
644             sec_login_set_context(lcontext, &st);
645             if (st) {
646                   dce_error_inq_text(st, err_string, &lst);
647                   fprintf(stderr, 
648                 "Unable to set context for %s because %s\n",
649                     username, err_string);
650                   goto abort;
651             }
652
653         /*
654          * Now free up the local context and leave the 
655          * network context with its pag
656          */
657 #if 0
658         sec_login_release_context(&lcontext, &st);
659         if (st) {
660           dce_error_inq_text(st, err_string, &lst);
661           fprintf(stderr,
662                "Unable to release context for %s because %s\n",
663             username, err_string);
664           goto abort;
665         }
666 #endif
667         }
668         else {
669           DEEDEBUG2("validate failed %d\n",st);
670           dce_error_inq_text(st, err_string, &lst);
671           fprintf(stderr,
672              "Unable to validate %s because %s\n", username, 
673                         err_string);
674           goto abort;
675         }
676   }
677   else {
678         dce_error_inq_text(st, err_string, &lst);
679         fprintf(stderr, 
680           "Unable to setup login entry for %s because %s\n",
681        username, err_string);
682           goto abort;
683   }
684
685  done:
686     /* if we were root, get back to root */
687
688     DEEDEBUG2("sec_login_inq_pag %8.8x\n",
689              sec_login_inq_pag(lcontext, &st));
690
691     if (uid == 0) {
692       seteuid(0);
693     }  
694
695         DEEDEBUG("completed\n");
696         return(0);
697
698  abort:
699     if (uid == 0) {
700       seteuid(0);
701     }  
702
703     DEEDEBUG("Aborting\n");
704     return(2);
705 }
706
707
708
709 /*-------------------------------------------------*/
710 main(argc, argv)
711   int argc;
712   char *argv[];
713 {
714   int status;
715   extern int optind;
716   extern char *optarg;
717   int rv;
718
719   char *lusername = NULL;
720   char *pname = NULL;
721   int fflag = 0;
722   struct passwd *pw;
723   uid_t luid;
724   uid_t myuid;
725   char *ccname;
726   krb5_creds *tgt = NULL;
727
728 #ifdef DEBUG
729   close(2);
730   open("/tmp/k5dce.debug",O_WRONLY|O_CREAT|O_APPEND, 0600);
731 #endif
732
733   if (myuid = getuid()) {
734     DEEDEBUG2("UID = %d\n",myuid);
735     exit(33); /* must be root to run this, get out now */
736   }
737
738   while ((rv = getopt(argc,argv,"l:p:fs")) != -1) {
739     DEEDEBUG2("Arg = %c\n", rv);
740     switch(rv) {
741       case 'l':         /* user name */
742         lusername = optarg;
743         DEEDEBUG2("Optarg = %s\n", optarg);
744         break;
745       case 'p':         /* principal name */
746         pname = optarg; 
747         DEEDEBUG2("Optarg = %s\n", optarg);
748         break;
749       case 'f':         /* convert a forwarded TGT to a context */
750         fflag++;
751         break;
752       case 's':      /* old test parameter, ignore it */
753         break; 
754     }
755   }
756
757   setlocale(LC_ALL, "");
758   krb5_init_ets();
759   time(&now); /* set time to check expired tickets */
760
761   /* if lusername == NULL, Then user is passed as the USER= variable */ 
762
763   if (!lusername) {
764     lusername = getenv("USER");
765     if (!lusername) {
766       fprintf(stderr, "USER not in environment\n");
767       return(3);
768     }
769   }
770
771   if ((pw = getpwnam(lusername)) == NULL) {
772     fprintf(stderr, "Who are you?\n");
773     return(44);
774   }
775
776   luid = pw->pw_uid;
777
778   if (fflag) {  
779     status = k5dcecon(luid, lusername, pname); 
780   } else {
781     status = k5dcesession(luid, pname, &tgt, NULL, 0);
782   }
783  
784   if (!status) {
785     printf("%s",getenv("KRB5CCNAME")); /* return via stdout to caller */
786     DEEDEBUG2("KRB5CCNAME=%s\n",getenv("KRB5CCNAME"));
787   }
788
789   DEEDEBUG2("Returning status %d\n",status);
790   return (status);
791 }