2 * (c) Copyright 1995 HEWLETT-PACKARD COMPANY
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.
18 * k5dcecon - Program to convert a K5 TGT to a DCE context,
19 * for use with DFS and its PAG.
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.
27 * The calling program can then use the name of the cache
28 * to set the KRB5CCNAME and PAG for itself and its children.
30 * If no ticket was passed, an attemplt to join an existing
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.
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
43 * D. E. Engert 6/17/97 ANL
49 #include <sys/types.h>
60 #include <dce/sec_login.h>
61 #include <dce/dce_error.h>
62 #include <dce/passwd.h>
66 #define DEEDEBUG(A) fprintf(stderr,A); fflush(stderr)
67 #define DEEDEBUG2(A,B) fprintf(stderr,A,B); fflush(stderr)
70 #define DEEDEBUG2(A,B)
74 #define seteuid(A) setresuid(-1,A,-1)
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
);
85 char *progname
= "k5dcecon";
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.
94 void com_err(char *p1
, int code
, ...)
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
);
102 /*---------------------------------------------*/
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
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.
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.
128 int k5dcesession(luid
, pname
, tgt
, ppag
, tflags
)
136 struct dirent
*direntp
;
138 krb5_timestamp endtime
;
142 char prev_name
[17] = "";
143 krb5_timestamp prev_endtime
;
147 char ccname
[64] = "FILE:/opt/dcelocal/var/security/creds/";
150 sec_login_handle_t lcontext
= 0;
151 dce_error_string_t err_string
;
154 DEEDEBUG2("k5dcesession looking for flags %8.8x\n",tflags
);
156 dirp
= opendir("/opt/dcelocal/var/security/creds/");
161 while ( (direntp
= readdir( dirp
)) != NULL
) {
164 * (but root has the ffffffff which we are not interested in)
166 if (!strncmp(direntp
->d_name
,"dcecred_",8)
167 && (strlen(direntp
->d_name
) == 16)) {
169 /* looks like a cache name, lets do the stat, etc */
171 strcpy(ccname
+38,direntp
->d_name
);
172 if (!k5dcematch(luid
, pname
, ccname
, &size
, &xtgt
)) {
174 /* it's 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
178 * it must also had the same set of flags at least
179 * i.e. if the forwarded TGT is forwardable, this one must
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
));
188 if ((xtgt
->ticket_flags
& tflags
) == tflags
) {
190 if (xtgt
->times
.endtime
> prev_endtime
) {
192 } else if ((xtgt
->times
.endtime
= prev_endtime
)
193 && (size
> prev_size
)){
196 } else { /* the first */
197 if (xtgt
->times
.endtime
>= now
) {
202 strcpy(prev_name
, direntp
->d_name
);
203 prev_endtime
= xtgt
->times
.endtime
;
205 sscanf(prev_name
+8,"%8X",&prev_pag
);
213 (void)closedir( dirp
);
216 return 1; /* failed to find one */
218 DEEDEBUG2("Best: %s\n",prev_name
);
223 strcpy(ccname
+38,prev_name
);
224 setenv("KRB5CCNAME",ccname
,1);
230 /*----------------------------------------------*/
231 /* see if this cache is for this this principal */
233 int k5dcematch(luid
, pname
, ccname
, sizep
, tgt
)
237 off_t
*sizep
; /* size of the file */
247 /* DEEDEBUG2("k5dcematch called: cache=%s\n",ccname+38); */
249 if (!strncmp(ccname
,"FILE:",5)) {
251 strcpy(ccdata
,ccname
+5);
252 strcat(ccdata
,".data");
254 /* DEEDEBUG2("Checking the .data file for %s\n",ccdata); */
256 if (stat(ccdata
, &stbuf
))
259 if (stbuf
.st_uid
!= luid
)
262 if ((fd
= open(ccdata
,O_RDONLY
)) == -1)
265 if ((read(fd
,&status
,4)) != 4) {
270 /* DEEDEBUG2(".data file status = %d\n", status); */
275 if (stat(ccname
+5, &stbuf
))
278 if (stbuf
.st_uid
!= luid
)
281 *sizep
= stbuf
.st_size
;
284 return(k5dcegettgt(&cache
, ccname
, pname
, tgt
));
288 /*----------------------------------------*/
289 /* k5dcegettgt - get the tgt from a cache */
291 int k5dcegettgt(pcache
, ccname
, pname
, tgt
)
303 krb5_principal princ
;
306 char *sname
, *realm
, *tgtname
= NULL
;
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
318 setenv("KRB5CCNAME",ccname
,1);
322 if (code
= krb5_cc_default(pcache
)) {
323 com_err(progname
, code
, "while getting ccache");
327 DEEDEBUG("Got cache\n");
329 if (code
= krb5_cc_set_flags(*pcache
, flags
)) {
330 com_err(progname
, code
,"While setting flags");
333 DEEDEBUG("Set flags\n");
334 if (code
= krb5_cc_get_principal(*pcache
, &princ
)) {
335 com_err(progname
, code
, "While getting princ");
338 DEEDEBUG("Got principal\n");
339 if (code
= krb5_unparse_name(princ
, &kusername
)) {
340 com_err(progname
, code
, "While unparsing principal");
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");
350 DEEDEBUG("Principals equal\n");
352 realm
= strchr(pname
,'@');
355 if ((tgtname
= malloc(9 + 2 * strlen(realm
))) == 0) {
356 fprintf(stderr
,"Malloc failed for tgtname\n");
360 strcpy(tgtname
,"krbtgt/");
361 strcat(tgtname
,realm
);
363 strcat(tgtname
,realm
);
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");
371 while (!(code
= krb5_cc_next_cred(*pcache
, &cur
, &creds
))) {
372 krb5_creds
*cred
= &creds
;
374 if (code
= krb5_unparse_name(cred
->server
, &sname
)) {
375 com_err(progname
, code
, "while unparsing server name");
379 if (strncmp(sname
, tgtname
, strlen(tgtname
)) == 0) {
381 if (code
= krb5_copy_creds(&creds
, tgt
)) {
382 com_err(progname
, code
, "while copying TGT");
388 /* we should do a krb5_free_cred_contents(creds); */
391 if (code
= krb5_cc_end_seq_get(*pcache
, &cur
)) {
392 com_err(progname
, code
, "while finishing retrieval");
397 flags
= KRB5_TC_OPENCLOSE
;
398 krb5_cc_set_flags(*pcache
, flags
); /* force a close */
408 /*------------------------------------------*/
409 /* Convert a forwarded TGT to a DCE context */
410 int k5dcecon(luid
, luser
, pname
)
416 krb5_creds
*ftgt
= NULL
;
417 krb5_creds
*tgt
= NULL
;
419 boolean32 reset_passwd
= 0;
421 dce_error_string_t err_string
;
430 krb5_timestamp endtime
;
433 /* If there is no cache to be converted, we should not be here */
435 if ((ccname
= getenv("KRB5CCNAME")) == NULL
) {
436 DEEDEBUG("No KRB5CCNAME\n");
440 if (k5dcegettgt(&fcache
, ccname
, pname
, &ftgt
)) {
441 fprintf(stderr
, "%s: Did not find TGT\n", progname
);
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 */
452 setenv("KRB5CCNAME","",1);
454 #define TKT_ACCEPTABLE (TKT_FLG_FORWARDABLE | TKT_FLG_PROXIABLE \
455 | TKT_FLG_MAY_POSTDATE | TKT_FLG_RENEWABLE | TKT_FLG_HW_AUTH \
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
));
464 DEEDEBUG("Using existing cache\n");
465 return(0); /* use the original one */
468 /* see if the tgts match up */
470 if ((code
= k5dcecreate(luid
, luser
, pname
, &ftgt
))) {
475 * Destroy the Kerberos5 cred cache file.
476 * but dont care aout the return code.
479 DEEDEBUG("Destroying the old cache\n");
480 if ((code
= krb5_cc_destroy(fcache
))) {
481 com_err(progname
, code
, "while destroying Kerberos5 ccache");
487 /*--------------------------------------------------*/
488 /* k5dceupdate - update the cache with a new TGT */
489 /* Assumed that the KRB5CCNAME has been set */
491 int k5dceupdate(krbtgt
, pag
)
499 if (code
= krb5_cc_default(&ccache
)) {
500 com_err(progname
, code
, "while opening cache for update");
504 if (code
= ccache
->ops
->init(ccache
,(*krbtgt
)->client
)) {
505 com_err(progname
, code
, "while reinitilizing cache");
509 /* krb5_cc_store_cred */
510 if (code
= ccache
->ops
->store(ccache
, *krbtgt
)) {
511 com_err(progname
, code
, "while updating cache");
515 sec_login_pag_new_tgt(pag
, (*krbtgt
)->times
.endtime
);
518 /*--------------------------------------------------*/
519 /* k5dcecreate - create a new DCE context */
521 int k5dcecreate(luid
, luser
, pname
, krbtgt
)
535 sec_login_handle_t lcontext
= 0;
536 sec_login_auth_src_t auth_src
= 0;
537 boolean32 reset_passwd
= 0;
539 dce_error_string_t err_string
;
541 setenv("KRB5CCNAME","",1); /* make sure it not misused */
544 DEEDEBUG2("uid=%d\n",uid
);
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.
552 if (seteuid(luid
) < 0)
556 cp
= strchr(pname
,'@');
560 DEEDEBUG2("basename=%s\n",cp
);
561 DEEDEBUG2("realm=%s\n",urealm
);
563 /* now build the username as a single string or a /.../cell/user
564 * if this is a cross cell
567 if ((username
= malloc(7+strlen(pname
)+strlen(urealm
))) == 0) {
568 fprintf(stderr
,"Malloc failed for username\n");
571 if (krb5_get_default_realm(&defrealm
)) {
572 DEEDEBUG("krb5_get_default_realm failed\n");
577 if (!strcmp(urealm
,defrealm
)) {
578 strcpy(username
,pname
);
580 strcpy(username
,"/.../");
581 strcat(username
,urealm
);
582 strcat(username
,"/");
583 strcat(username
,pname
);
587 * Setup a DCE login context
590 if (sec_login_setup_identity((unsigned_char_p_t
)username
,
591 (sec_login_external_tgt
|sec_login_proxy_cred
),
596 DEEDEBUG("Adding our new TGT\n");
597 sec_login_krb5_add_cred(lcontext
, *krbtgt
, &st
);
599 dce_error_inq_text(st
, err_string
, &lst
);
601 "Error while adding credentials for %s because %s\n",
602 username
, err_string
);
605 DEEDEBUG("validating and certifying\n");
607 * Now "validate" and certify the identity,
608 * usually we would pass a password here, but...
609 * sec_login_valid_and_cert_ident
610 * sec_login_validate_identity
613 if (sec_login_validate_identity(lcontext
, 0, &reset_passwd
,
615 DEEDEBUG2("validate_identity st=%d\n",st
);
617 dce_error_inq_text(st
, err_string
, &lst
);
618 fprintf(stderr
, "Validation error for %s because %s\n",
619 username
, err_string
);
622 if (!sec_login_certify_identity(lcontext
,&st
)) {
623 dce_error_inq_text(st
, err_string
, &lst
);
625 "Credentials not certified because %s\n",err_string
);
629 "Password must be changed for %s\n", username
);
631 if (auth_src
== sec_login_auth_src_local
) {
633 "Credentials obtained from local registry for %s\n",
636 if (auth_src
== sec_login_auth_src_overridden
) {
637 fprintf(stderr
, "Validated %s from local override entry, no network credentials obtained\n", username
);
642 * Actually create the cred files.
644 DEEDEBUG("Ceating new cred files.\n");
645 sec_login_set_context(lcontext
, &st
);
647 dce_error_inq_text(st
, err_string
, &lst
);
649 "Unable to set context for %s because %s\n",
650 username
, err_string
);
655 * Now free up the local context and leave the
656 * network context with its pag
659 sec_login_release_context(&lcontext
, &st
);
661 dce_error_inq_text(st
, err_string
, &lst
);
663 "Unable to release context for %s because %s\n",
664 username
, err_string
);
670 DEEDEBUG2("validate failed %d\n",st
);
671 dce_error_inq_text(st
, err_string
, &lst
);
673 "Unable to validate %s because %s\n", username
,
679 dce_error_inq_text(st
, err_string
, &lst
);
681 "Unable to setup login entry for %s because %s\n",
682 username
, err_string
);
687 /* if we were root, get back to root */
689 DEEDEBUG2("sec_login_inq_pag %8.8x\n",
690 sec_login_inq_pag(lcontext
, &st
));
696 DEEDEBUG("completed\n");
704 DEEDEBUG("Aborting\n");
710 /*-------------------------------------------------*/
720 char *lusername
= NULL
;
727 krb5_creds
*tgt
= NULL
;
731 open("/tmp/k5dce.debug",O_WRONLY
|O_CREAT
|O_APPEND
, 0600);
734 if (myuid
= getuid()) {
735 DEEDEBUG2("UID = %d\n",myuid
);
736 exit(33); /* must be root to run this, get out now */
739 while ((rv
= getopt(argc
,argv
,"l:p:fs")) != -1) {
740 DEEDEBUG2("Arg = %c\n", rv
);
742 case 'l': /* user name */
744 DEEDEBUG2("Optarg = %s\n", optarg
);
746 case 'p': /* principal name */
748 DEEDEBUG2("Optarg = %s\n", optarg
);
750 case 'f': /* convert a forwarded TGT to a context */
753 case 's': /* old test parameter, ignore it */
758 setlocale(LC_ALL
, "");
760 time(&now
); /* set time to check expired tickets */
762 /* if lusername == NULL, Then user is passed as the USER= variable */
765 lusername
= getenv("USER");
767 fprintf(stderr
, "USER not in environment\n");
772 if ((pw
= getpwnam(lusername
)) == NULL
) {
773 fprintf(stderr
, "Who are you?\n");
780 status
= k5dcecon(luid
, lusername
, pname
);
782 status
= k5dcesession(luid
, pname
, &tgt
, NULL
, 0);
786 printf("%s",getenv("KRB5CCNAME")); /* return via stdout to caller */
787 DEEDEBUG2("KRB5CCNAME=%s\n",getenv("KRB5CCNAME"));
790 DEEDEBUG2("Returning status %d\n",status
);