Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked

Change a user's Kerberos Password?

by 5mi11er (Deacon)
on Mar 20, 2009 at 22:22 UTC ( #752162=perlquestion: print w/replies, xml ) Need Help??
5mi11er has asked for the wisdom of the Perl Monks concerning the following question:

Last week I asked how to change an expired AD password with LDAP and Perl at this thread: 751018.

Turns out I was asking the wrong question, you MUST change an expired AD password with Kerberos.

So, now after looking at the Kerberos libraries on CPAN, it appears that changing a user's password is not one of the abilities that any of those libraries have. Which seems strange since Kerberos is specifically a secure password authentication system. Sure it does things with tickets to allow authenticated users access to resources, but to be unable to change a password? Seems to me like it would be something that would happen quite often.

Now, I'm new enough to kerberos where I may just be totally missing something that should be obvious, but I've looked at it several times over the past few days, and I still haven't seen that ability.

So, could someone who's versed in Kerberos shine some light on how I can allow a user to change their own password? Oh, I'm attempting to use apache/linux to do this, not windows, or I wouldn't be asking these questions.



Replies are listed 'Best First'.
Re: Change a user's Kerberos Password?
by ig (Vicar) on Mar 20, 2009 at 22:55 UTC

    The kpasswd program allows a user to change their Kerberos password and the PAM module pam_krb5 supports changing Kerberos passwords.

Re: Change a user's Kerberos Password?
by ig (Vicar) on Mar 22, 2009 at 01:56 UTC
Re: Change a user's Kerberos Password?
by ig (Vicar) on Mar 23, 2009 at 22:50 UTC

    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: oldpw newpw"; my $newpw = shift or die "USAGE: 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, ? : "" ); } return(6); } if ( != NULL) free(; if ( != NULL) free( +; return(0); }
      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.

Re: Change a user's Kerberos Password?
by 5mi11er (Deacon) on Jul 08, 2009 at 20:16 UTC
      Looking at the change_LDAP_password function in locksmith (at least in trunk) I notice you are hashing and replacing the attribute directly. It is probably easier/cleaner/better? to test for and use the ldap exop for password changes:

      $msg = $ldaph->set_password(user => $dn, newpasswd => $newpass, oldpasswd => $oldpass);

      This will let stuff like the openLdap smpk5pwd and ppolicy modules take care of other password related stuff like syncing NT/LANMAN passwords (needed for MSCHAP/eap/peap for wifi via radius) and password policy enforcement on the server. It also handles the encryption on the server with whatever standard the admin has setup in it there.

        Probably an excellent suggestion, but I haven't had to use that system in a while, and no longer have access to those systems, so I have no way of easily testing whether that would work or not.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://752162]
Approved by planetscape
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (3)
As of 2018-05-26 00:48 GMT
Find Nodes?
    Voting Booth?