#include "kadm_locl.h"
#include "ksrvutil.h"

RCSID("$Id: ksrvutil_get.c,v 1.17 1996/08/04 15:34:24 assar Exp $");

#define BAD_PW 1
#define GOOD_PW 0
#define FUDGE_VALUE 15		/* for ticket expiration time */
#define PE_NO 0
#define PE_YES 1
#define PE_UNSURE 2

static char tktstring[128];


static int
princ_exists(char *name, char *instance, char *realm)
{
    int status;

    status = krb_get_pw_in_tkt(name, instance, realm, "krbtgt", realm, 1, "");

    if ((status == KSUCCESS) || (status == INTK_BADPW))
	return(PE_YES);
    else if (status == KDC_PR_UNKNOWN)
	return(PE_NO);
    else
	return(PE_UNSURE);
}

static int
get_admin_password(char *myname, char *myinst, char *myrealm)
{
  int status;
  char admin_passwd[MAX_KPW_LEN];	/* Admin's password */
  int ticket_life = 1;	/* minimum ticket lifetime */
  char buf[1024];
  CREDENTIALS c;

  if (princ_exists(myname, myinst, myrealm) != PE_NO) {
    sprintf(buf, "Password for %s.%s@%s:", 
	    myname, myinst, myrealm);
    if (des_read_pw_string(admin_passwd, sizeof(admin_passwd)-1,
			    buf, 0)) {
      fprintf(stderr, "Error reading admin password.\n");
      goto bad;
    }
    status = krb_get_pw_in_tkt(myname, myinst, myrealm, PWSERV_NAME, 
			       KADM_SINST, ticket_life, admin_passwd);
    memset(admin_passwd, 0, sizeof(admin_passwd));
    
    /* Initialize non shared random sequence from session key. */
    memset(&c, 0, sizeof(c));
    krb_get_cred(PWSERV_NAME, KADM_SINST, myrealm, &c);
    des_init_random_number_generator(&c.session);
  }
  else
    status = KDC_PR_UNKNOWN;
  
  switch(status) {
  case GT_PW_OK:
    return(GOOD_PW);
  case KDC_PR_UNKNOWN:
    printf("Principal %s.%s@%s does not exist.\n", myname, myinst, myrealm);
    goto bad;
  case GT_PW_BADPW:
    printf("Incorrect admin password.\n");
    goto bad;
  default:
    com_err("kadmin", status+krb_err_base,
	    "while getting password tickets");
    goto bad;
  }
  
  bad:
  memset(admin_passwd, 0, sizeof(admin_passwd));
  (void) dest_tkt();
  return(BAD_PW);
}

static void
srvtab_put_key (int fd, char *filename, char *name, char *inst, char *realm,
		int8_t kvno, des_cblock key)
{
  char sname[ANAME_SZ];	/* name of service */
  char sinst[INST_SZ];	/* instance of service */
  char srealm[REALM_SZ];	/* realm of service */
  int8_t skvno;
  des_cblock skey;

  lseek(fd, 0, SEEK_SET);

  while(getst(fd, sname,  SNAME_SZ) > 0 &&
	getst(fd, sinst,  INST_SZ) > 0  &&
	getst(fd, srealm, REALM_SZ) > 0 &&
	read(fd, &skvno,  sizeof(skvno)) > 0 &&
	read(fd, skey,    sizeof(skey)) > 0) {
    if(strcmp(name,  sname)  == 0 &&
       strcmp(inst,  sinst)  == 0 &&
       strcmp(realm, srealm) == 0) {
      lseek(fd, lseek(fd,0,SEEK_CUR)-(sizeof(skvno) + sizeof(skey)), SEEK_SET);
      safe_write(filename, fd, &kvno, sizeof(kvno));
      safe_write(filename, fd, key,   sizeof(des_cblock));
      return;
    }
  }
  safe_write(filename, fd, name,  strlen(name) + 1);
  safe_write(filename, fd, inst,  strlen(inst) + 1);
  safe_write(filename, fd, realm, strlen(realm) + 1);
  safe_write(filename, fd, &kvno, sizeof(kvno));
  safe_write(filename, fd, key,   sizeof(des_cblock));
}

/* 
 * node list of services 
 */

struct srv_ent{
  char name[SNAME_SZ];
  char inst[INST_SZ];
  char realm[REALM_SZ];
  struct srv_ent *next;
};

static int
key_to_key(char *user, char *instance, char *realm, void *arg,
	   des_cblock *key)
{
  memcpy(key, arg, sizeof(des_cblock));
  return 0;
}

static void
get_srvtab_ent(int fd, char *filename, char *name, char *inst, char *realm)
{
  char chname[128];
  des_cblock newkey;
  char old_tktfile[MaxPathLen], new_tktfile[MaxPathLen];
  char garbage_name[ANAME_SZ];
  char garbage_inst[ANAME_SZ];
  CREDENTIALS c;
  u_int8_t kvno;
  Kadm_vals values;
  int ret;

  strncpy(chname, krb_get_phost(inst), sizeof(chname));
  if(strcmp(inst, chname))
    fprintf(stderr, 
	    "Warning: Are you sure `%s' should not be `%s'?\n",
	    inst, chname);
    
  memset(&values, 0, sizeof(values));
  strcpy(values.name, name);
  strcpy(values.instance, inst);
  (void) des_new_random_key(&newkey);
  values.key_low = (newkey[0] << 24) | (newkey[1] << 16)
    | (newkey[2] << 8) | (newkey[3] << 0);
  values.key_high = (newkey[4] << 24) | (newkey[5] << 16)
    | (newkey[6] << 8) | (newkey[7] << 0);

  SET_FIELD(KADM_NAME,values.fields);
  SET_FIELD(KADM_INST,values.fields);
  SET_FIELD(KADM_DESKEY,values.fields);

  ret = kadm_mod(&values, &values);
  if(ret == KADM_NOENTRY)
    ret = kadm_add(&values);
  if (ret != KSUCCESS) {
    fprintf(stderr, "%s: Couldn't get srvtab entry for %s.%s: %s\n",
	    progname, name, inst, error_message(ret));
    return;
  }
  
  values.key_low = values.key_high = 0;

  /* get the key version number */

  strcpy(old_tktfile, tkt_string());
  sprintf(new_tktfile, TKT_ROOT "_ksrvutil-get.%d", (int)getpid());
  krb_set_tkt_string(new_tktfile);

  ret = krb_get_in_tkt(name, inst, realm, name, inst,
		       1, key_to_key, NULL, &newkey);

  if (ret == KSUCCESS &&
      (ret = tf_init(tkt_string(), R_TKT_FIL)) == KSUCCESS &&
      (ret = tf_get_pname(garbage_name)) == KSUCCESS &&
      (ret = tf_get_pinst(garbage_inst)) == KSUCCESS &&
      (ret = tf_get_cred(&c)) == KSUCCESS)
    kvno = c.kvno;

  tf_close();
  krb_set_tkt_string(old_tktfile);
  unlink(new_tktfile);
    
  if(ret != KSUCCESS) {
    fprintf (stderr, "%s: Could not get a ticket for %s.%s@%s: %s\n",
	     progname, name, inst, realm, krb_get_err_text(ret));
    return;
  }

  /* Write the new key & c:o to the srvtab file */

  srvtab_put_key (fd, filename, name, inst, realm, kvno, newkey);
  memset(&newkey, 0, sizeof(newkey));

  fprintf (stderr, "Written %s.%s\n", name, inst);
}


static void
ksrvutil_kadm(int fd, char *filename, struct srv_ent *p)
{
  int ret;
  CREDENTIALS c;
  
  ret = kadm_init_link(PWSERV_NAME, KADM_SINST, u_realm);
  if (ret != KADM_SUCCESS) {
    fprintf(stderr, "%s: Couldn't initialize kadmin link: %s\n",
	    progname, krb_get_err_text(ret));
    leave(NULL, 1);
  }
  
  ret = krb_get_cred (PWSERV_NAME, KADM_SINST, u_realm, &c);
  if (ret == KSUCCESS)
    des_init_random_number_generator (&c.session);
  else {
    umask(077);
       
    /*
     *  create ticket file and get admin tickets
     */
    sprintf(tktstring, TKT_ROOT "_ksrvutil_%d", (int)getpid());
    krb_set_tkt_string(tktstring);
    destroyp = TRUE;
       
    ret = get_admin_password(u_name, u_inst, u_realm);
    if (ret) {
      fprintf(stderr, "%s: Couldn't get admin password.\n",
	      progname);
      leave(NULL, 1);
    }
  }  
  for(;p;){
    get_srvtab_ent(fd, filename, p->name, p->inst, p->realm);
    p=p->next;
  }
  unlink(tktstring);
}


void
ksrvutil_get(int fd, char *filename, int argc, char **argv)
{
  char sname[ANAME_SZ];		/* name of service */
  char sinst[INST_SZ];		/* instance of service */
  char srealm[REALM_SZ];	/* realm of service */
  char databuf[BUFSIZ];
  char local_realm[REALM_SZ];	/* local kerberos realm */
  char local_hostname[100];
  char prompt[100];
  struct srv_ent *head=NULL;
  int i;

  if (krb_get_lrealm(local_realm, 1) != KSUCCESS)
    strcpy(local_realm, KRB_REALM);
  k_gethostname(local_hostname, sizeof(local_hostname));
  strcpy(local_hostname, krb_get_phost(local_hostname));

  if (argc)
    for(i=0; i < argc; ++i) {
      struct srv_ent *p=malloc(sizeof(*p));

      if(p == NULL) {
	fprintf(stderr, "%s: out of memory in malloc\n", progname);
	leave(NULL,1);
      }
      strcpy(p->name, "rcmd");
      strcpy(p->inst, local_hostname);
      strcpy(p->realm, local_realm);
      p->next = head;
      if (kname_parse (p->name, p->inst, p->realm, argv[i]) !=
	  KSUCCESS) {
	fprintf (stderr, "%s: parse error on '%s'\n", progname,
		 argv[i]);
	free(p);
	continue;
      }
      head = p;
    }

  else
    do{
      safe_read_stdin("Name [rcmd]: ", databuf, sizeof(databuf));
      if(databuf[0])
	strncpy(sname, databuf, sizeof(sname)-1);
      else
	strcpy(sname, "rcmd");
    
      sprintf(prompt, "Instance [%s]: ", local_hostname);
      safe_read_stdin(prompt, databuf, sizeof(databuf));
      if (databuf[0])
	strncpy(sinst, databuf, sizeof(sinst) - 1);
      else
	strcpy(sinst, local_hostname);
    
      sprintf(prompt, "Realm [%s]: ", local_realm);
      safe_read_stdin(prompt, databuf, sizeof(databuf));
      if (databuf[0])
	strncpy(srealm, databuf, sizeof(srealm) - 1);
      else
	strcpy(srealm, local_realm);
      if(yn("Is this correct?")){
	struct srv_ent *p=(struct srv_ent*)malloc(sizeof(struct srv_ent));
	p->next=head;
	head=p;
	strcpy(p->name, sname);
	strcpy(p->inst, sinst);
	strcpy(p->realm, srealm);
      }
    }while(ny("Add more keys?"));
  
  
  ksrvutil_kadm(fd, filename, head);

  {
    struct srv_ent *p=head, *q;
    while(p){
      q=p;
      p=p->next;
      free(q);
    }
  }

}
