/*	$NetBSD: kadm_passwd.c,v 1.8 2000/02/14 04:44:10 aidan Exp $	*/

/*
 * Copyright 1988 by the Massachusetts Institute of Technology.
 *
 * For copying and distribution information, please see the file
 * Copyright.MIT.
 *
 * from FreeBSD: kpasswd.c,v 1.5 1995/12/23 22:02:13 ache Exp $
 * from eBones: kpasswd.c,v 4.3 89/09/26 09:33:02 jtkohl Exp $
 */

#ifndef	lint
static const char rcsid[] = "$NetBSD: kadm_passwd.c,v 1.8 2000/02/14 04:44:10 aidan Exp $";
#endif	lint

/*
 * kpasswd
 * change your password with kerberos
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <pwd.h>
#include <string.h>
#include <netinet/in.h>
#include <kerberosIV/des.h>
#include <kerberosIV/krb.h>
#include <kerberosIV/krb_db.h>
#include <kerberosIV/kadm.h>
#include <com_err.h>
#include <err.h>
#include <unistd.h>

#include "extern.h"

#if 0	/* XXX ??? */
static void go_home(char *, int);
#endif
void get_pw_new_key __P((des_cblock, char *, char *, char *, int));

static const char *iflag;
static const char *rflag;
static char uflag[ANAME_SZ + INST_SZ + REALM_SZ + 3];
static int force_krb4;

int krb4_chpw(const char *uname)
{
    char name[ANAME_SZ];	/* name of user */
    char inst[INST_SZ];		/* instance of user */
    char realm[REALM_SZ];	/* realm of user */
    char default_name[ANAME_SZ];
    char default_inst[INST_SZ];
    char default_realm[REALM_SZ];
    int realm_given = 0;	/* True if realm was give on cmdline */
    int use_default = 1;	/* True if we should use default name */
    struct passwd *pw;
    int status;			/* return code */
    des_cblock new_key;
    char tktstring[MAXPATHLEN];

#ifdef NOENCRYPTION
#define read_long_pw_string placebo_read_pw_string
#else
#define read_long_pw_string des_read_pw_string
#endif

    bzero(name, sizeof(name));
    bzero(inst, sizeof(inst));
    bzero(realm, sizeof(realm));

    if (krb_get_tf_fullname(TKT_FILE, default_name, default_inst,
			    default_realm) != KSUCCESS) {
	pw = getpwuid((int) getuid());
	if (pw) {
		strcpy(default_name, pw->pw_name);
	} else {
	    /* seems like a null name is kinda silly */
		strcpy(default_name, "");
	}
	strcpy(default_inst, "");
	if (krb_get_lrealm(default_realm, 1) != KSUCCESS)
	    strcpy(default_realm, KRB_REALM);
    }

    /* the uflag includes elements of uname, iflag, and rflag.
       passwd.c will print an error and exit if uflag && (iflag || rflag).
       here, we just ignore the other flags if uflag is set. */

    if(uflag[0]) {
	    if ((status = kname_parse(name, inst, realm, uflag)) != 0) {
		    errx(2, "Kerberos error: %s", krb_err_txt[status]);
	    }
	    if (realm[0])
		realm_given++;
	    else
		if (krb_get_lrealm(realm, 1) != KSUCCESS)
		    strcpy(realm, KRB_REALM);
    } else {
	    if(uname) {
		    strncpy(name, uname, sizeof(name) - 1);
		    if (! k_isname(name)) {
			    errx(1, "bad name: %s", uname);
		    }
	    }

	    if(iflag) {
		    strncpy(inst, iflag, sizeof(inst) - 1);
		    if (! k_isinst(inst)) {
			    errx(1, "bad instance: %s", iflag);
		    }
	    }

	    if(rflag) {
		    strncpy(realm, rflag, sizeof(realm) - 1);
		    realm_given++;
		    if (! k_isrealm(realm)) {
			    errx(1, "bad realm: %s", rflag);
		    }
	    }
    }

    if(uname || iflag || rflag || uflag[0]) use_default = 0;

    if (use_default) {
	strcpy(name, default_name);
	strcpy(inst, default_inst);
	strcpy(realm, default_realm);
    } else {
	if (!name[0])
	    strcpy(name, default_name);
	if (!realm[0])
	    strcpy(realm, default_realm);
    }

    (void) sprintf(tktstring, "/tmp/tkt_cpw_%d",getpid());
    krb_set_tkt_string(tktstring);

    get_pw_new_key(new_key, name, inst, realm, realm_given);

    if ((status = kadm_init_link("changepw", KRB_MASTER, realm))
	!= KADM_SUCCESS)
	com_err("kpasswd", status, "while initializing");
    else if ((status = kadm_change_pw(new_key)) != KADM_SUCCESS)
	com_err("kpasswd", status, " attempting to change password.");

    if (status != KADM_SUCCESS)
	warnx("Password NOT changed.");
    else
	printf("Password changed.\n");

    (void) dest_tkt();
    if (status)
	exit(2);
    else
	exit(0);
}

void get_pw_new_key(new_key, name, inst, realm, print_realm)
  des_cblock new_key;
  char *name;
  char *inst;
  char *realm;
  int print_realm;		/* True if realm was give on cmdline */
{
    char ppromp[40+ANAME_SZ+INST_SZ+REALM_SZ]; /* for the password prompt */
    char pword[MAX_KPW_LEN];	               /* storage for the password */
    char npromp[40+ANAME_SZ+INST_SZ+REALM_SZ]; /* for the password prompt */

    char local_realm[REALM_SZ];
    int status;

    /*
     * We don't care about failure; this is to determine whether or
     * not to print the realm in the prompt for a new password.
     */
    (void) krb_get_lrealm(local_realm, 1);

    if (strcmp(local_realm, realm))
	print_realm++;

    (void) sprintf(ppromp,"Old password for %s%s%s%s%s:",
		   name, *inst ? "." : "", inst,
		   print_realm ? "@" : "", print_realm ? realm : "");
    if (read_long_pw_string(pword, sizeof(pword)-1, ppromp, 0)) {
	errx(1, "Error reading old password.");
    }

    if ((status = krb_get_pw_in_tkt(name, inst, realm, PWSERV_NAME,
				    KADM_SINST, 1, pword)) != KSUCCESS) {
	if (status == INTK_BADPW) {
	    errx(0,"Incorrect old password.");
	}
	else {
	    errx(1, "Kerberos error: %s\n", krb_err_txt[status]);
	}
    }
    bzero(pword, sizeof(pword));
    do {
	(void) sprintf(npromp,"New Password for %s%s%s%s%s:",
		       name, *inst ? "." : "", inst,
		       print_realm ? "@" : "", print_realm ? realm : "");
	if (read_long_pw_string(pword, sizeof(pword)-1, npromp, 1)) {
	    (void) dest_tkt();
	    errx(1, "Error reading new password, password unchanged.");
	}
	if (strlen(pword) == 0)
	    printf("Null passwords are not allowed; try again.\n");
    } while (strlen(pword) == 0);

#ifdef NOENCRYPTION
    bzero((char *) new_key, sizeof(des_cblock));
    new_key[0] = (unsigned char) 1;
#else
    (void) des_string_to_key(pword, (des_cblock *)new_key);
#endif
    bzero(pword, sizeof(pword));
}

int krb4_arg_end()
{
    if (uflag[0] != '\0' && (iflag != 0 || rflag != 0))
	errx(1, "Can't use '-u' with '-i' or '-r'.");
    if (force_krb4)
	return (PW_USE_FORCE);
    return (PW_USE);
}

int krb4_arg(ch, arg)
    char ch;
    const char *arg;
{
    switch (ch) {
    case 'u':
	strncpy(uflag, arg, sizeof(uflag));
	uflag[sizeof(uflag)-1] = '\0';
	break;
    case 'i':
	iflag = arg;
	break;
    case 'r':
	rflag = arg;
	break;
    case 'k':
    case '4':
	force_krb4 = 1;
	break;
    default:
	return(0);
    }
    return(1);
}

void krb4_end()
{
    /* NOOP */
}

int krb4_init(progname)
    const char *progname;
{
    int kstatus;
    char realm[MAXHOSTNAMELEN];

    force_krb4 = 0;
    uflag[0] = '\0';
    iflag = rflag = 0;
    kstatus = krb_get_lrealm(realm, 1);
    if((kstatus != KSUCCESS) || (strcmp(realm, KRB_REALM) == 0))
	return(-1);
    return(0);
}

#if 0	/* XXX ??? */
static void
go_home(str,x)
  char *str;
  int x;
{
    fprintf(stderr, str, x);
    (void) dest_tkt();
    exit(1);
}
#endif
