summaryrefslogtreecommitdiffstats
path: root/crypto/heimdal/appl/dceutils/k5dcecon.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/heimdal/appl/dceutils/k5dcecon.c')
-rw-r--r--crypto/heimdal/appl/dceutils/k5dcecon.c791
1 files changed, 791 insertions, 0 deletions
diff --git a/crypto/heimdal/appl/dceutils/k5dcecon.c b/crypto/heimdal/appl/dceutils/k5dcecon.c
new file mode 100644
index 0000000..38acee9
--- /dev/null
+++ b/crypto/heimdal/appl/dceutils/k5dcecon.c
@@ -0,0 +1,791 @@
+/*
+ * (c) Copyright 1995 HEWLETT-PACKARD COMPANY
+ *
+ * To anyone who acknowledges that this file is provided
+ * "AS IS" without any express or implied warranty:
+ * permission to use, copy, modify, and distribute this
+ * file for any purpose is hereby granted without fee,
+ * provided that the above copyright notice and this
+ * notice appears in all copies, and that the name of
+ * Hewlett-Packard Company not be used in advertising or
+ * publicity pertaining to distribution of the software
+ * without specific, written prior permission. Hewlett-
+ * Packard Company makes no representations about the
+ * suitability of this software for any purpose.
+ *
+ */
+/*
+ * k5dcecon - Program to convert a K5 TGT to a DCE context,
+ * for use with DFS and its PAG.
+ *
+ * The program is designed to be called as a sub process,
+ * and return via stdout the name of the cache which implies
+ * the PAG which should be used. This program itself does not
+ * use the cache or PAG itself, so the PAG in the kernel for
+ * this program may not be set.
+ *
+ * The calling program can then use the name of the cache
+ * to set the KRB5CCNAME and PAG for its self and its children.
+ *
+ * If no ticket was passed, an attemplt to join an existing
+ * PAG will be made.
+ *
+ * If a forwarded K5 TGT is passed in, either a new DCE
+ * context will be created, or an existing one will be updated.
+ * If the same ticket was already used to create an existing
+ * context, it will be joined instead.
+ *
+ * Parts of this program are based on k5dceauth,c which was
+ * given to me by HP and by the k5dcelogin.c which I developed.
+ * A slightly different version of k5dcelogin.c, was added to
+ * DCE 1.2.2
+ *
+ * D. E. Engert 6/17/97 ANL
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <pwd.h>
+#include <string.h>
+#include <time.h>
+
+#include <errno.h>
+#include "k5dce.h"
+
+#include <dce/sec_login.h>
+#include <dce/dce_error.h>
+#include <dce/passwd.h>
+
+/* #define DEBUG */
+#if defined(DEBUG)
+#define DEEDEBUG(A) fprintf(stderr,A); fflush(stderr)
+#define DEEDEBUG2(A,B) fprintf(stderr,A,B); fflush(stderr)
+#else
+#define DEEDEBUG(A)
+#define DEEDEBUG2(A,B)
+#endif
+
+#ifdef __hpux
+#define seteuid(A) setresuid(-1,A,-1);
+#endif
+
+
+int k5dcecreate (uid_t, char *, char*, krb5_creds **);
+int k5dcecon (uid_t, char *, char *);
+int k5dcegettgt (krb5_ccache *, char *, char *, krb5_creds **);
+int k5dcematch (uid_t, char *, char *, off_t *, krb5_creds **);
+int k5dcesession (uid_t, char *, krb5_creds **, int *,krb5_flags);
+
+
+char *progname = "k5dcecon";
+static time_t now;
+
+#ifdef notdef
+#ifdef _AIX
+/*---------------------------------------------*/
+ /* AIX with DCE 1.1 does not have the com_err in the libdce.a
+ * do a half hearted job of substituting for it.
+ */
+void com_err(char *p1, int code, ...)
+{
+ int lst;
+ dce_error_string_t err_string;
+ dce_error_inq_text(code, err_string, &lst);
+ fprintf(stderr,"Error %d in %s: %s\n", code, p1, err_string );
+}
+
+/*---------------------------------------------*/
+void krb5_init_ets()
+{
+
+}
+#endif
+#endif
+
+
+/*------------------------------------------------*/
+/* find a cache to use for our new pag */
+/* Since there is no simple way to determine which
+ * caches are associated with a pag, we will have
+ * do look around and see what makes most sense on
+ * different systems.
+ * on a Solaris system, and in the DCE source,
+ * the pags always start with a 41.
+ * this is not true on the IBM, where there does not
+ * appear to be any pattern.
+ *
+ * But since we are always certifing our creds when
+ * they are received, we can us that fact, and look
+ * at the first word of the associated data file
+ * to see that it has a "5". If not don't use.
+ */
+
+int k5dcesession(luid, pname, tgt, ppag, tflags)
+ uid_t luid;
+ char *pname;
+ krb5_creds **tgt;
+ int *ppag;
+ krb5_flags tflags;
+{
+ DIR *dirp;
+ struct dirent *direntp;
+ off_t size;
+ krb5_timestamp endtime;
+ int better = 0;
+ krb5_creds *xtgt;
+
+ char prev_name[17] = "";
+ krb5_timestamp prev_endtime;
+ off_t prev_size;
+ u_long prev_pag = 0;
+
+ char ccname[64] = "FILE:/opt/dcelocal/var/security/creds/";
+
+ error_status_t st;
+ sec_login_handle_t lcontext = 0;
+ dce_error_string_t err_string;
+ int lst;
+
+ DEEDEBUG2("k5dcesession looking for flags %8.8x\n",tflags);
+
+ dirp = opendir("/opt/dcelocal/var/security/creds/");
+ if (dirp == NULL) {
+ return 1;
+ }
+
+ while ( (direntp = readdir( dirp )) != NULL ) {
+
+/*
+ * (but root has the ffffffff which we are not interested in)
+ */
+ if (!strncmp(direntp->d_name,"dcecred_",8)
+ && (strlen(direntp->d_name) == 16)) {
+
+ /* looks like a cache name, lets do the stat, etc */
+
+ strcpy(ccname+38,direntp->d_name);
+ if (!k5dcematch(luid, pname, ccname, &size, &xtgt)) {
+
+ /* its one of our caches, see if it is better
+ * i.e. the endtime is farther, and if the endtimes
+ * are the same, take the larger, as he who has the
+ * most tickets wins.
+ * it must also had the same set of flags at least
+ * i.e. if the forwarded TGT is forwardable, this one must
+ * be as well.
+ */
+
+ DEEDEBUG2("Cache:%s",direntp->d_name);
+ DEEDEBUG2(" size:%d",size);
+ DEEDEBUG2(" flags:%8.8x",xtgt->ticket_flags);
+ DEEDEBUG2(" %s",ctime((time_t *)&xtgt->times.endtime));
+
+ if ((xtgt->ticket_flags & tflags) == tflags ) {
+ if (prev_name[0]) {
+ if (xtgt->times.endtime > prev_endtime) {
+ better = 1;
+ } else if ((xtgt->times.endtime = prev_endtime)
+ && (size > prev_size)){
+ better = 1;
+ }
+ } else { /* the first */
+ if (xtgt->times.endtime >= now) {
+ better = 1;
+ }
+ }
+ if (better) {
+ strcpy(prev_name, direntp->d_name);
+ prev_endtime = xtgt->times.endtime;
+ prev_size = size;
+ sscanf(prev_name+8,"%8X",&prev_pag);
+ *tgt = xtgt;
+ better = 0;
+ }
+ }
+ }
+ }
+ }
+ (void)closedir( dirp );
+
+ if (!prev_name[0])
+ return 1; /* failed to find one */
+
+ DEEDEBUG2("Best: %s\n",prev_name);
+
+ if (ppag)
+ *ppag = prev_pag;
+
+ strcpy(ccname+38,prev_name);
+ setenv("KRB5CCNAME",ccname,1);
+
+ return(0);
+}
+
+
+/*----------------------------------------------*/
+/* see if this cache is for this this principal */
+
+int k5dcematch(luid, pname, ccname, sizep, tgt)
+ uid_t luid;
+ char *pname;
+ char *ccname;
+ off_t *sizep; /* size of the file */
+ krb5_creds **tgt;
+{
+
+ krb5_ccache cache;
+ struct stat stbuf;
+ char ccdata[256];
+ int fd;
+ int status;
+
+ /* DEEDEBUG2("k5dcematch called: cache=%s\n",ccname+38); */
+
+ if (!strncmp(ccname,"FILE:",5)) {
+
+ strcpy(ccdata,ccname+5);
+ strcat(ccdata,".data");
+
+ /* DEEDEBUG2("Checking the .data file for %s\n",ccdata); */
+
+ if (stat(ccdata, &stbuf))
+ return(1);
+
+ if (stbuf.st_uid != luid)
+ return(1);
+
+ if ((fd = open(ccdata,O_RDONLY)) == -1)
+ return(1);
+
+ if ((read(fd,&status,4)) != 4) {
+ close(fd);
+ return(1);
+ }
+
+ /* DEEDEBUG2(".data file status = %d\n", status); */
+
+ if (status != 5)
+ return(1);
+
+ if (stat(ccname+5, &stbuf))
+ return(1);
+
+ if (stbuf.st_uid != luid)
+ return(1);
+
+ *sizep = stbuf.st_size;
+ }
+
+ return(k5dcegettgt(&cache, ccname, pname, tgt));
+}
+
+
+/*----------------------------------------*/
+/* k5dcegettgt - get the tgt from a cache */
+
+int k5dcegettgt(pcache, ccname, pname, tgt)
+ krb5_ccache *pcache;
+ char *ccname;
+ char *pname;
+ krb5_creds **tgt;
+
+{
+ krb5_ccache cache;
+ krb5_cc_cursor cur;
+ krb5_creds creds;
+ int code;
+ int found = 1;
+ krb5_principal princ;
+ char *kusername;
+ krb5_flags flags;
+ char *sname, *realm, *tgtname = NULL;
+
+ /* Since DCE does not expose much of the Kerberos interface,
+ * we will have to use what we can. This means setting the
+ * KRB5CCNAME for each file we want to test
+ * We will also not worry about freeing extra cache structures
+ * as this this routine is also not exposed, and this should not
+ * effect this module.
+ * We should also free the creds contents, but that is not exposed
+ * either.
+ */
+
+ setenv("KRB5CCNAME",ccname,1);
+ cache = NULL;
+ *tgt = NULL;
+
+ if (code = krb5_cc_default(pcache)) {
+ com_err(progname, code, "while getting ccache");
+ goto return2;
+ }
+
+ DEEDEBUG("Got cache\n");
+ flags = 0;
+ if (code = krb5_cc_set_flags(*pcache, flags)) {
+ com_err(progname, code,"While setting flags");
+ goto return2;
+ }
+ DEEDEBUG("Set flags\n");
+ if (code = krb5_cc_get_principal(*pcache, &princ)) {
+ com_err(progname, code, "While getting princ");
+ goto return1;
+ }
+ DEEDEBUG("Got principal\n");
+ if (code = krb5_unparse_name(princ, &kusername)) {
+ com_err(progname, code, "While unparsing principal");
+ goto return1;
+ }
+
+ DEEDEBUG2("Unparsed to \"%s\"\n", kusername);
+ DEEDEBUG2("pname is \"%s\"\n", pname);
+ if (strcmp(kusername, pname)) {
+ DEEDEBUG("Principals not equal\n");
+ goto return1;
+ }
+ DEEDEBUG("Principals equal\n");
+
+ realm = strchr(pname,'@');
+ realm++;
+
+ if ((tgtname = malloc(9 + 2 * strlen(realm))) == 0) {
+ fprintf(stderr,"Malloc failed for tgtname\n");
+ goto return1;
+ }
+
+ strcpy(tgtname,"krbtgt/");
+ strcat(tgtname,realm);
+ strcat(tgtname,"@");
+ strcat(tgtname,realm);
+
+ DEEDEBUG2("Getting tgt %s\n", tgtname);
+ if (code = krb5_cc_start_seq_get(*pcache, &cur)) {
+ com_err(progname, code, "while starting to retrieve tickets");
+ goto return1;
+ }
+
+ while (!(code = krb5_cc_next_cred(*pcache, &cur, &creds))) {
+ krb5_creds *cred = &creds;
+
+ if (code = krb5_unparse_name(cred->server, &sname)) {
+ com_err(progname, code, "while unparsing server name");
+ continue;
+ }
+
+ if (strncmp(sname, tgtname, strlen(tgtname)) == 0) {
+ DEEDEBUG("FOUND\n");
+ if (code = krb5_copy_creds(&creds, tgt)) {
+ com_err(progname, code, "while copying TGT");
+ goto return1;
+ }
+ found = 0;
+ break;
+ }
+ /* we should do a krb5_free_cred_contents(creds); */
+ }
+
+ if (code = krb5_cc_end_seq_get(*pcache, &cur)) {
+ com_err(progname, code, "while finishing retrieval");
+ goto return2;
+ }
+
+return1:
+ flags = KRB5_TC_OPENCLOSE;
+ krb5_cc_set_flags(*pcache, flags); /* force a close */
+
+return2:
+ if (tgtname)
+ free(tgtname);
+
+ return(found);
+}
+
+
+/*------------------------------------------*/
+/* Convert a forwarded TGT to a DCE context */
+int k5dcecon(luid, luser, pname)
+ uid_t luid;
+ char *luser;
+ char *pname;
+{
+
+ krb5_creds *ftgt = NULL;
+ krb5_creds *tgt = NULL;
+ unsigned32 dfspag;
+ boolean32 reset_passwd = 0;
+ int lst;
+ dce_error_string_t err_string;
+ char *shell_prog;
+ krb5_ccache fcache;
+ char *ccname;
+ char *kusername;
+ char *urealm;
+ char *cp;
+ int pag;
+ int code;
+ krb5_timestamp endtime;
+
+
+ /* If there is no cache to be converted, we should not be here */
+
+ if ((ccname = getenv("KRB5CCNAME")) == NULL) {
+ DEEDEBUG("No KRB5CCNAME\n");
+ return(1);
+ }
+
+ if (k5dcegettgt(&fcache, ccname, pname, &ftgt)) {
+ fprintf(stderr, "%s: Did not find TGT\n", progname);
+ return(1);
+ }
+
+
+ DEEDEBUG2("flags=%x\n",ftgt->ticket_flags);
+ if (!(ftgt->ticket_flags & TKT_FLG_FORWARDABLE)){
+ fprintf(stderr,"Ticket not forwardable\n");
+ return(0); /* but OK to continue */
+ }
+
+ setenv("KRB5CCNAME","",1);
+
+#define TKT_ACCEPTABLE (TKT_FLG_FORWARDABLE | TKT_FLG_PROXIABLE \
+ | TKT_FLG_MAY_POSTDATE | TKT_FLG_RENEWABLE | TKT_FLG_HW_AUTH \
+ | TKT_FLG_PRE_AUTH)
+
+ if (!k5dcesession(luid, pname, &tgt, &pag,
+ (ftgt->ticket_flags & TKT_ACCEPTABLE))) {
+ if (ftgt->times.endtime > tgt->times.endtime) {
+ DEEDEBUG("Updating existing cache\n");
+ return(k5dceupdate(&ftgt, pag));
+ } else {
+ DEEDEBUG("Using existing cache\n");
+ return(0); /* use the original one */
+ }
+ }
+ /* see if the tgts match up */
+
+ if ((code = k5dcecreate(luid, luser, pname, &ftgt))) {
+ return (code);
+ }
+
+ /*
+ * Destroy the Kerberos5 cred cache file.
+ * but dont care aout the return code.
+ */
+
+ DEEDEBUG("Destroying the old cache\n");
+ if ((code = krb5_cc_destroy(fcache))) {
+ com_err(progname, code, "while destroying Kerberos5 ccache");
+ }
+ return (0);
+}
+
+
+/*--------------------------------------------------*/
+/* k5dceupdate - update the cache with a new TGT */
+/* Assumed that the KRB5CCNAME has been set */
+
+int k5dceupdate(krbtgt, pag)
+ krb5_creds **krbtgt;
+ int pag;
+{
+
+ krb5_ccache ccache;
+ int code;
+
+ if (code = krb5_cc_default(&ccache)) {
+ com_err(progname, code, "while opening cache for update");
+ return(2);
+ }
+
+ if (code = ccache->ops->init(ccache,(*krbtgt)->client)) {
+ com_err(progname, code, "while reinitilizing cache");
+ return(3);
+ }
+
+ /* krb5_cc_store_cred */
+ if (code = ccache->ops->store(ccache, *krbtgt)) {
+ com_err(progname, code, "while updating cache");
+ return(2);
+ }
+
+ sec_login_pag_new_tgt(pag, (*krbtgt)->times.endtime);
+ return(0);
+}
+/*--------------------------------------------------*/
+/* k5dcecreate - create a new DCE context */
+
+int k5dcecreate(luid, luser, pname, krbtgt)
+ uid_t luid;
+ char *luser;
+ char *pname;
+ krb5_creds **krbtgt;
+{
+
+ char *cp;
+ char *urealm;
+ char *username;
+ char *defrealm;
+ uid_t uid;
+
+ error_status_t st;
+ sec_login_handle_t lcontext = 0;
+ sec_login_auth_src_t auth_src = 0;
+ boolean32 reset_passwd = 0;
+ int lst;
+ dce_error_string_t err_string;
+
+ setenv("KRB5CCNAME","",1); /* make sure it not misused */
+
+ uid = getuid();
+ DEEDEBUG2("uid=%d\n",uid);
+
+ /* if run as root, change to user, so as to have the
+ * cache created for the local user even if cross-cell
+ * If run as a user, let standard file protection work.
+ */
+
+ if (uid == 0) {
+ seteuid(luid);
+ }
+
+ cp = strchr(pname,'@');
+ *cp = '\0';
+ urealm = ++cp;
+
+ DEEDEBUG2("basename=%s\n",cp);
+ DEEDEBUG2("realm=%s\n",urealm);
+
+ /* now build the username as a single string or a /.../cell/user
+ * if this is a cross cell
+ */
+
+ if ((username = malloc(7+strlen(pname)+strlen(urealm))) == 0) {
+ fprintf(stderr,"Malloc failed for username\n");
+ goto abort;
+ }
+ if (krb5_get_default_realm(&defrealm)) {
+ DEEDEBUG("krb5_get_default_realm failed\n");
+ goto abort;
+ }
+
+
+ if (!strcmp(urealm,defrealm)) {
+ strcpy(username,pname);
+ } else {
+ strcpy(username,"/.../");
+ strcat(username,urealm);
+ strcat(username,"/");
+ strcat(username,pname);
+ }
+
+ /*
+ * Setup a DCE login context
+ */
+
+ if (sec_login_setup_identity((unsigned_char_p_t)username,
+ (sec_login_external_tgt|sec_login_proxy_cred),
+ &lcontext, &st)) {
+ /*
+ * Add our TGT.
+ */
+ DEEDEBUG("Adding our new TGT\n");
+ sec_login_krb5_add_cred(lcontext, *krbtgt, &st);
+ if (st) {
+ dce_error_inq_text(st, err_string, &lst);
+ fprintf(stderr,
+ "Error while adding credentials for %s because %s\n",
+ username, err_string);
+ goto abort;
+ }
+ DEEDEBUG("validating and certifying\n");
+ /*
+ * Now "validate" and certify the identity,
+ * usually we would pass a password here, but...
+ * sec_login_valid_and_cert_ident
+ * sec_login_validate_identity
+ */
+
+ if (sec_login_validate_identity(lcontext, 0, &reset_passwd,
+ &auth_src, &st)) {
+ DEEDEBUG2("validate_identity st=%d\n",st);
+ if (st) {
+ dce_error_inq_text(st, err_string, &lst);
+ fprintf(stderr, "Validation error for %s because %s\n",
+ username, err_string);
+ goto abort;
+ }
+ if (!sec_login_certify_identity(lcontext,&st)) {
+ dce_error_inq_text(st, err_string, &lst);
+ fprintf(stderr,
+ "Credentials not certified because %s\n",err_string);
+ }
+ if (reset_passwd) {
+ fprintf(stderr,
+ "Password must be changed for %s\n", username);
+ }
+ if (auth_src == sec_login_auth_src_local) {
+ fprintf(stderr,
+ "Credentials obtained from local registry for %s\n",
+ username);
+ }
+ if (auth_src == sec_login_auth_src_overridden) {
+ fprintf(stderr, "Validated %s from local override entry, no network credentials obtained\n", username);
+ goto abort;
+
+ }
+ /*
+ * Actually create the cred files.
+ */
+ DEEDEBUG("Ceating new cred files.\n");
+ sec_login_set_context(lcontext, &st);
+ if (st) {
+ dce_error_inq_text(st, err_string, &lst);
+ fprintf(stderr,
+ "Unable to set context for %s because %s\n",
+ username, err_string);
+ goto abort;
+ }
+
+ /*
+ * Now free up the local context and leave the
+ * network context with its pag
+ */
+#if 0
+ sec_login_release_context(&lcontext, &st);
+ if (st) {
+ dce_error_inq_text(st, err_string, &lst);
+ fprintf(stderr,
+ "Unable to release context for %s because %s\n",
+ username, err_string);
+ goto abort;
+ }
+#endif
+ }
+ else {
+ DEEDEBUG2("validate failed %d\n",st);
+ dce_error_inq_text(st, err_string, &lst);
+ fprintf(stderr,
+ "Unable to validate %s because %s\n", username,
+ err_string);
+ goto abort;
+ }
+ }
+ else {
+ dce_error_inq_text(st, err_string, &lst);
+ fprintf(stderr,
+ "Unable to setup login entry for %s because %s\n",
+ username, err_string);
+ goto abort;
+ }
+
+ done:
+ /* if we were root, get back to root */
+
+ DEEDEBUG2("sec_login_inq_pag %8.8x\n",
+ sec_login_inq_pag(lcontext, &st));
+
+ if (uid == 0) {
+ seteuid(0);
+ }
+
+ DEEDEBUG("completed\n");
+ return(0);
+
+ abort:
+ if (uid == 0) {
+ seteuid(0);
+ }
+
+ DEEDEBUG("Aborting\n");
+ return(2);
+}
+
+
+
+/*-------------------------------------------------*/
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int status;
+ extern int optind;
+ extern char *optarg;
+ int rv;
+
+ char *lusername = NULL;
+ char *pname = NULL;
+ int fflag = 0;
+ struct passwd *pw;
+ uid_t luid;
+ uid_t myuid;
+ char *ccname;
+ krb5_creds *tgt = NULL;
+
+#ifdef DEBUG
+ close(2);
+ open("/tmp/k5dce.debug",O_WRONLY|O_CREAT|O_APPEND);
+#endif
+
+ if (myuid = getuid()) {
+ DEEDEBUG2("UID = %d\n",myuid);
+ exit(33); /* must be root to run this, get out now */
+ }
+
+ while ((rv = getopt(argc,argv,"l:p:fs")) != -1) {
+ DEEDEBUG2("Arg = %c\n", rv);
+ switch(rv) {
+ case 'l': /* user name */
+ lusername = optarg;
+ DEEDEBUG2("Optarg = %s\n", optarg);
+ break;
+ case 'p': /* principal name */
+ pname = optarg;
+ DEEDEBUG2("Optarg = %s\n", optarg);
+ break;
+ case 'f': /* convert a forwarded TGT to a context */
+ fflag++;
+ break;
+ case 's': /* old test parameter, ignore it */
+ break;
+ }
+ }
+
+ setlocale(LC_ALL, "");
+ krb5_init_ets();
+ time(&now); /* set time to check expired tickets */
+
+ /* if lusername == NULL, Then user is passed as the USER= variable */
+
+ if (!lusername) {
+ lusername = getenv("USER");
+ if (!lusername) {
+ fprintf(stderr, "USER not in environment\n");
+ return(3);
+ }
+ }
+
+ if ((pw = getpwnam(lusername)) == NULL) {
+ fprintf(stderr, "Who are you?\n");
+ return(44);
+ }
+
+ luid = pw->pw_uid;
+
+ if (fflag) {
+ status = k5dcecon(luid, lusername, pname);
+ } else {
+ status = k5dcesession(luid, pname, &tgt, NULL, 0);
+ }
+
+ if (!status) {
+ printf("%s",getenv("KRB5CCNAME")); /* return via stdout to caller */
+ DEEDEBUG2("KRB5CCNAME=%s\n",getenv("KRB5CCNAME"));
+ }
+
+ DEEDEBUG2("Returning status %d\n",status);
+ return (status);
+}
OpenPOWER on IntegriCloud