/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include <locale.h>
#include <stdio.h>
#include <sys/types.h>
#include "k5-platform.h"

#ifndef _WIN32
#include <unistd.h>
#endif

#include <krb5.h>

#define P1 _("Enter new password")
#define P2 _("Enter it again")

#ifdef HAVE_PWD_H
#include <pwd.h>

static
void get_name_from_passwd_file(program_name, kcontext, me)
    char * program_name;
    krb5_context kcontext;
    krb5_principal * me;
{
    struct passwd *pw;
    krb5_error_code code;
    if ((pw = getpwuid(getuid()))) {
        if ((code = krb5_parse_name(kcontext, pw->pw_name, me))) {
            com_err(program_name, code, _("when parsing name %s"),
                    pw->pw_name);
            exit(1);
        }
    } else {
        fprintf(stderr, _("Unable to identify user from password file\n"));
        exit(1);
    }
}
#else /* HAVE_PWD_H */
void get_name_from_passwd_file(kcontext, me)
    krb5_context kcontext;
    krb5_principal * me;
{
    fprintf(stderr, _("Unable to identify user\n"));
    exit(1);
}
#endif /* HAVE_PWD_H */

int main(int argc, char *argv[])
{
    krb5_error_code ret;
    krb5_context context;
    krb5_principal princ = NULL;
    char *pname;
    krb5_ccache ccache;
    krb5_get_init_creds_opt *opts = NULL;
    krb5_creds creds;
    char *message;

    char pw[1024];
    unsigned int pwlen;
    int result_code;
    krb5_data result_code_string, result_string;

    setlocale(LC_ALL, "");
    if (argc > 2) {
        fprintf(stderr, _("usage: %s [principal]\n"), argv[0]);
        exit(1);
    }

    pname = argv[1];

    ret = krb5_init_context(&context);
    if (ret) {
        com_err(argv[0], ret, _("initializing kerberos library"));
        exit(1);
    }
    if ((ret = krb5_get_init_creds_opt_alloc(context, &opts))) {
        com_err(argv[0], ret, _("allocating krb5_get_init_creds_opt"));
        exit(1);
    }

    /* in order, use the first of:
       - a name specified on the command line
       - the principal name from an existing ccache
       - the name corresponding to the ruid of the process

       otherwise, it's an error.
       We always attempt to open the default ccache in order to use FAST if
       possible.
    */
    ret = krb5_cc_default(context, &ccache);
    if (ret != 0) {
        com_err(argv[0], ret, _("opening default ccache"));
        exit(1);
    }
    ret = krb5_cc_get_principal(context, ccache, &princ);
    if (ret != 0 && ret != KRB5_CC_NOTFOUND && ret != KRB5_FCC_NOFILE) {
        com_err(argv[0], ret, _("getting principal from ccache"));
        exit(1);
    } else {
        if (princ != NULL) {
            ret = krb5_get_init_creds_opt_set_fast_ccache(context, opts,
                                                          ccache);
            if (ret) {
                com_err(argv[0], ret, _("while setting FAST ccache"));
                exit(1);
            }
        }
    }
    ret = krb5_cc_close(context, ccache);
    if (ret != 0) {
        com_err(argv[0], ret, _("closing ccache"));
        exit(1);
    }
    if (pname) {
        krb5_free_principal(context, princ);
        princ = NULL;
        if ((ret = krb5_parse_name(context, pname, &princ))) {
            com_err(argv[0], ret, _("parsing client name"));
            exit(1);
        }
    }
    if (princ == NULL)
        get_name_from_passwd_file(argv[0], context, &princ);

    krb5_get_init_creds_opt_set_tkt_life(opts, 5*60);
    krb5_get_init_creds_opt_set_renew_life(opts, 0);
    krb5_get_init_creds_opt_set_forwardable(opts, 0);
    krb5_get_init_creds_opt_set_proxiable(opts, 0);

    if ((ret = krb5_get_init_creds_password(context, &creds, princ, NULL,
                                            krb5_prompter_posix, NULL,
                                            0, "kadmin/changepw", opts))) {
        if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY)
            com_err(argv[0], 0,
                    _("Password incorrect while getting initial ticket"));
        else
            com_err(argv[0], ret, _("getting initial ticket"));
        krb5_get_init_creds_opt_free(context, opts);
        exit(1);
    }

    pwlen = sizeof(pw);
    if ((ret = krb5_read_password(context, P1, P2, pw, &pwlen))) {
        com_err(argv[0], ret, _("while reading password"));
        krb5_get_init_creds_opt_free(context, opts);
        exit(1);
    }

    if ((ret = krb5_change_password(context, &creds, pw,
                                    &result_code, &result_code_string,
                                    &result_string))) {
        com_err(argv[0], ret, _("changing password"));
        krb5_get_init_creds_opt_free(context, opts);
        exit(1);
    }

    if (result_code) {
        if (krb5_chpw_message(context, &result_string, &message) != 0)
            message = NULL;
        printf("%.*s%s%s\n",
               (int) result_code_string.length, result_code_string.data,
               message ? ": " : "", message ? message : NULL);
        krb5_free_string(context, message);
        krb5_get_init_creds_opt_free(context, opts);
        exit(2);
    }

    if (result_string.data != NULL)
        free(result_string.data);
    if (result_code_string.data != NULL)
        free(result_code_string.data);
    krb5_get_init_creds_opt_free(context, opts);

    printf(_("Password changed.\n"));
    exit(0);
}
