diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /third_party/heimdal/appl/dceutils/k5dcecon.c | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/heimdal/appl/dceutils/k5dcecon.c')
-rw-r--r-- | third_party/heimdal/appl/dceutils/k5dcecon.c | 792 |
1 files changed, 792 insertions, 0 deletions
diff --git a/third_party/heimdal/appl/dceutils/k5dcecon.c b/third_party/heimdal/appl/dceutils/k5dcecon.c new file mode 100644 index 0000000..c905625 --- /dev/null +++ b/third_party/heimdal/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) == 0 && + (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) == 0) { + + 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) != 0) { + 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 don't care about 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) == 0) { + 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); +} |