Home | History | Annotate | Download | only in kpasswd
      1 /*
      2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 
      7 /*
      8  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
      9  *
     10  *	Openvision retains the copyright to derivative works of
     11  *	this source code.  Do *NOT* create a derivative of this
     12  *	source code before consulting with your legal department.
     13  *	Do *NOT* integrate *ANY* of this source code into another
     14  *	product before consulting with your legal department.
     15  *
     16  *	For further information, read the top-level Openvision
     17  *	copyright which is contained in the top-level MIT Kerberos
     18  *	copyright.
     19  *
     20  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
     21  *
     22  */
     23 
     24 
     25 /*
     26  * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved.
     27  *
     28  * $Header$
     29  *
     30  *
     31  */
     32 
     33 static char rcsid[] = "$Id: kpasswd.c 17258 2005-06-21 01:36:03Z raeburn $";
     34 
     35 #include <kadm5/admin.h>
     36 #include <krb5.h>
     37 
     38 #include "kpasswd_strings.h"
     39 #define string_text error_message
     40 
     41 #include "kpasswd.h"
     42 
     43 #include <stdio.h>
     44 #include <pwd.h>
     45 #include <string.h>
     46 #include <libintl.h>
     47 
     48 extern char *whoami;
     49 
     50 extern void display_intro_message();
     51 extern long read_old_password();
     52 extern long read_new_password();
     53 
     54 #define MISC_EXIT_STATUS 6
     55 
     56 /*
     57  * Function: kpasswd
     58  *
     59  * Purpose: Initialize and call lower level routines to change a password
     60  *
     61  * Arguments:
     62  *
     63  *	context		(r) krb5_context to use
     64  *	argc/argv	(r) principal name to use, optional
     65  *	read_old_password (f) function to read old password
     66  *	read_new_password (f) function to read new and change password
     67  *	display_intro_message (f) function to display intro message
     68  *	whoami		(extern) argv[0]
     69  *
     70  * Returns:
     71  *                      exit status of 0 for success
     72  *			1 principal unknown
     73  *			2 old password wrong
     74  *			3 cannot initialize admin server session
     75  *			4 new passwd mismatch or error trying to change pw
     76  *                      5 password not typed
     77  *                      6 misc error
     78  *                      7 incorrect usage
     79  *
     80  * Requires:
     81  *	Passwords cannot be more than 255 characters long.
     82  *
     83  * Effects:
     84  *
     85  * If argc is 2, the password for the principal specified in argv[1]
     86  * is changed; otherwise, the principal of the default credential
     87  * cache or username is used.  display_intro_message is called with
     88  * the arguments KPW_STR_CHANGING_PW_FOR and the principal name.
     89  * read_old_password is then called to prompt for the old password.
     90  * The admin system is then initialized, the principal's policy
     91  * retrieved and explained, if appropriate, and finally
     92  * read_new_password is called to read the new password and change the
     93  * principal's password (presumably ovsec_kadm_chpass_principal).
     94  * admin system is de-initialized before the function returns.
     95  *
     96  * Modifies:
     97  *
     98  * Changes the principal's password.
     99  *
    100  */
    101 int
    102 kpasswd(context, argc, argv)
    103    krb5_context context;
    104    int argc;
    105    char *argv[];
    106 {
    107   kadm5_ret_t code;
    108   krb5_ccache ccache = NULL;
    109   krb5_principal princ = 0;
    110   char *princ_str;
    111   struct passwd *pw = 0;
    112   unsigned int pwsize;
    113   char password[255];  /* I don't really like 255 but that's what kinit uses */
    114   char msg_ret[1024], admin_realm[1024];
    115   kadm5_principal_ent_rec principal_entry;
    116   kadm5_policy_ent_rec policy_entry;
    117   void *server_handle;
    118   kadm5_config_params params;
    119   char *cpw_service;
    120 
    121 	memset((char *)&params, 0, sizeof (params));
    122 	memset(&principal_entry, 0, sizeof (principal_entry));
    123 	memset(&policy_entry, 0, sizeof (policy_entry));
    124 
    125   if (argc > 2) {
    126       com_err(whoami, KPW_STR_USAGE, 0);
    127       return(7);
    128       /*NOTREACHED*/
    129     }
    130 
    131   /************************************
    132    *  Get principal name to change    *
    133    ************************************/
    134 
    135   /* Look on the command line first, followed by the default credential
    136      cache, followed by defaulting to the Unix user name */
    137 
    138   if (argc == 2)
    139     princ_str = strdup(argv[1]);
    140   else {
    141     code = krb5_cc_default(context, &ccache);
    142     /* If we succeed, find who is in the credential cache */
    143     if (code == 0) {
    144       /* Get default principal from cache if one exists */
    145       code = krb5_cc_get_principal(context, ccache, &princ);
    146       /* if we got a principal, unparse it, otherwise get out of the if
    147 	 with an error code */
    148       (void) krb5_cc_close(context, ccache);
    149       if (code == 0) {
    150 	code = krb5_unparse_name(context, princ, &princ_str);
    151 	if (code != 0) {
    152 	  com_err(whoami,  code, string_text(KPW_STR_UNPARSE_NAME));
    153 	  return(MISC_EXIT_STATUS);
    154 	}
    155       }
    156     }
    157 
    158     /* this is a crock.. we want to compare against */
    159     /* "KRB5_CC_DOESNOTEXIST" but there is no such error code, and */
    160     /* both the file and stdio types return FCC_NOFILE.  If there is */
    161     /* ever another ccache type (or if the error codes are ever */
    162     /* fixed), this code will have to be updated. */
    163     if (code && code != KRB5_FCC_NOFILE) {
    164       com_err(whoami, code, string_text(KPW_STR_WHILE_LOOKING_AT_CC));
    165       return(MISC_EXIT_STATUS);
    166     }
    167 
    168     /* if either krb5_cc failed check the passwd file */
    169     if (code != 0) {
    170       pw = getpwuid( getuid());
    171       if (pw == NULL) {
    172 	com_err(whoami, 0, string_text(KPW_STR_NOT_IN_PASSWD_FILE));
    173 	return(MISC_EXIT_STATUS);
    174       }
    175       princ_str = strdup(pw->pw_name);
    176     }
    177   }
    178 
    179   display_intro_message(string_text(KPW_STR_CHANGING_PW_FOR), princ_str);
    180 
    181   /* Need to get a krb5_principal, unless we started from with one from
    182      the credential cache */
    183 
    184   if (! princ) {
    185       code = krb5_parse_name (context, princ_str, &princ);
    186       if (code != 0) {
    187 	  com_err(whoami, code, string_text(KPW_STR_PARSE_NAME), princ_str);
    188 	  free(princ_str);
    189 	  return(MISC_EXIT_STATUS);
    190       }
    191   }
    192 
    193   pwsize = sizeof(password);
    194   code = read_old_password(context, password, &pwsize);
    195 
    196   if (code != 0) {
    197     memset(password, 0, sizeof(password));
    198     com_err(whoami, code, string_text(KPW_STR_WHILE_READING_PASSWORD));
    199     krb5_free_principal(context, princ);
    200     free(princ_str);
    201     return(MISC_EXIT_STATUS);
    202   }
    203   if (pwsize == 0) {
    204     memset(password, 0, sizeof(password));
    205     com_err(whoami, 0, string_text(KPW_STR_NO_PASSWORD_READ));
    206     krb5_free_principal(context, princ);
    207     free(princ_str);
    208     return(5);
    209   }
    210 
    211 	snprintf(admin_realm, sizeof (admin_realm),
    212 		krb5_princ_realm(context, princ)->data);
    213 	params.mask |= KADM5_CONFIG_REALM;
    214 	params.realm = admin_realm;
    215 
    216 
    217 	if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) {
    218 		fprintf(stderr, gettext("%s: unable to get host based "
    219 					"service name for realm %s\n"),
    220 			whoami, admin_realm);
    221 		exit(1);
    222 	}
    223 
    224 	code = kadm5_init_with_password(princ_str, password, cpw_service,
    225 					&params, KADM5_STRUCT_VERSION,
    226 					KADM5_API_VERSION_2, NULL,
    227 					&server_handle);
    228 	free(cpw_service);
    229 	if (code != 0) {
    230 		if (code == KADM5_BAD_PASSWORD)
    231 			com_err(whoami, 0,
    232 				string_text(KPW_STR_OLD_PASSWORD_INCORRECT));
    233 		else
    234 			com_err(whoami, 0,
    235 				string_text(KPW_STR_CANT_OPEN_ADMIN_SERVER),
    236 				admin_realm,
    237 				error_message(code));
    238 		krb5_free_principal(context, princ);
    239 		free(princ_str);
    240 		return ((code == KADM5_BAD_PASSWORD) ? 2 : 3);
    241 	}
    242 
    243 	/*
    244 	 * we can only check the policy if the server speaks
    245 	 * RPCSEC_GSS
    246 	 */
    247 	if (_kadm5_get_kpasswd_protocol(server_handle) == KRB5_CHGPWD_RPCSEC) {
    248 		/* Explain policy restrictions on new password if any. */
    249 		/*
    250 		 * Note: copy of this exists in login
    251 		 * (kverify.c/get_verified_in_tkt).
    252 		 */
    253 
    254 		code = kadm5_get_principal(server_handle, princ,
    255 					&principal_entry,
    256 					KADM5_PRINCIPAL_NORMAL_MASK);
    257 		if (code != 0) {
    258 			com_err(whoami, 0,
    259 				string_text((code == KADM5_UNK_PRINC)
    260 					    ? KPW_STR_PRIN_UNKNOWN :
    261 					    KPW_STR_CANT_GET_POLICY_INFO),
    262 				princ_str);
    263 			krb5_free_principal(context, princ);
    264 			free(princ_str);
    265 			(void) kadm5_destroy(server_handle);
    266 			return ((code == KADM5_UNK_PRINC) ? 1 :
    267 				MISC_EXIT_STATUS);
    268 		}
    269 		if ((principal_entry.aux_attributes & KADM5_POLICY) != 0) {
    270 			code = kadm5_get_policy(server_handle,
    271 						principal_entry.policy,
    272 						&policy_entry);
    273 			if (code != 0) {
    274 				/*
    275 				 * doesn't matter which error comes back,
    276 				 * there's no nice recovery or need to
    277 				 * differentiate to the user
    278 				 */
    279 				com_err(whoami, 0,
    280 				string_text(KPW_STR_CANT_GET_POLICY_INFO),
    281 				princ_str);
    282 				(void) kadm5_free_principal_ent(server_handle,
    283 							&principal_entry);
    284 				krb5_free_principal(context, princ);
    285 				free(princ_str);
    286 				free(princ_str);
    287 				(void) kadm5_destroy(server_handle);
    288 				return (MISC_EXIT_STATUS);
    289 			}
    290 			com_err(whoami, 0,
    291 				string_text(KPW_STR_POLICY_EXPLANATION),
    292 				princ_str, principal_entry.policy,
    293 				policy_entry.pw_min_length,
    294 				policy_entry.pw_min_classes);
    295 			if (code = kadm5_free_principal_ent(server_handle,
    296 						    &principal_entry)) {
    297 				(void) kadm5_free_policy_ent(server_handle,
    298 							    &policy_entry);
    299 				krb5_free_principal(context, princ);
    300 				free(princ_str);
    301 				com_err(whoami, code,
    302 				string_text(KPW_STR_WHILE_FREEING_PRINCIPAL));
    303 				(void) kadm5_destroy(server_handle);
    304 				return (MISC_EXIT_STATUS);
    305 			}
    306 			if (code = kadm5_free_policy_ent(server_handle,
    307 							&policy_entry)) {
    308 				krb5_free_principal(context, princ);
    309 				free(princ_str);
    310 				com_err(whoami, code,
    311 				string_text(KPW_STR_WHILE_FREEING_POLICY));
    312 				(void) kadm5_destroy(server_handle);
    313 				return (MISC_EXIT_STATUS);
    314 			}
    315 		} else {
    316 			/*
    317 			 * kpasswd *COULD* output something here to
    318 			 * encourage the choice of good passwords,
    319 			 * in the absence of an enforced policy.
    320 			 */
    321 			if (code = kadm5_free_principal_ent(server_handle,
    322 						    &principal_entry)) {
    323 				krb5_free_principal(context, princ);
    324 				free(princ_str);
    325 				com_err(whoami, code,
    326 				string_text(KPW_STR_WHILE_FREEING_PRINCIPAL));
    327 				(void) kadm5_destroy(server_handle);
    328 				return (MISC_EXIT_STATUS);
    329 			}
    330 		}
    331 	} /* if protocol == KRB5_CHGPWD_RPCSEC */
    332 
    333   pwsize = sizeof(password);
    334   code = read_new_password(server_handle, password, &pwsize, msg_ret, sizeof (msg_ret), princ);
    335   memset(password, 0, sizeof(password));
    336 
    337   if (code)
    338     com_err(whoami, 0, msg_ret);
    339 
    340   krb5_free_principal(context, princ);
    341   free(princ_str);
    342 
    343   (void) kadm5_destroy(server_handle);
    344 
    345   if (code == KRB5_LIBOS_CANTREADPWD)
    346      return(5);
    347   else if (code)
    348      return(4);
    349   else
    350      return(0);
    351 }
    352