/* * Log to a cell. If the cell has already been logged to, return without * doing anything. Otherwise, log to it and mark that it has been logged * to. */ static int auth_to_cell(krb5_context context, char *user, char *cell, char *realm) { int status = 0; char username[BUFSIZ]; /* To hold client username structure */ afs_int32 viceId; /* AFS uid of user */ char name[ANAME_SZ]; /* Name of afs key */ char primary_instance[INST_SZ]; /* Instance of afs key */ char secondary_instance[INST_SZ]; /* Backup instance to try */ int try_secondary = 0; /* Flag to indicate if we try second */ char realm_of_user[REALM_SZ]; /* Kerberos realm of user */ char realm_of_cell[REALM_SZ]; /* Kerberos realm of cell */ char local_cell[MAXCELLCHARS+1]; char cell_to_use[MAXCELLCHARS+1]; /* Cell to authenticate to */ static char lastcell[MAXCELLCHARS+1] = { 0 }; static char confname[512] = { 0 }; krb5_creds *v5cred = NULL; struct ktc_principal aserver; struct ktc_principal aclient; struct ktc_token atoken, btoken; int afssetpag = 0, uid = -1; struct passwd *pwd; memset(name, 0, sizeof(name)); memset(primary_instance, 0, sizeof(primary_instance)); memset(secondary_instance, 0, sizeof(secondary_instance)); memset(realm_of_user, 0, sizeof(realm_of_user)); memset(realm_of_cell, 0, sizeof(realm_of_cell)); syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog starting: user %s uid %d", user, getuid()); if (confname[0] == '\0') { strncpy(confname, AFSDIR_CLIENT_ETC_DIRPATH, sizeof(confname)); confname[sizeof(confname) - 2] = '\0'; } /* NULL or empty cell returns information on local cell */ if ((status = get_cellconfig(cell, &ak_cellconfig, local_cell, linkedcell))) { syslog(LOG_AUTH|LOG_ERR, "LAM aklog: get_cellconfig returns %d", status); return(status); } strncpy(cell_to_use, ak_cellconfig.name, MAXCELLCHARS); cell_to_use[MAXCELLCHARS] = 0; /* * Find out which realm we're supposed to authenticate to. If one * is not included, use the kerberos realm found in the credentials * cache. */ if (realm && realm[0]) { strcpy(realm_of_cell, realm); } else { char *afs_realm = afs_realm_of_cell(context, &ak_cellconfig, FALSE); if (!afs_realm) { syslog(LOG_AUTH|LOG_ERR, "LAM aklog: afs_realm_of_cell returns %d", status); return AFSCONF_FAILURE; } strcpy(realm_of_cell, afs_realm); } /* We use the afs.<cellname> convention here... * * Doug Engert's original code had principals of the form: * * "afsx/cell@realm" * * in the KDC, so the name wouldn't conflict with DFS. Since we're * not using DFS, I changed it just to look for the following * principals: * * afs/<cell>@<realm> * afs@<realm> * * Because people are transitioning from afs@realm to afs/cell, * we configure things so that if the first one isn't found, we * try the second one. You can select which one you prefer with * a configure option. */ strcpy(name, AFSKEY); if (1 || strcasecmp(cell_to_use, realm_of_cell) != 0) { strncpy(primary_instance, cell_to_use, sizeof(primary_instance)); primary_instance[sizeof(primary_instance)-1] = '\0'; if (strcasecmp(cell_to_use, realm_of_cell) == 0) { try_secondary = 1; secondary_instance[0] = '\0'; } } else { primary_instance[0] = '\0'; try_secondary = 1; strncpy(secondary_instance, cell_to_use, sizeof(secondary_instance)); secondary_instance[sizeof(secondary_instance)-1] = '\0'; } /* * Extract the session key from the ticket file and hand-frob an * afs style authenticator. */ /* * Try to obtain AFS tickets. Because there are two valid service * names, we will try both, but trying the more specific first. * * afs/<cell>@<realm> i.e. allow for single name with "." * afs@<realm> */ status = get_credv5(context, user, name, primary_instance, realm_of_cell, &v5cred); if ((status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || status == KRB5KRB_ERR_GENERIC) && !realm_of_cell[0]) { char *afs_realm = afs_realm_of_cell(context, &ak_cellconfig, TRUE); if (!afs_realm) { syslog(LOG_AUTH|LOG_ERR, "LAM aklog: afs_realm_of_cell returns %d", status); return AFSCONF_FAILURE; } strcpy(realm_of_cell, afs_realm); if (strcasecmp(cell_to_use, realm_of_cell) == 0) { try_secondary = 1; secondary_instance[0] = '\0'; } status = get_credv5(context, user, name, primary_instance, realm_of_cell, &v5cred); } if (status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || status == KRB5KRB_ERR_GENERIC) { if (try_secondary) status = get_credv5(context, user, name, secondary_instance, realm_of_cell, &v5cred); } if (status) { syslog(LOG_AUTH|LOG_ERR, "LAM aklog: get_credv5 returns %d", status); return status; } strncpy(aserver.name, AFSKEY, MAXKTCNAMELEN - 1); strncpy(aserver.instance, AFSINST, MAXKTCNAMELEN - 1); strncpy(aserver.cell, cell_to_use, MAXKTCREALMLEN - 1); /* * The default is to use rxkad2b, which means we put in a full * V5 ticket. If the user specifies -524, we talk to the * 524 ticket converter. */ { char *p; int len; len = min(get_princ_len(context, v5cred->client, 0), second_comp(context, v5cred->client) ? MAXKTCNAMELEN - 2 : MAXKTCNAMELEN - 1); strncpy(username, get_princ_str(context, v5cred->client, 0), len); username[len] = '\0'; if (second_comp(context, v5cred->client)) { strcat(username, "."); p = username + strlen(username); len = min(get_princ_len(context, v5cred->client, 1), MAXKTCNAMELEN - strlen(username) - 1); strncpy(p, get_princ_str(context, v5cred->client, 1), len); p[len] = '\0'; } memset(&atoken, 0, sizeof(atoken)); atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5; atoken.startTime = v5cred->times.starttime;; atoken.endTime = v5cred->times.endtime; memcpy(&atoken.sessionKey, get_cred_keydata(v5cred), get_cred_keylen(v5cred)); atoken.ticketLen = v5cred->ticket.length; memcpy(atoken.ticket, v5cred->ticket.data, atoken.ticketLen); } if ((status = get_user_realm(context, realm_of_user))) { syslog(LOG_AUTH|LOG_ERR, "LAM aklog: get_user_realm returns %d", status); return KRB5_REALM_UNKNOWN; } if (strcmp(realm_of_user, realm_of_cell)) { strcat(username, "@"); strcat(username, realm_of_user); } strcpy(lastcell, aserver.cell); /* * This is a crock, but it is Transarc's crock, so * we have to play along in order to get the * functionality. The way the afs id is stored is * as a string in the username field of the token. * Contrary to what you may think by looking at * the code for tokens, this hack (AFS ID %d) will * not work if you change %d to something else. */ #if 0 /* This actually crashes long-running daemons */ if (!pr_Initialize (0, confname, aserver.cell)) status = pr_SNameToId (username, &viceId); if ((status == 0) && (viceId != ANONYMOUSID)) sprintf (username, "AFS ID %d", (int) viceId); #else /* * This actually only works assuming that your uid and pts space match * and probably this works only for the local cell anyway. */ if ((uid = getuid()) == 0) { if ((pwd = getpwnam(user)) == NULL) { syslog(LOG_AUTH|LOG_ERR, "LAM aklog: getpwnam %s failed", user); return AUTH_FAILURE; } } /* Don't do first-time registration. Handle only the simple case */ if ((status == 0) && (viceId != ANONYMOUSID)) sprintf (username, "AFS ID %d", ((uid==0)?(int)pwd->pw_uid:(int)uid)); #endif /* Reset the "aclient" structure before we call ktc_SetToken. * This structure was first set by the ktc_GetToken call when * we were comparing whether identical tokens already existed. */ strncpy(aclient.name, username, MAXKTCNAMELEN - 1); strcpy(aclient.instance, ""); strncpy(aclient.cell, realm_of_user, MAXKTCREALMLEN - 1); #ifndef AFS_AIX51_ENV /* on AIX 4.1.4 with AFS 3.4a+ if a write is not done before * this routine, it will not add the token. It is not clear what * is going on here! So we will do the following operation. * On AIX 5 this kills our parent. So we won't. */ write(2,"",0); /* dummy write */ #endif afssetpag = (getpagvalue("afs") > 0) ? 1 : 0; if (uid == 0) { struct sigaction newAction, origAction; pid_t cid, pcid; int wstatus; sigemptyset(&newAction.sa_mask); newAction.sa_handler = SIG_DFL; newAction.sa_flags = 0; status = sigaction(SIGCHLD, &newAction, &origAction); if (status) { syslog(LOG_AUTH|LOG_ERR, "LAM aklog: sigaction returned %d", status); return AUTH_FAILURE; } syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: in daemon? forking to set tokens"); cid = fork(); if (cid <= 0) { syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog child: setting tokens"); setuid(pwd->pw_uid); status = ktc_SetToken(&aserver, &atoken, &aclient, afssetpag); if (status != 0) syslog(LOG_AUTH|LOG_ERR, "LAM aklog child: set tokens, returning %d", status); exit((status == 0)?0:255); } else { do { pcid = waitpid(cid, &wstatus, 0); } while ((pcid == -1) && (errno == EINTR)); if ((pcid == cid) && WIFEXITED(wstatus)) status = WEXITSTATUS(wstatus); else status = -1; } syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: collected child status %d", status); sigaction(SIGCHLD, &origAction, NULL); } else { status = ktc_SetToken(&aserver, &atoken, &aclient, afssetpag); } if (status != 0) syslog(LOG_AUTH|LOG_ERR, "LAM aklog: set tokens returned %d", status); else syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: set tokens, pag %d", getpagvalue("afs")); return(status); }
static int CommandProc(struct cmd_syndesc *as, void *arock) { krb5_principal princ = 0; char *cell, *pname, **hrealms, *service; char service_temp[MAXKTCREALMLEN + 20]; krb5_creds incred[1], mcred[1], *outcred = 0, *afscred; krb5_ccache cc = 0; #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC krb5_get_init_creds_opt *gic_opts; #else krb5_get_init_creds_opt gic_opts[1]; #endif char *tofree = NULL, *outname; int code; char *what; int i, dosetpag, evil, noprdb, id; #ifdef AFS_RXK5 int authtype; #endif krb5_data enc_part[1]; krb5_prompter_fct pf = NULL; char *pass = 0; void *pa = 0; struct kp_arg klog_arg[1]; char passwd[BUFSIZ]; struct afsconf_cell cellconfig[1]; static char rn[] = "klog"; /*Routine name */ static int Pipe = 0; /* reading from a pipe */ static int Silent = 0; /* Don't want error messages */ int writeTicketFile = 0; /* write ticket file to /tmp */ service = 0; memset(incred, 0, sizeof *incred); /* blow away command line arguments */ for (i = 1; i < zero_argc; i++) memset(zero_argv[i], 0, strlen(zero_argv[i])); zero_argc = 0; memset(klog_arg, 0, sizeof *klog_arg); /* first determine quiet flag based on -silent switch */ Silent = (as->parms[aSILENT].items ? 1 : 0); if (Silent) { afs_set_com_err_hook(silent_errors); } if ((code = krb5_init_context(&k5context))) { afs_com_err(rn, code, "while initializing Kerberos 5 library"); KLOGEXIT(code); } if ((code = rx_Init(0))) { afs_com_err(rn, code, "while initializing rx"); KLOGEXIT(code); } initialize_U_error_table(); /*initialize_krb5_error_table();*/ initialize_RXK_error_table(); initialize_KTC_error_table(); initialize_ACFG_error_table(); /* initialize_rx_error_table(); */ if (!(tdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH))) { afs_com_err(rn, 0, "can't get afs configuration (afsconf_Open(%s))", AFSDIR_CLIENT_ETC_DIRPATH); KLOGEXIT(1); } /* * Enable DES enctypes, which are currently still required for AFS. * krb5_allow_weak_crypto is MIT Kerberos 1.8. krb5_enctype_enable is * Heimdal. */ #if defined(HAVE_KRB5_ENCTYPE_ENABLE) i = krb5_enctype_valid(k5context, ETYPE_DES_CBC_CRC); if (i) krb5_enctype_enable(k5context, ETYPE_DES_CBC_CRC); #elif defined(HAVE_KRB5_ALLOW_WEAK_CRYPTO) krb5_allow_weak_crypto(k5context, 1); #endif /* Parse remaining arguments. */ dosetpag = !! as->parms[aSETPAG].items; Pipe = !! as->parms[aPIPE].items; writeTicketFile = !! as->parms[aTMP].items; noprdb = !! as->parms[aNOPRDB].items; evil = (always_evil&1) || !! as->parms[aUNWRAP].items; #ifdef AFS_RXK5 authtype = 0; if (as->parms[aK5].items) authtype |= FORCE_RXK5; if (as->parms[aK4].items) authtype |= FORCE_RXKAD; if (!authtype) authtype |= env_afs_rxk5_default(); #endif cell = as->parms[aCELL].items ? as->parms[aCELL].items->data : 0; if ((code = afsconf_GetCellInfo(tdir, cell, "afsprot", cellconfig))) { if (cell) afs_com_err(rn, code, "Can't get cell information for '%s'", cell); else afs_com_err(rn, code, "Can't get determine local cell!"); KLOGEXIT(code); } if (as->parms[aKRBREALM].items) { code = krb5_set_default_realm(k5context, as->parms[aKRBREALM].items->data); if (code) { afs_com_err(rn, code, "Can't make <%s> the default realm", as->parms[aKRBREALM].items->data); KLOGEXIT(code); } } else if ((code = krb5_get_host_realm(k5context, cellconfig->hostName[0], &hrealms))) { afs_com_err(rn, code, "Can't get realm for host <%s> in cell <%s>\n", cellconfig->hostName[0], cellconfig->name); KLOGEXIT(code); } else { if (hrealms && *hrealms) { code = krb5_set_default_realm(k5context, *hrealms); if (code) { afs_com_err(rn, code, "Can't make <%s> the default realm", *hrealms); KLOGEXIT(code); } } if (hrealms) krb5_free_host_realm(k5context, hrealms); } id = getuid(); if (as->parms[aPRINCIPAL].items) { pname = as->parms[aPRINCIPAL].items->data; } else { /* No explicit name provided: use Unix uid. */ struct passwd *pw; pw = getpwuid(id); if (pw == 0) { afs_com_err(rn, 0, "Can't figure out your name from your user id (%d).", id); if (!Silent) fprintf(stderr, "%s: Try providing the user name.\n", rn); KLOGEXIT(1); } pname = pw->pw_name; } code = krb5_parse_name(k5context, pname, &princ); if (code) { afs_com_err(rn, code, "Can't parse principal <%s>", pname); KLOGEXIT(code); } if (as->parms[aPASSWORD].items) { /* * Current argument is the desired password string. Remember it in * our local buffer, and zero out the argument string - anyone can * see it there with ps! */ strncpy(passwd, as->parms[aPASSWORD].items->data, sizeof(passwd)); memset(as->parms[aPASSWORD].items->data, 0, strlen(as->parms[aPASSWORD].items->data)); pass = passwd; } /* Get the password if it wasn't provided. */ if (!pass) { if (Pipe) { strncpy(passwd, getpipepass(), sizeof(passwd)); pass = passwd; } else { pf = klog_prompter; pa = klog_arg; } } service = 0; #ifdef AFS_RXK5 if (authtype & FORCE_RXK5) { tofree = get_afs_krb5_svc_princ(cellconfig); snprintf(service_temp, sizeof service_temp, "%s", tofree); } else #endif snprintf (service_temp, sizeof service_temp, "afs/%s", cellconfig->name); klog_arg->pp = &pass; klog_arg->pstore = passwd; klog_arg->allocated = sizeof(passwd); /* XXX should allow k5 to prompt in most cases -- what about expired pw?*/ #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC code = krb5_get_init_creds_opt_alloc(k5context, &gic_opts); if (code) { afs_com_err(rn, code, "Can't allocate get_init_creds options"); KLOGEXIT(code); } #else krb5_get_init_creds_opt_init(gic_opts); #endif for (;;) { code = krb5_get_init_creds_password(k5context, incred, princ, pass, pf, /* prompter */ pa, /* data */ 0, /* start_time */ 0, /* in_tkt_service */ gic_opts); if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) break; } memset(passwd, 0, sizeof(passwd)); if (code) { char *r = 0; if (krb5_get_default_realm(k5context, &r)) r = 0; if (r) afs_com_err(rn, code, "Unable to authenticate in realm %s", r); else afs_com_err(rn, code, "Unable to authenticate to use cell %s", cellconfig->name); if (r) free(r); KLOGEXIT(code); } for (;;writeTicketFile = 0) { if (writeTicketFile) { what = "getting default ccache"; code = krb5_cc_default(k5context, &cc); } else { what = "krb5_cc_resolve"; code = krb5_cc_resolve(k5context, "MEMORY:core", &cc); if (code) goto Failed; } what = "initializing ccache"; code = krb5_cc_initialize(k5context, cc, princ); if (code) goto Failed; what = "writing Kerberos ticket file"; code = krb5_cc_store_cred(k5context, cc, incred); if (code) goto Failed; if (writeTicketFile) fprintf(stderr, "Wrote ticket file to %s\n", krb5_cc_get_name(k5context, cc)); break; Failed: if (code) afs_com_err(rn, code, "%s", what); if (writeTicketFile) { if (cc) { krb5_cc_close(k5context, cc); cc = 0; } continue; } KLOGEXIT(code); } for (service = service_temp;;service = "afs") { memset(mcred, 0, sizeof *mcred); mcred->client = princ; code = krb5_parse_name(k5context, service, &mcred->server); if (code) { afs_com_err(rn, code, "Unable to parse service <%s>\n", service); KLOGEXIT(code); } if (tofree) { free(tofree); tofree = 0; } if (!(code = krb5_unparse_name(k5context, mcred->server, &outname))) tofree = outname; else outname = service; code = krb5_get_credentials(k5context, 0, cc, mcred, &outcred); krb5_free_principal(k5context, mcred->server); if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || service != service_temp) break; #ifdef AFS_RXK5 if (authtype & FORCE_RXK5) break; #endif } afscred = outcred; if (code) { afs_com_err(rn, code, "Unable to get credentials to use %s", outname); KLOGEXIT(code); } #ifdef AFS_RXK5 if (authtype & FORCE_RXK5) { struct ktc_principal aserver[1]; int viceid = 555; memset(aserver, 0, sizeof *aserver); strncpy(aserver->cell, cellconfig->name, MAXKTCREALMLEN-1); code = ktc_SetK5Token(k5context, aserver, afscred, viceid, dosetpag); if (code) { afs_com_err(rn, code, "Unable to store tokens for cell %s\n", cellconfig->name); KLOGEXIT(1); } } else #endif { struct ktc_principal aserver[1], aclient[1]; struct ktc_token atoken[1]; memset(atoken, 0, sizeof *atoken); if (evil) { size_t elen = enc_part->length; atoken->kvno = RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY; if (afs_krb5_skip_ticket_wrapper(afscred->ticket.data, afscred->ticket.length, (char **) &enc_part->data, &elen)) { afs_com_err(rn, 0, "Can't unwrap %s AFS credential", cellconfig->name); KLOGEXIT(1); } } else { atoken->kvno = RXKAD_TKT_TYPE_KERBEROS_V5; *enc_part = afscred->ticket; } atoken->startTime = afscred->times.starttime; atoken->endTime = afscred->times.endtime; if (tkt_DeriveDesKey(get_creds_enctype(afscred), get_cred_keydata(afscred), get_cred_keylen(afscred), &atoken->sessionKey)) { afs_com_err(rn, 0, "Cannot derive DES key from enctype %i of length %u", get_creds_enctype(afscred), (unsigned)get_cred_keylen(afscred)); KLOGEXIT(1); } memcpy(atoken->ticket, enc_part->data, atoken->ticketLen = enc_part->length); memset(aserver, 0, sizeof *aserver); strncpy(aserver->name, "afs", 4); strncpy(aserver->cell, cellconfig->name, MAXKTCREALMLEN-1); memset(aclient, 0, sizeof *aclient); i = realm_len(k5context, afscred->client); if (i > MAXKTCREALMLEN-1) i = MAXKTCREALMLEN-1; memcpy(aclient->cell, realm_data(k5context, afscred->client), i); if (!noprdb) { int viceid = 0; k5_to_k4_name(k5context, afscred->client, aclient); code = whoami(atoken, cellconfig, aclient, &viceid); if (code) { afs_com_err(rn, code, "Can't get your viceid for cell %s", cellconfig->name); *aclient->name = 0; } else snprintf(aclient->name, MAXKTCNAMELEN-1, "AFS ID %d", viceid); } if (!*aclient->name) k5_to_k4_name(k5context, afscred->client, aclient); code = ktc_SetToken(aserver, atoken, aclient, dosetpag); if (code) { afs_com_err(rn, code, "Unable to store tokens for cell %s\n", cellconfig->name); KLOGEXIT(1); } } krb5_free_principal(k5context, princ); krb5_free_cred_contents(k5context, incred); if (outcred) krb5_free_creds(k5context, outcred); if (cc) krb5_cc_close(k5context, cc); if (tofree) free(tofree); return 0; }