17c478bd9Sstevel@tonic-gate /*
2159d09a2SMark Phalan * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
37c478bd9Sstevel@tonic-gate * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate */
57c478bd9Sstevel@tonic-gate
67c478bd9Sstevel@tonic-gate
77c478bd9Sstevel@tonic-gate /*
87c478bd9Sstevel@tonic-gate * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
97c478bd9Sstevel@tonic-gate *
107c478bd9Sstevel@tonic-gate * Openvision retains the copyright to derivative works of
117c478bd9Sstevel@tonic-gate * this source code. Do *NOT* create a derivative of this
127c478bd9Sstevel@tonic-gate * source code before consulting with your legal department.
137c478bd9Sstevel@tonic-gate * Do *NOT* integrate *ANY* of this source code into another
147c478bd9Sstevel@tonic-gate * product before consulting with your legal department.
157c478bd9Sstevel@tonic-gate *
167c478bd9Sstevel@tonic-gate * For further information, read the top-level Openvision
177c478bd9Sstevel@tonic-gate * copyright which is contained in the top-level MIT Kerberos
187c478bd9Sstevel@tonic-gate * copyright.
197c478bd9Sstevel@tonic-gate *
207c478bd9Sstevel@tonic-gate * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
217c478bd9Sstevel@tonic-gate *
227c478bd9Sstevel@tonic-gate */
237c478bd9Sstevel@tonic-gate
247c478bd9Sstevel@tonic-gate
257c478bd9Sstevel@tonic-gate /*
267c478bd9Sstevel@tonic-gate * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved.
27*55fea89dSDan Cross *
28159d09a2SMark Phalan * $Header$
297c478bd9Sstevel@tonic-gate *
307c478bd9Sstevel@tonic-gate *
317c478bd9Sstevel@tonic-gate */
327c478bd9Sstevel@tonic-gate
33159d09a2SMark Phalan static char rcsid[] = "$Id: kpasswd.c 17258 2005-06-21 01:36:03Z raeburn $";
347c478bd9Sstevel@tonic-gate
357c478bd9Sstevel@tonic-gate #include <kadm5/admin.h>
367c478bd9Sstevel@tonic-gate #include <krb5.h>
377c478bd9Sstevel@tonic-gate
387c478bd9Sstevel@tonic-gate #include "kpasswd_strings.h"
3956a424ccSmp #define string_text error_message
4056a424ccSmp
4156a424ccSmp #include "kpasswd.h"
427c478bd9Sstevel@tonic-gate
437c478bd9Sstevel@tonic-gate #include <stdio.h>
447c478bd9Sstevel@tonic-gate #include <pwd.h>
457c478bd9Sstevel@tonic-gate #include <string.h>
467c478bd9Sstevel@tonic-gate #include <libintl.h>
477c478bd9Sstevel@tonic-gate
487c478bd9Sstevel@tonic-gate extern char *whoami;
497c478bd9Sstevel@tonic-gate
507c478bd9Sstevel@tonic-gate extern void display_intro_message();
517c478bd9Sstevel@tonic-gate extern long read_old_password();
527c478bd9Sstevel@tonic-gate extern long read_new_password();
537c478bd9Sstevel@tonic-gate
5456a424ccSmp #define MISC_EXIT_STATUS 6
557c478bd9Sstevel@tonic-gate
567c478bd9Sstevel@tonic-gate /*
577c478bd9Sstevel@tonic-gate * Function: kpasswd
587c478bd9Sstevel@tonic-gate *
597c478bd9Sstevel@tonic-gate * Purpose: Initialize and call lower level routines to change a password
607c478bd9Sstevel@tonic-gate *
617c478bd9Sstevel@tonic-gate * Arguments:
627c478bd9Sstevel@tonic-gate *
637c478bd9Sstevel@tonic-gate * context (r) krb5_context to use
647c478bd9Sstevel@tonic-gate * argc/argv (r) principal name to use, optional
657c478bd9Sstevel@tonic-gate * read_old_password (f) function to read old password
667c478bd9Sstevel@tonic-gate * read_new_password (f) function to read new and change password
677c478bd9Sstevel@tonic-gate * display_intro_message (f) function to display intro message
687c478bd9Sstevel@tonic-gate * whoami (extern) argv[0]
69*55fea89dSDan Cross *
707c478bd9Sstevel@tonic-gate * Returns:
717c478bd9Sstevel@tonic-gate * exit status of 0 for success
727c478bd9Sstevel@tonic-gate * 1 principal unknown
737c478bd9Sstevel@tonic-gate * 2 old password wrong
747c478bd9Sstevel@tonic-gate * 3 cannot initialize admin server session
757c478bd9Sstevel@tonic-gate * 4 new passwd mismatch or error trying to change pw
767c478bd9Sstevel@tonic-gate * 5 password not typed
777c478bd9Sstevel@tonic-gate * 6 misc error
787c478bd9Sstevel@tonic-gate * 7 incorrect usage
79*55fea89dSDan Cross *
807c478bd9Sstevel@tonic-gate * Requires:
817c478bd9Sstevel@tonic-gate * Passwords cannot be more than 255 characters long.
82*55fea89dSDan Cross *
837c478bd9Sstevel@tonic-gate * Effects:
847c478bd9Sstevel@tonic-gate *
857c478bd9Sstevel@tonic-gate * If argc is 2, the password for the principal specified in argv[1]
867c478bd9Sstevel@tonic-gate * is changed; otherwise, the principal of the default credential
877c478bd9Sstevel@tonic-gate * cache or username is used. display_intro_message is called with
887c478bd9Sstevel@tonic-gate * the arguments KPW_STR_CHANGING_PW_FOR and the principal name.
897c478bd9Sstevel@tonic-gate * read_old_password is then called to prompt for the old password.
907c478bd9Sstevel@tonic-gate * The admin system is then initialized, the principal's policy
917c478bd9Sstevel@tonic-gate * retrieved and explained, if appropriate, and finally
927c478bd9Sstevel@tonic-gate * read_new_password is called to read the new password and change the
937c478bd9Sstevel@tonic-gate * principal's password (presumably ovsec_kadm_chpass_principal).
947c478bd9Sstevel@tonic-gate * admin system is de-initialized before the function returns.
95*55fea89dSDan Cross *
967c478bd9Sstevel@tonic-gate * Modifies:
977c478bd9Sstevel@tonic-gate *
987c478bd9Sstevel@tonic-gate * Changes the principal's password.
997c478bd9Sstevel@tonic-gate *
1007c478bd9Sstevel@tonic-gate */
1017c478bd9Sstevel@tonic-gate int
kpasswd(context,argc,argv)1027c478bd9Sstevel@tonic-gate kpasswd(context, argc, argv)
10356a424ccSmp krb5_context context;
10456a424ccSmp int argc;
10556a424ccSmp char *argv[];
1067c478bd9Sstevel@tonic-gate {
10756a424ccSmp kadm5_ret_t code;
10856a424ccSmp krb5_ccache ccache = NULL;
10956a424ccSmp krb5_principal princ = 0;
11056a424ccSmp char *princ_str;
11156a424ccSmp struct passwd *pw = 0;
11256a424ccSmp unsigned int pwsize;
11356a424ccSmp char password[255]; /* I don't really like 255 but that's what kinit uses */
11456a424ccSmp char msg_ret[1024], admin_realm[1024];
11556a424ccSmp kadm5_principal_ent_rec principal_entry;
11656a424ccSmp kadm5_policy_ent_rec policy_entry;
11756a424ccSmp void *server_handle;
11856a424ccSmp kadm5_config_params params;
11956a424ccSmp char *cpw_service;
1207c478bd9Sstevel@tonic-gate
1217c478bd9Sstevel@tonic-gate memset((char *)¶ms, 0, sizeof (params));
1227c478bd9Sstevel@tonic-gate memset(&principal_entry, 0, sizeof (principal_entry));
1237c478bd9Sstevel@tonic-gate memset(&policy_entry, 0, sizeof (policy_entry));
1247c478bd9Sstevel@tonic-gate
12556a424ccSmp if (argc > 2) {
12656a424ccSmp com_err(whoami, KPW_STR_USAGE, 0);
12756a424ccSmp return(7);
12856a424ccSmp /*NOTREACHED*/
12956a424ccSmp }
1307c478bd9Sstevel@tonic-gate
13156a424ccSmp /************************************
132*55fea89dSDan Cross * Get principal name to change *
13356a424ccSmp ************************************/
1347c478bd9Sstevel@tonic-gate
13556a424ccSmp /* Look on the command line first, followed by the default credential
13656a424ccSmp cache, followed by defaulting to the Unix user name */
13756a424ccSmp
13856a424ccSmp if (argc == 2)
13956a424ccSmp princ_str = strdup(argv[1]);
14056a424ccSmp else {
14156a424ccSmp code = krb5_cc_default(context, &ccache);
14256a424ccSmp /* If we succeed, find who is in the credential cache */
14356a424ccSmp if (code == 0) {
14456a424ccSmp /* Get default principal from cache if one exists */
14556a424ccSmp code = krb5_cc_get_principal(context, ccache, &princ);
14656a424ccSmp /* if we got a principal, unparse it, otherwise get out of the if
14756a424ccSmp with an error code */
14856a424ccSmp (void) krb5_cc_close(context, ccache);
14956a424ccSmp if (code == 0) {
15056a424ccSmp code = krb5_unparse_name(context, princ, &princ_str);
15156a424ccSmp if (code != 0) {
15256a424ccSmp com_err(whoami, code, string_text(KPW_STR_UNPARSE_NAME));
15356a424ccSmp return(MISC_EXIT_STATUS);
1547c478bd9Sstevel@tonic-gate }
15556a424ccSmp }
15656a424ccSmp }
1577c478bd9Sstevel@tonic-gate
15856a424ccSmp /* this is a crock.. we want to compare against */
15956a424ccSmp /* "KRB5_CC_DOESNOTEXIST" but there is no such error code, and */
16056a424ccSmp /* both the file and stdio types return FCC_NOFILE. If there is */
16156a424ccSmp /* ever another ccache type (or if the error codes are ever */
16256a424ccSmp /* fixed), this code will have to be updated. */
16356a424ccSmp if (code && code != KRB5_FCC_NOFILE) {
16456a424ccSmp com_err(whoami, code, string_text(KPW_STR_WHILE_LOOKING_AT_CC));
16556a424ccSmp return(MISC_EXIT_STATUS);
16656a424ccSmp }
1677c478bd9Sstevel@tonic-gate
16856a424ccSmp /* if either krb5_cc failed check the passwd file */
16956a424ccSmp if (code != 0) {
17056a424ccSmp pw = getpwuid( getuid());
17156a424ccSmp if (pw == NULL) {
17256a424ccSmp com_err(whoami, 0, string_text(KPW_STR_NOT_IN_PASSWD_FILE));
17356a424ccSmp return(MISC_EXIT_STATUS);
17456a424ccSmp }
17556a424ccSmp princ_str = strdup(pw->pw_name);
17656a424ccSmp }
177*55fea89dSDan Cross }
178*55fea89dSDan Cross
17956a424ccSmp display_intro_message(string_text(KPW_STR_CHANGING_PW_FOR), princ_str);
1807c478bd9Sstevel@tonic-gate
18156a424ccSmp /* Need to get a krb5_principal, unless we started from with one from
18256a424ccSmp the credential cache */
1837c478bd9Sstevel@tonic-gate
18456a424ccSmp if (! princ) {
18556a424ccSmp code = krb5_parse_name (context, princ_str, &princ);
18656a424ccSmp if (code != 0) {
18756a424ccSmp com_err(whoami, code, string_text(KPW_STR_PARSE_NAME), princ_str);
18856a424ccSmp free(princ_str);
18956a424ccSmp return(MISC_EXIT_STATUS);
19056a424ccSmp }
19156a424ccSmp }
192*55fea89dSDan Cross
19356a424ccSmp pwsize = sizeof(password);
19456a424ccSmp code = read_old_password(context, password, &pwsize);
19556a424ccSmp
19656a424ccSmp if (code != 0) {
19756a424ccSmp memset(password, 0, sizeof(password));
19856a424ccSmp com_err(whoami, code, string_text(KPW_STR_WHILE_READING_PASSWORD));
19956a424ccSmp krb5_free_principal(context, princ);
20056a424ccSmp free(princ_str);
20156a424ccSmp return(MISC_EXIT_STATUS);
20256a424ccSmp }
20356a424ccSmp if (pwsize == 0) {
20456a424ccSmp memset(password, 0, sizeof(password));
20556a424ccSmp com_err(whoami, 0, string_text(KPW_STR_NO_PASSWORD_READ));
20656a424ccSmp krb5_free_principal(context, princ);
20756a424ccSmp free(princ_str);
20856a424ccSmp return(5);
20956a424ccSmp }
2107c478bd9Sstevel@tonic-gate
2117c478bd9Sstevel@tonic-gate snprintf(admin_realm, sizeof (admin_realm),
2127c478bd9Sstevel@tonic-gate krb5_princ_realm(context, princ)->data);
2137c478bd9Sstevel@tonic-gate params.mask |= KADM5_CONFIG_REALM;
2147c478bd9Sstevel@tonic-gate params.realm = admin_realm;
2157c478bd9Sstevel@tonic-gate
2167c478bd9Sstevel@tonic-gate
2177c478bd9Sstevel@tonic-gate if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) {
2187c478bd9Sstevel@tonic-gate fprintf(stderr, gettext("%s: unable to get host based "
2197c478bd9Sstevel@tonic-gate "service name for realm %s\n"),
2207c478bd9Sstevel@tonic-gate whoami, admin_realm);
2217c478bd9Sstevel@tonic-gate exit(1);
2227c478bd9Sstevel@tonic-gate }
2237c478bd9Sstevel@tonic-gate
2247c478bd9Sstevel@tonic-gate code = kadm5_init_with_password(princ_str, password, cpw_service,
2257c478bd9Sstevel@tonic-gate ¶ms, KADM5_STRUCT_VERSION,
22654925bf6Swillf KADM5_API_VERSION_2, NULL,
22754925bf6Swillf &server_handle);
2287c478bd9Sstevel@tonic-gate free(cpw_service);
2297c478bd9Sstevel@tonic-gate if (code != 0) {
2307c478bd9Sstevel@tonic-gate if (code == KADM5_BAD_PASSWORD)
2317c478bd9Sstevel@tonic-gate com_err(whoami, 0,
2327c478bd9Sstevel@tonic-gate string_text(KPW_STR_OLD_PASSWORD_INCORRECT));
2337c478bd9Sstevel@tonic-gate else
2347c478bd9Sstevel@tonic-gate com_err(whoami, 0,
2357c478bd9Sstevel@tonic-gate string_text(KPW_STR_CANT_OPEN_ADMIN_SERVER),
2367c478bd9Sstevel@tonic-gate admin_realm,
2377c478bd9Sstevel@tonic-gate error_message(code));
2387c478bd9Sstevel@tonic-gate krb5_free_principal(context, princ);
2397c478bd9Sstevel@tonic-gate free(princ_str);
2407c478bd9Sstevel@tonic-gate return ((code == KADM5_BAD_PASSWORD) ? 2 : 3);
2417c478bd9Sstevel@tonic-gate }
2427c478bd9Sstevel@tonic-gate
2437c478bd9Sstevel@tonic-gate /*
2447c478bd9Sstevel@tonic-gate * we can only check the policy if the server speaks
2457c478bd9Sstevel@tonic-gate * RPCSEC_GSS
2467c478bd9Sstevel@tonic-gate */
2477c478bd9Sstevel@tonic-gate if (_kadm5_get_kpasswd_protocol(server_handle) == KRB5_CHGPWD_RPCSEC) {
2487c478bd9Sstevel@tonic-gate /* Explain policy restrictions on new password if any. */
2497c478bd9Sstevel@tonic-gate /*
2507c478bd9Sstevel@tonic-gate * Note: copy of this exists in login
2517c478bd9Sstevel@tonic-gate * (kverify.c/get_verified_in_tkt).
2527c478bd9Sstevel@tonic-gate */
2537c478bd9Sstevel@tonic-gate
2547c478bd9Sstevel@tonic-gate code = kadm5_get_principal(server_handle, princ,
2557c478bd9Sstevel@tonic-gate &principal_entry,
2567c478bd9Sstevel@tonic-gate KADM5_PRINCIPAL_NORMAL_MASK);
2577c478bd9Sstevel@tonic-gate if (code != 0) {
2587c478bd9Sstevel@tonic-gate com_err(whoami, 0,
2597c478bd9Sstevel@tonic-gate string_text((code == KADM5_UNK_PRINC)
2607c478bd9Sstevel@tonic-gate ? KPW_STR_PRIN_UNKNOWN :
2617c478bd9Sstevel@tonic-gate KPW_STR_CANT_GET_POLICY_INFO),
2627c478bd9Sstevel@tonic-gate princ_str);
2637c478bd9Sstevel@tonic-gate krb5_free_principal(context, princ);
2647c478bd9Sstevel@tonic-gate free(princ_str);
2657c478bd9Sstevel@tonic-gate (void) kadm5_destroy(server_handle);
2667c478bd9Sstevel@tonic-gate return ((code == KADM5_UNK_PRINC) ? 1 :
2677c478bd9Sstevel@tonic-gate MISC_EXIT_STATUS);
2687c478bd9Sstevel@tonic-gate }
2697c478bd9Sstevel@tonic-gate if ((principal_entry.aux_attributes & KADM5_POLICY) != 0) {
2707c478bd9Sstevel@tonic-gate code = kadm5_get_policy(server_handle,
2717c478bd9Sstevel@tonic-gate principal_entry.policy,
2727c478bd9Sstevel@tonic-gate &policy_entry);
2737c478bd9Sstevel@tonic-gate if (code != 0) {
2747c478bd9Sstevel@tonic-gate /*
2757c478bd9Sstevel@tonic-gate * doesn't matter which error comes back,
2767c478bd9Sstevel@tonic-gate * there's no nice recovery or need to
2777c478bd9Sstevel@tonic-gate * differentiate to the user
2787c478bd9Sstevel@tonic-gate */
2797c478bd9Sstevel@tonic-gate com_err(whoami, 0,
2807c478bd9Sstevel@tonic-gate string_text(KPW_STR_CANT_GET_POLICY_INFO),
2817c478bd9Sstevel@tonic-gate princ_str);
2827c478bd9Sstevel@tonic-gate (void) kadm5_free_principal_ent(server_handle,
2837c478bd9Sstevel@tonic-gate &principal_entry);
2847c478bd9Sstevel@tonic-gate krb5_free_principal(context, princ);
2857c478bd9Sstevel@tonic-gate free(princ_str);
2867c478bd9Sstevel@tonic-gate free(princ_str);
2877c478bd9Sstevel@tonic-gate (void) kadm5_destroy(server_handle);
2887c478bd9Sstevel@tonic-gate return (MISC_EXIT_STATUS);
2897c478bd9Sstevel@tonic-gate }
2907c478bd9Sstevel@tonic-gate com_err(whoami, 0,
2917c478bd9Sstevel@tonic-gate string_text(KPW_STR_POLICY_EXPLANATION),
2927c478bd9Sstevel@tonic-gate princ_str, principal_entry.policy,
2937c478bd9Sstevel@tonic-gate policy_entry.pw_min_length,
2947c478bd9Sstevel@tonic-gate policy_entry.pw_min_classes);
2957c478bd9Sstevel@tonic-gate if (code = kadm5_free_principal_ent(server_handle,
2967c478bd9Sstevel@tonic-gate &principal_entry)) {
2977c478bd9Sstevel@tonic-gate (void) kadm5_free_policy_ent(server_handle,
2987c478bd9Sstevel@tonic-gate &policy_entry);
2997c478bd9Sstevel@tonic-gate krb5_free_principal(context, princ);
3007c478bd9Sstevel@tonic-gate free(princ_str);
3017c478bd9Sstevel@tonic-gate com_err(whoami, code,
3027c478bd9Sstevel@tonic-gate string_text(KPW_STR_WHILE_FREEING_PRINCIPAL));
3037c478bd9Sstevel@tonic-gate (void) kadm5_destroy(server_handle);
3047c478bd9Sstevel@tonic-gate return (MISC_EXIT_STATUS);
3057c478bd9Sstevel@tonic-gate }
3067c478bd9Sstevel@tonic-gate if (code = kadm5_free_policy_ent(server_handle,
3077c478bd9Sstevel@tonic-gate &policy_entry)) {
3087c478bd9Sstevel@tonic-gate krb5_free_principal(context, princ);
3097c478bd9Sstevel@tonic-gate free(princ_str);
3107c478bd9Sstevel@tonic-gate com_err(whoami, code,
3117c478bd9Sstevel@tonic-gate string_text(KPW_STR_WHILE_FREEING_POLICY));
3127c478bd9Sstevel@tonic-gate (void) kadm5_destroy(server_handle);
3137c478bd9Sstevel@tonic-gate return (MISC_EXIT_STATUS);
3147c478bd9Sstevel@tonic-gate }
3157c478bd9Sstevel@tonic-gate } else {
3167c478bd9Sstevel@tonic-gate /*
3177c478bd9Sstevel@tonic-gate * kpasswd *COULD* output something here to
3187c478bd9Sstevel@tonic-gate * encourage the choice of good passwords,
3197c478bd9Sstevel@tonic-gate * in the absence of an enforced policy.
3207c478bd9Sstevel@tonic-gate */
3217c478bd9Sstevel@tonic-gate if (code = kadm5_free_principal_ent(server_handle,
3227c478bd9Sstevel@tonic-gate &principal_entry)) {
3237c478bd9Sstevel@tonic-gate krb5_free_principal(context, princ);
3247c478bd9Sstevel@tonic-gate free(princ_str);
3257c478bd9Sstevel@tonic-gate com_err(whoami, code,
3267c478bd9Sstevel@tonic-gate string_text(KPW_STR_WHILE_FREEING_PRINCIPAL));
3277c478bd9Sstevel@tonic-gate (void) kadm5_destroy(server_handle);
3287c478bd9Sstevel@tonic-gate return (MISC_EXIT_STATUS);
3297c478bd9Sstevel@tonic-gate }
3307c478bd9Sstevel@tonic-gate }
3317c478bd9Sstevel@tonic-gate } /* if protocol == KRB5_CHGPWD_RPCSEC */
3327c478bd9Sstevel@tonic-gate
33356a424ccSmp pwsize = sizeof(password);
33456a424ccSmp code = read_new_password(server_handle, password, &pwsize, msg_ret, sizeof (msg_ret), princ);
33556a424ccSmp memset(password, 0, sizeof(password));
3367c478bd9Sstevel@tonic-gate
33756a424ccSmp if (code)
33856a424ccSmp com_err(whoami, 0, msg_ret);
3397c478bd9Sstevel@tonic-gate
34056a424ccSmp krb5_free_principal(context, princ);
34156a424ccSmp free(princ_str);
3427c478bd9Sstevel@tonic-gate
34356a424ccSmp (void) kadm5_destroy(server_handle);
344*55fea89dSDan Cross
34556a424ccSmp if (code == KRB5_LIBOS_CANTREADPWD)
34656a424ccSmp return(5);
34756a424ccSmp else if (code)
34856a424ccSmp return(4);
34956a424ccSmp else
35056a424ccSmp return(0);
3517c478bd9Sstevel@tonic-gate }
352