1 /* $NetBSD: krb5_passwd.c,v 1.20 2012/04/22 23:43:51 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2000, 2005 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Johan Danielsson; and by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* uses the `Kerberos Change Password Protocol' */ 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <err.h> 39 #include <errno.h> 40 #include <pwd.h> 41 #include <unistd.h> 42 43 #include <openssl/ui.h> 44 #include <krb5.h> 45 46 #include "extern.h" 47 48 static void 49 pwkrb5_warn(const char *msg, krb5_context context, krb5_error_code ret) 50 { 51 const char *errtxt = krb5_get_error_message(context, ret); 52 if (errtxt != NULL) { 53 warnx("%s: %s", msg, errtxt); 54 krb5_free_error_message(context, errtxt); 55 } else 56 warnx("%s: %d", msg, ret); 57 } 58 59 #ifdef USE_PAM 60 61 void 62 pwkrb5_usage(const char *prefix) 63 { 64 65 (void) fprintf(stderr, "%s %s [-d krb5 | -k] [principal]\n", 66 prefix, getprogname()); 67 } 68 69 void 70 pwkrb5_argv0_usage(const char *prefix) 71 { 72 73 (void) fprintf(stderr, "%s %s [principal]\n", 74 prefix, getprogname()); 75 } 76 77 void 78 pwkrb5_process(const char *username, int argc, char **argv) 79 { 80 krb5_context context; 81 krb5_error_code ret; 82 krb5_get_init_creds_opt *opt; 83 krb5_principal principal; 84 krb5_creds cred; 85 int result_code; 86 krb5_data result_code_string, result_string; 87 char pwbuf[BUFSIZ]; 88 int ch; 89 90 while ((ch = getopt(argc, argv, "5ku:")) != -1) { 91 switch (ch) { 92 case '5': 93 /* 94 * Compatibility option that historically 95 * specified to use Kerberos 5. Silently 96 * ignore it. 97 */ 98 break; 99 100 case 'k': 101 /* 102 * Absorb the -k that may have gotten us here. 103 */ 104 break; 105 106 case 'u': 107 /* 108 * Historical option to specify principal. 109 */ 110 username = optarg; 111 break; 112 113 default: 114 usage(); 115 /* NOTREACHED */ 116 } 117 } 118 119 argc -= optind; 120 argv += optind; 121 122 switch (argc) { 123 case 0: 124 /* username already provided */ 125 break; 126 case 1: 127 /* overrides -u <principal> */ 128 username = argv[0]; 129 break; 130 default: 131 usage(); 132 /* NOTREACHED */ 133 } 134 135 ret = krb5_init_context(&context); 136 if (ret != 0) { 137 if (ret == ENXIO) 138 errx(1, "Kerberos 5 not in use."); 139 errx(1, "Unable to initialize Kerberos 5: %s", strerror(ret)); 140 } 141 142 ret = krb5_get_init_creds_opt_alloc(context, &opt); 143 if (ret) { 144 pwkrb5_warn("failed to allocate opts", context, ret); 145 goto bad; 146 } 147 148 krb5_get_init_creds_opt_set_tkt_life(opt, 300L); 149 krb5_get_init_creds_opt_set_forwardable(opt, FALSE); 150 krb5_get_init_creds_opt_set_proxiable(opt, FALSE); 151 152 ret = krb5_parse_name(context, username, &principal); 153 if (ret) { 154 krb5_get_init_creds_opt_free(context, opt); 155 pwkrb5_warn("failed to parse principal", context, ret); 156 goto bad; 157 } 158 159 ret = krb5_get_init_creds_password(context, 160 &cred, 161 principal, 162 NULL, 163 krb5_prompter_posix, 164 NULL, 165 0L, 166 "kadmin/changepw", 167 opt); 168 169 krb5_get_init_creds_opt_free(context, opt); 170 switch (ret) { 171 case 0: 172 break; 173 174 case KRB5_LIBOS_PWDINTR : 175 /* XXX */ 176 goto bad; 177 178 case KRB5KRB_AP_ERR_BAD_INTEGRITY : 179 case KRB5KRB_AP_ERR_MODIFIED : 180 fprintf(stderr, "Password incorrect\n"); 181 goto bad; 182 183 default: 184 pwkrb5_warn("failed to get credentials", context, ret); 185 goto bad; 186 } 187 188 krb5_data_zero(&result_code_string); 189 krb5_data_zero(&result_string); 190 191 /* XXX use getpass? It has a broken interface. */ 192 if (UI_UTIL_read_pw_string(pwbuf, sizeof(pwbuf), 193 "New password: ", 1) != 0) 194 goto bad; 195 196 ret = krb5_set_password(context, &cred, pwbuf, NULL, 197 &result_code, 198 &result_code_string, 199 &result_string); 200 if (ret) { 201 pwkrb5_warn("unable to set password", context, ret); 202 goto bad; 203 } 204 205 printf("%s%s%.*s\n", 206 krb5_passwd_result_to_string(context, result_code), 207 result_string.length > 0 ? " : " : "", 208 (int)result_string.length, 209 result_string.length > 0 ? (char *)result_string.data : ""); 210 211 krb5_data_free(&result_code_string); 212 krb5_data_free(&result_string); 213 214 krb5_free_cred_contents(context, &cred); 215 krb5_free_context(context); 216 if (result_code) 217 exit(1); 218 return; 219 220 bad: 221 krb5_free_context(context); 222 exit(1); 223 } 224 225 #else /* ! USE_PAM */ 226 227 static krb5_context defcontext; 228 static krb5_principal defprinc; 229 static int kusage = PW_USE; 230 231 int 232 krb5_init(const char *progname) 233 { 234 return krb5_init_context(&defcontext); 235 } 236 237 int 238 krb5_arg (char ch, const char *opt) 239 { 240 krb5_error_code ret; 241 switch(ch) { 242 case '5': 243 case 'k': 244 kusage = PW_USE_FORCE; 245 return 1; 246 case 'u': 247 ret = krb5_parse_name(defcontext, opt, &defprinc); 248 if(ret) { 249 krb5_warn(defcontext, ret, "%s", opt); 250 return 0; 251 } 252 return 1; 253 } 254 return 0; 255 } 256 257 int 258 krb5_arg_end(void) 259 { 260 return kusage; 261 } 262 263 void 264 krb5_end(void) 265 { 266 if (defcontext == NULL) 267 return; 268 if(defprinc) 269 krb5_free_principal(defcontext, defprinc); 270 krb5_free_context(defcontext); 271 } 272 273 int 274 krb5_chpw(const char *username) 275 { 276 krb5_error_code ret; 277 krb5_context context; 278 krb5_principal principal; 279 krb5_get_init_creds_opt *opt; 280 krb5_creds cred; 281 int result_code; 282 krb5_data result_code_string, result_string; 283 char pwbuf[BUFSIZ]; 284 285 ret = krb5_init_context (&context); 286 if (ret) { 287 pwkrb5_warn("failed kerberos initialisation", context, ret); 288 return 1; 289 } 290 291 ret = krb5_get_init_creds_opt_alloc (context, &opt); 292 if (ret) { 293 pwkrb5_warn("failed to allocate credential opt", context, ret); 294 return 1; 295 } 296 297 krb5_get_init_creds_opt_set_tkt_life (opt, 300); 298 krb5_get_init_creds_opt_set_forwardable (opt, FALSE); 299 krb5_get_init_creds_opt_set_proxiable (opt, FALSE); 300 301 if(username != NULL) { 302 ret = krb5_parse_name (context, username, &principal); 303 if (ret) { 304 krb5_get_init_creds_opt_free (context, opt); 305 pwkrb5_warn("failed to parse principal", context, ret); 306 return 1; 307 } 308 } else 309 principal = defprinc; 310 311 ret = krb5_get_init_creds_password (context, 312 &cred, 313 principal, 314 NULL, 315 krb5_prompter_posix, 316 NULL, 317 0, 318 "kadmin/changepw", 319 opt); 320 321 krb5_get_init_creds_opt_free (context, opt); 322 switch (ret) { 323 case 0: 324 break; 325 case KRB5_LIBOS_PWDINTR : 326 /* XXX */ 327 return 1; 328 case KRB5KRB_AP_ERR_BAD_INTEGRITY : 329 case KRB5KRB_AP_ERR_MODIFIED : 330 fprintf(stderr, "Password incorrect\n"); 331 return 1; 332 break; 333 default: 334 pwkrb5_warn("failed to get credentials", context, ret); 335 return 1; 336 } 337 krb5_data_zero (&result_code_string); 338 krb5_data_zero (&result_string); 339 340 /* XXX use getpass? It has a broken interface. */ 341 if(UI_UTIL_read_pw_string(pwbuf, sizeof(pwbuf), "New password: ", 1) != 0) 342 return 1; 343 344 ret = krb5_set_password (context, &cred, pwbuf, NULL, 345 &result_code, 346 &result_code_string, 347 &result_string); 348 if (ret) 349 krb5_err (context, 1, ret, "krb5_set_password"); 350 351 printf ("%s%s%.*s\n", krb5_passwd_result_to_string(context, result_code), 352 result_string.length > 0 ? " : " : "", 353 (int)result_string.length, 354 result_string.length > 0 ? (char *)result_string.data : ""); 355 356 krb5_data_free (&result_code_string); 357 krb5_data_free (&result_string); 358 359 krb5_free_cred_contents (context, &cred); 360 krb5_free_context (context); 361 return result_code; 362 } 363 364 #endif /* USE_PAM */ 365