in reply to Change a user's Kerberos Password?

I looked at several of the krb5 related modules and it appears that none of them provide a means for a user to change their own password.

The following demonstrates that it is possible to change an AD password from linux using the krb5 library. This code is crude and minimally tested - you should use it with caution except as a basis for discovery.

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Inline ( C => 'DATA', LIBS => '-lkrb5', ); my $oldpw = shift or die "USAGE: test.pl oldpw newpw"; my $newpw = shift or die "USAGE: test.pl oldpw newpw"; my $ret = change_password($oldpw, $newpw); $oldpw = $newpw = ""; undef($oldpw); undef($newpw); print "change_password returned $ret\n"; print " :" . change_password_error($ret) . "\n"; __DATA__ __C__ #include <stdio.h> #include <krb5.h> #include <et/com_err.h> static krb5_error_code error_code = 0; static char error_text[1024] = ""; char *change_password_error(int error_number) { static char buf[1024]; char *text[] = { /* 0 */ "success", /* 1 */ "krb5_init_context failed", /* 2 */ "krb5_parse_name failed", /* 3 */ "krb5_get_init_creds_opt_alloc failed", /* 4 */ "krb5_get_init_creds failed", /* 5 */ "krb5_change_password failed", /* 6 */ "krb5_change_password failed", }; if(error_number > 6) { sprintf(buf, "Unknown error code: %d", error_number); } else { if(error_code) { sprintf(buf, "%s: %s", text[error_number], error_message(error_code) ); } else if(strlen(error_text)) { sprintf(buf, "%s: %s", text[error_number], error_text ); } else { sprintf(buf, "%s", text[error_number]); } } return(buf); } int change_password(char* oldpw, char* newpw) { krb5_context context; krb5_principal princ; krb5_get_init_creds_opt *opts = NULL; krb5_creds creds; int result_code; krb5_data result_code_string; krb5_data result_string; error_code = krb5_init_context(&context); if ( error_code ) return(1); error_code = krb5_parse_name(context, "billyb", &princ); if ( error_code ) return(2); error_code = krb5_get_init_creds_password( context, &creds, princ, oldpw, NULL, NULL, 0, "kadmin/changepw", NULL ); if ( error_code ) return(4); error_code = krb5_change_password(context, &creds, newpw, &result_code, &result_code_string, &result_string); if ( error_code ) return(5); if(result_code) { if( result_code_string.length + result_string.length + 5 > sizeof(error_text) ) { sprintf(error_text, "buffer overrun"); } else { printf("setting error_text\n"); sprintf(error_text, "%.*s%s%.*s", (int) result_code_string.length, result_code_string.da +ta, result_string.length?": ":"", (int) result_string.length, result_string.data ? result_string.data : "" ); } return(6); } if (result_string.data != NULL) free(result_string.data); if (result_code_string.data != NULL) free(result_code_string.data) +; return(0); }

Replies are listed 'Best First'.
Re^2: Change a user's Kerberos Password?
by 5mi11er (Deacon) on Mar 26, 2009 at 13:52 UTC
    Ah, I'd not remembered you could inline C like that. I had similar C code from some other project I'd found on the 'net, but I wasn't looking forward to figuring out all the error checking I'd need between that executable and perl. Using this, I wouldn't have to worry about it as much.

    But, I had continued my quest for a pure perl solution while waiting for replies to my question. I sent some email to one of the authors of the Kerberos modules, Jeff Horwitz, he sent the following code as a quick example of how he allows users to change their password. He states in his email:

    here's some patched together code that should do the trick (insert your own username, password, and error checking):
    use Authen::Krb5; use Authen::Krb5::Admin; Authen::Krb5::init_context(); my $kadm = Authen::Krb5::Admin->init_with_password($user, $pw); my $princ = Authen::Krb5::parse_name($user); my $rc = $kadm->chpass_principal($princ, $pw);
    Being part of the "Admin" package, I had assumed that the chpass_principal method needed administrative access rights, apparently that assumption was incorrect. I've not yet tried it, but have no reason to believe it won't work. When I get a chance to try it out, hopefully by the end of today, I'll report back.

    Update: Sorry for the delay, I had other projects that kept me from testing this until late last Friday. This doesn't work on an expired password as I need. The init_with_password method returns an error saying the password has expired, thus the $kadm object is invalid, and the chpass_principal method can not be called. Looks like its' back to the inline C code... -Scott

      Does anyone know how to to get Authen::Krb5::Admin->init_with_password to work with something besides the default realm?

      I tried:

      my $MSAD_HOST = "AD domain controller"; my $MSAD_DOMAIN = "AD domain" Authen::Krb5::init_context() or die $@; my $krb5conf = Authen::Krb5::Admin::Config->new(); $krb5conf->admin_server($MSAD_HOST); $krb5conf->realm($MSAD_DOMAIN); my $kadm5 = Authen::Krb5::Admin->init_with_password($user, $oldpw, KADM5_CHANGEPW_SERVICE, $krb5conf) or die $@;

      The above always dies whe I try the init_with_password().

      I can change the password using kpasswd:

      $ kpasswd sptester@DOMAIN.NET

      So I am guessing there is something wrong with my syntax.

        Well, what I did, because I needed to talk to two different AD domains, was to create two krb5.conf files.

        Example: Two companies, two domains, call them ZAY and BXC. First configure your krb5.conf file for connecting to the ZAY domain. Once you're able to kinit and net join to that domain, copy the krb5.conf to zay-krb5.conf.

        Next, configure the krb5.conf file to connect to the BXC domain. Once you're able to kinit and net join to that domain, copy the krb5.conf to bxc-krb5.conf.

        Once you know which domain you want to talk to, lets assume zay for this example, do this:

        export KRB5_CONFIG='zay-krb5.conf'; perl <script name>
        -Scott