#include "kauth.h"

RCSID("$Id: kauthd.c,v 1.6 1996/06/25 13:31:13 joda Exp $");

static char name[SNAME_SZ + 1];
static char inst[INST_SZ + 1];
static char realm[REALM_SZ + 1];
static char locuser[SNAME_SZ + 1];
static int  lifetime;
static char tktfile[MaxPathLen + 1];

struct remote_args {
     int sock;
     des_key_schedule *schedule;
     des_cblock *session;
     struct sockaddr_in *me, *her;
};

static int
decrypt_remote_tkt (char *user, char *inst, char *realm, void *varg,
		    key_proc_t key_proc, KTEXT *cipp)
{
     char buf[BUFSIZ];
     void *ptr;
     int len;
     KTEXT cip  = *cipp;
     struct remote_args *args = (struct remote_args *)varg;

     write_encrypted (args->sock, cip->dat, cip->length,
		      *args->schedule, args->session, args->me,
		      args->her);
     len = read_encrypted (args->sock, buf, sizeof(buf), &ptr, *args->schedule,
			   args->session, args->her, args->me);
     memcpy(cip->dat, ptr, cip->length);
	  
     return 0;
}

static int
doit(int sock)
{
     int status;
     KTEXT_ST ticket;
     AUTH_DAT auth;
     char instance[INST_SZ + 1];
     des_key_schedule schedule;
     struct sockaddr_in thisaddr, thataddr;
     int addrlen;
     int len;
     char buf[BUFSIZ];
     void *data;
     struct passwd *passwd;
     char version[KRB_SENDAUTH_VLEN];

     addrlen = sizeof(thisaddr);
     if (getsockname (sock, (struct sockaddr *)&thisaddr, &addrlen) < 0 ||
	 addrlen != sizeof(thisaddr)) {
	  return 1;
     }
     addrlen = sizeof(thataddr);
     if (getpeername (sock, (struct sockaddr *)&thataddr, &addrlen) < 0 ||
	 addrlen != sizeof(thataddr)) {
	  return 1;
     }

     k_getsockinst (sock, instance);
     status = krb_recvauth (KOPT_DO_MUTUAL, sock, &ticket, "rcmd", instance,
			    &thataddr, &thisaddr, &auth, "", schedule,
			    version);
     if (status != KSUCCESS ||
	 strncmp(version, "RKINIT.0", KRB_SENDAUTH_VLEN) != 0) {
	  return 1;
     }
     len = read_encrypted (sock, buf, sizeof(buf), &data, schedule,
			   &auth.session, &thataddr, &thisaddr);
     if (len < 0) {
	  write_encrypted (sock, "read_enc failed",
			   sizeof("read_enc failed") - 1, schedule,
			   &auth.session, &thisaddr, &thataddr);
	  return 1;
     }
     if (unpack_args(data, name, inst, realm, &lifetime, locuser,
		     tktfile)) {
	  write_encrypted (sock, "unpack_args failed",
			   sizeof("unpack_args failed") - 1, schedule,
			   &auth.session, &thisaddr, &thataddr);
	  return 1;
     }

     if( kuserok(&auth, locuser) != 0) {
	  sprintf(buf, "%s cannot get tickets for %s%s%s@%s",
		  locuser, name, *inst == '\0' ? "" : ".",
		  inst, realm);
	  write_encrypted (sock, buf, strlen(buf), schedule,
			   &auth.session, &thisaddr, &thataddr);
	  return 1;
     }
     passwd = k_getpwnam (locuser);
     if (passwd == NULL) {
	  sprintf (buf, "No user '%s'", locuser);
	  write_encrypted (sock, buf, strlen(buf), schedule,
			   &auth.session, &thisaddr, &thataddr);
	  return 1;
     }
     if (setgid (passwd->pw_gid) ||
	 initgroups(passwd->pw_name, passwd->pw_gid) ||
	 setuid(passwd->pw_uid)) {
	  sprintf (buf, "Could not change user");
	  write_encrypted (sock, buf, strlen(buf), schedule,
			   &auth.session, &thisaddr, &thataddr);
	  return 1;
     }
     write_encrypted (sock, "ok", sizeof("ok") - 1, schedule,
		      &auth.session, &thisaddr, &thataddr);

     if (*tktfile == 0)
	 sprintf(tktfile, "%s%d", TKT_ROOT, getuid());
     krb_set_tkt_string (tktfile);

     {
	  struct remote_args arg;

	  arg.sock     = sock;
	  arg.schedule = &schedule;
	  arg.session  = &auth.session;
	  arg.me       = &thisaddr;
	  arg.her      = &thataddr;
	  status = krb_get_in_tkt (name, inst, realm, "krbtgt", realm,
				   lifetime, NULL, decrypt_remote_tkt, &arg);
     }
     if (status == KSUCCESS) {
	  write_encrypted (sock, "ok", sizeof("ok") - 1, schedule,
			   &auth.session, &thisaddr, &thataddr);
	  return 0;
     } else {
	  sprintf (buf, "TGT failed: %s", krb_get_err_text(status));
	  write_encrypted (sock, buf, strlen(buf), schedule,
			   &auth.session, &thisaddr, &thataddr);
	  return 1;
     }
}

int
main (int argc, char **argv)
{
    int s = 0;
    if(argc > 1 && strcmp(argv[1], "-i") == 0){
	int s2;
	int one = 1;
	struct sockaddr_in sa;
	s2 = socket(AF_INET, SOCK_STREAM, 0);
	if(s2 < 0){
	    perror("socket");
	    exit(1);
	}
	memset(&sa, 0, sizeof(sa));
	sa.sin_family = AF_INET;
	sa.sin_port = k_getportbyname("kauth", "tcp", htons(2120));
	
	if(setsockopt(s2, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0){
	    perror("setsockopt");
	    exit(1);
	}

	if(bind(s2, (struct sockaddr*)&sa, sizeof(sa)) < 0){
	    perror("bind");
	    exit(1);
	}
	if(listen(s2, 5) < 0){
	    perror("listen");
	    exit(1);
	}
	s = accept(s2, NULL, 0);
	if(s < 0){
	    perror("accept");
	    exit(1);
	}
	close(s2);
    }
    return doit(s);
}
