diff options
Diffstat (limited to 'appl/dceutils/k5dcecon.c')
| -rw-r--r-- | appl/dceutils/k5dcecon.c | 792 | 
1 files changed, 792 insertions, 0 deletions
| diff --git a/appl/dceutils/k5dcecon.c b/appl/dceutils/k5dcecon.c new file mode 100644 index 000000000000..88cdf281fe71 --- /dev/null +++ b/appl/dceutils/k5dcecon.c @@ -0,0 +1,792 @@ +/* + * (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 itself 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))  { + +        /* it's 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) { +		if (seteuid(luid) < 0) +			goto abort; +	} + +	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, 0600); +#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); +} | 
