389 lines
8.9 KiB
C
389 lines
8.9 KiB
C
/*
|
|
* $Id$
|
|
* $Source: /home/ncvs/src/eBones/libexec/rkinitd/krb.c,v $
|
|
* $Author: jkh $
|
|
*
|
|
* This file contains all of the kerberos part of rkinitd.
|
|
*/
|
|
|
|
#if !defined(lint) && !defined(SABER) && !defined(LOCORE) && defined(RCS_HDRS)
|
|
static char *rcsid = "$Id$";
|
|
#endif /* lint || SABER || LOCORE || RCS_HDRS */
|
|
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <errno.h>
|
|
#include <syslog.h>
|
|
#include <netinet/in.h>
|
|
#include <setjmp.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <pwd.h>
|
|
#include <krb.h>
|
|
#include <des.h>
|
|
|
|
#include <rkinit.h>
|
|
#include <rkinit_private.h>
|
|
#include <rkinit_err.h>
|
|
|
|
#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 <sys/id.h>
|
|
|
|
/*
|
|
* 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);
|
|
}
|