/*
 * Copyright 1994 University of Wisconsin-Madison
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of the University of Wisconsin-Madison not
 * be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.  The University of
 * Wisconsin-Madison makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 *
 * THE UNIVERSITY OF WISCONSIN-MADISON DISCLAIMS ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF WISCONSIN-MADISON BE LIABLE FOR
 * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *           Tim Theisen           Associate Researcher
 * Internet: tim@cs.wisc.edu       Department of Computer Sciences
 *     UUCP: uwvax!tim             University of Wisconsin-Madison
 *    Phone: (608)262-0438         1210 West Dayton Street
 *      FAX: (608)262-9777         Madison, WI   53706-1685
 */

#include <stdio.h>
#include <kerberos/com_err.h>

#include <krb5/krb5.h>
#include <krb5/ext-proto.h>
#include <krb5/los-proto.h>

#ifndef KRB5_TGS_NAME
#define KRB5_TGS_NAME		"krbtgt"
#define KRB5_TGS_NAME_SIZE	6
#endif

krb5_data tgtname = {
    KRB5_TGS_NAME_SIZE,
    KRB5_TGS_NAME
};

/*
 * Try no preauthentication first; then try the encrypted timestamp
 */
int preauth_search_list[] = {
    0,                      
    KRB5_PADATA_ENC_TIMESTAMP,
    -1
    };

/*
 * Initialize krb5 credential cache.
 * Returns 0 on success, otherwise an error code.
 * Prints to standard all problems except unknown principal or bad password.
 */
int
#ifdef __STDC__
k5login(char *cache_name, char *login, char *password,
	long lifetime, long rlife)
#else
k5login(cache_name, login, password, lifetime, rlife)
    char *cache_name;		/* "FILE:/tmp/krb5_pidXXXXX", for example. */
				/* NULL uses the default: "/tmp/krb5cc_uid" */
    char *login;
    char *password;
    long lifetime;		/* in seconds; 0 means max lifetime */
    long rlife;			/* in seconds; 0 means max renewable lifetime */
#endif
{
    char *client_name;
    krb5_address **my_addresses;
    krb5_error_code code;
    krb5_ccache ccache;
    krb5_principal me;
    krb5_principal server;
    krb5_creds my_creds;
    krb5_timestamp now;
    int i;
    int options = KDC_OPT_RENEWABLE|KDC_OPT_FORWARDABLE;

    if (cache_name) {
	if (code = krb5_cc_resolve(cache_name, &ccache)) {
	    com_err("k5login", code, "while getting named cache");
	    return code;
	}
    } else {
	if (code = krb5_cc_default(&ccache)) {
	    com_err("k5login", code, "while getting default cache");
	    return code;
	}
    }
    if (code = krb5_parse_name(login, &me)) {
	com_err ("k5login", code, "when parsing name %s", login);
	(void) krb5_cc_destroy(ccache);
	return code;
    }
    if (code = krb5_unparse_name(me, &client_name)) {
	com_err ("k5login", code, "when unparsing name");
	(void) krb5_cc_destroy(ccache);
	return code;
    }
    if (code = krb5_cc_initialize (ccache, me)) {
	com_err ("k5login", code, "when initializing cache");
	(void) krb5_cc_destroy(ccache);
	return code;
    }
    memset((char *)&my_creds, 0, sizeof(my_creds));
    my_creds.client = me;
    if (code = krb5_build_principal_ext(&server,
					krb5_princ_realm(me)->length,
					krb5_princ_realm(me)->data,
					tgtname.length, tgtname.data,
					krb5_princ_realm(me)->length,
					krb5_princ_realm(me)->data,
					0)) {
	com_err("k5login", code, "while building server name");
	(void) krb5_cc_destroy(ccache);
	return code;
    }
    my_creds.server = server;
    if (code = krb5_os_localaddr(&my_addresses)) {
	com_err ("k5login", code, "when getting my address");
	(void) krb5_cc_destroy(ccache);
	return code;
    }
    if (code = krb5_timeofday(&now)) {
	com_err("k5login", code, "while getting time of day");
	(void) krb5_cc_destroy(ccache);
	return code;
    }
    my_creds.times.starttime = 0;	/* start timer when request
					   gets to KDC */
    my_creds.times.endtime = lifetime ? now + lifetime : 0;
    my_creds.times.renew_till = rlife ? now + rlife : 0;
    for (i=0; preauth_search_list[i] >= 0; i++) {
	code = krb5_get_in_tkt_with_password(options, my_addresses,
					     preauth_search_list[i],
					     ETYPE_DES_CBC_CRC,
					     KEYTYPE_DES,
					     password,
					     ccache,
					     &my_creds, 0);
	if (code != KRB5KDC_PREAUTH_FAILED &&
	    code != KRB5KRB_ERR_GENERIC)
	    break;
    }
    krb5_free_principal(server);
    krb5_free_addresses(my_addresses);
    if (code) {
#if 0
	if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
	    fprintf (stderr, "%s: Password incorrect\n", "k5login");
	} else {
	    com_err ("k5login", code, "while getting initial credentials");
	}
#endif
	(void) krb5_cc_destroy(ccache);
	return code;
    }
    return code;
}
