/* * $Id: krb.c,v 1.2 1995/09/16 14:10:13 mark Exp $ * $Source: /home/cvs.crypt/src/eBones/libexec/rkinitd/krb.c,v $ * $Author: mark $ * * This file contains all of the kerberos part of rkinitd. */ #if !defined(lint) && !defined(SABER) && !defined(LOCORE) && defined(RCS_HDRS) static char *rcsid = "$Id: krb.c,v 1.2 1995/09/16 14:10:13 mark Exp $"; #endif /* lint || SABER || LOCORE || RCS_HDRS */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rkinitd.h" #define FAILURE (!RKINIT_SUCCESS) extern int errno; static char errbuf[BUFSIZ]; typedef struct { jmp_buf env; } rkinitd_intkt_info; #if defined(_AIX) && defined(_IBMR2) #include /* * The RIOS has bizzarre ideas about changing uids around. They are * such that the seteuid and setruid calls here fail. For this reason * we are replacing the seteuid and setruid calls. * * The bizzarre ideas are as follows: * * The effective ID may be changed only to the current real or * saved IDs. * * The saved uid may be set only if the real and effective * uids are being set to the same value. * * The real uid may be set only if the effective * uid is being set to the same value. */ #ifdef __STDC__ static int setruid(uid_t ruid) #else static int setruid(ruid) uid_t ruid; #endif /* __STDC__ */ { uid_t euid; euid = geteuid(); if (setuidx(ID_REAL | ID_EFFECTIVE, ruid) == -1) return (-1); return (setuidx(ID_EFFECTIVE, euid)); } #ifdef __STDC__ static int seteuid(uid_t euid) #else static int seteuid(euid) uid_t euid; #endif /* __STDC__ */ { uid_t ruid; ruid = getuid(); if (setuidx(ID_SAVED | ID_REAL | ID_EFFECTIVE, euid) == -1) return (-1); return (setruid(ruid)); } #ifdef __STDC__ static int setreuid(uid_t ruid, uid_t euid) #else static int setreuid(ruid, euid) uid_t ruid; uid_t euid; #endif /* __STDC__ */ { if (seteuid(euid) == -1) return (-1); return (setruid(ruid)); } #ifdef __STDC__ static int setuid(uid_t uid) #else static int setuid(uid) uid_t uid; #endif /* __STDC__ */ { return (setreuid(uid, uid)); } #endif /* RIOS */ #ifdef __STDC__ static void this_phost(char *host, int hostlen) #else static void this_phost(host, hostlen) char *host; int hostlen; #endif /* __STDC__ */ { char this_host[MAXHOSTNAMELEN + 1]; BCLEAR(this_host); if (gethostname(this_host, sizeof(this_host)) < 0) { sprintf(errbuf, "gethostname: %s", sys_errlist[errno]); rkinit_errmsg(errbuf); error(); exit(1); } strncpy(host, krb_get_phost(this_host), hostlen - 1); } #ifdef __STDC__ static int decrypt_tkt(char *user, char *instance, char *realm, char *arg, int (*key_proc)(), KTEXT *cipp) #else static int decrypt_tkt(user, instance, realm, arg, key_proc, cipp) char *user; char *instance; char *realm; char *arg; int (*key_proc)(); KTEXT *cipp; #endif /* __STDC__ */ { MSG_DAT msg_data; /* Message data containing decrypted data */ KTEXT_ST auth; /* Authenticator */ AUTH_DAT auth_dat; /* Authentication data */ KTEXT cip = *cipp; MSG_DAT scip; int status = 0; des_cblock key; des_key_schedule sched; char phost[MAXHOSTNAMELEN + 1]; struct sockaddr_in caddr; /* client internet address */ struct sockaddr_in saddr; /* server internet address */ rkinitd_intkt_info *rii = (rkinitd_intkt_info *)arg; u_char enc_data[MAX_KTXT_LEN]; SBCLEAR(auth); SBCLEAR(auth_dat); SBCLEAR(scip); BCLEAR(enc_data); scip.app_data = enc_data; /* * Exchange with the client our response from the KDC (ticket encrypted * in user's private key) for the same ticket encrypted in our * (not yet known) session key. */ rpc_exchange_tkt(cip, &scip); /* * Get the authenticator */ SBCLEAR(auth); rpc_getauth(&auth, &caddr, &saddr); /* * Decode authenticator and extract session key. The first zero * means we don't care what host this comes from. This needs to * be done with euid of root so that /etc/srvtab can be read. */ BCLEAR(phost); this_phost(phost, sizeof(phost)); /* * This function has to use longjmp to return to the caller * because the kerberos library routine that calls it doesn't * pay attention to the return value it gives. That means that * if any of these routines failed, the error returned to the client * would be "password incorrect". */ if ((status = krb_rd_req(&auth, KEY, phost, caddr.sin_addr.s_addr, &auth_dat, KEYFILE))) { sprintf(errbuf, "krb_rd_req: %s", krb_err_txt[status]); rkinit_errmsg(errbuf); longjmp(rii->env, status); } bcopy(auth_dat.session, key, sizeof(key)); if (des_key_sched(&key, sched)) { sprintf(errbuf, "Error in des_key_sched"); rkinit_errmsg(errbuf); longjmp(rii->env, RKINIT_DES); } /* Decrypt the data. */ if ((status = krb_rd_priv((u_char *)scip.app_data, scip.app_length, sched, key, &caddr, &saddr, &msg_data)) == KSUCCESS) { cip->length = msg_data.app_length; bcopy(msg_data.app_data, cip->dat, msg_data.app_length); cip->dat[cip->length] = 0; } else { sprintf(errbuf, "krb_rd_priv: %s", krb_err_txt[status]); rkinit_errmsg(errbuf); longjmp(rii->env, status); } return(status); } #ifdef __STDC__ static int validate_user(char *aname, char *inst, char *realm, char *username, char *errmsg) #else static int validate_user(aname, inst, realm, username, errmsg) char *aname; char *inst; char *realm; char *username; char *errmsg; #endif /* __STDC__ */ { struct passwd *pwnam; /* For access_check and uid */ AUTH_DAT auth_dat; int kstatus = KSUCCESS; SBCLEAR(auth_dat); if ((pwnam = getpwnam(username)) == NULL) { sprintf(errmsg, "%s does not exist on the remote host.", username); return(FAILURE); } strcpy(auth_dat.pname, aname); strcpy(auth_dat.pinst, inst); strcpy(auth_dat.prealm, realm); if (seteuid(pwnam->pw_uid) < 0) { sprintf(errmsg, "Failure setting euid to %d: %s\n", pwnam->pw_uid, sys_errlist[errno]); strcpy(errbuf, errmsg); error(); return(FAILURE); } kstatus = kuserok(&auth_dat, username); if (seteuid(0) < 0) { sprintf(errmsg, "Failure setting euid to 0: %s\n", sys_errlist[errno]); strcpy(errbuf, errmsg); error(); return(FAILURE); } if (kstatus != KSUCCESS) { sprintf(errmsg, "%s has not allowed you to log in with", username); if (strlen(auth_dat.pinst)) sprintf(errmsg, "%s %s.%s", errmsg, auth_dat.pname, auth_dat.pinst); else sprintf(errmsg, "%s %s", errmsg, auth_dat.pname); sprintf(errmsg, "%s@%s tickets.", errmsg, auth_dat.prealm); return(FAILURE); } /* * Set real uid to owner of ticket file. The library takes care * of making the appropriate change. */ if (setruid(pwnam->pw_uid) < 0) { sprintf(errmsg, "Failure setting ruid to %d: %s\n", pwnam->pw_uid, sys_errlist[errno]); strcpy(errbuf, errmsg); error(); return(FAILURE); } return(RKINIT_SUCCESS); } #ifdef __STDC__ int get_tickets(int version) #else int get_tickets(version) int version; #endif /* __STDC__ */ { rkinit_info info; AUTH_DAT auth_dat; int status; char errmsg[BUFSIZ]; /* error message for client */ rkinitd_intkt_info rii; SBCLEAR(info); SBCLEAR(auth_dat); BCLEAR(errmsg); SBCLEAR(rii); rpc_get_rkinit_info(&info); /* * The validate_user routine makes sure that the principal in question * is allowed to log in as username, and if so, does a setuid(localuid). * If there is an access violation or an error in setting the uid, * an error is returned and the string errmsg is initialized with * an error message that will be sent back to the client. */ if ((status = validate_user(info.aname, info.inst, info.realm, info.username, errmsg)) != RKINIT_SUCCESS) { rpc_send_error(errmsg); exit(0); } else rpc_send_success(); /* * If the name of a ticket file was specified, set it; otherwise, * just use the default. */ if (strlen(info.tktfilename)) krb_set_tkt_string(info.tktfilename); /* * Call internal kerberos library routine so that we can supply * our own ticket decryption routine. */ /* * We need a setjmp here because krb_get_in_tkt ignores the * return value of decrypt_tkt. Thus if we want any of its * return values to reach the client, we have to jump out of * the routine. */ if (setjmp(rii.env) == 0) { if ((status = krb_get_in_tkt(info.aname, info.inst, info.realm, info.sname, info.sinst, info.lifetime, NULL, decrypt_tkt, (char *)&rii))) { strcpy(errmsg, krb_err_txt[status]); rpc_send_error(errmsg); } else rpc_send_success(); } else rpc_send_error(errbuf); return(RKINIT_SUCCESS); }