1 /* $NetBSD: krb5_passwd.c,v 1.19 2011/04/24 21:16:43 elric 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 #ifdef USE_PAM 49 50 void 51 pwkrb5_usage(const char *prefix) 52 { 53 54 (void) fprintf(stderr, "%s %s [-d krb5 | -k] [principal]\n", 55 prefix, getprogname()); 56 } 57 58 void 59 pwkrb5_argv0_usage(const char *prefix) 60 { 61 62 (void) fprintf(stderr, "%s %s [principal]\n", 63 prefix, getprogname()); 64 } 65 66 void 67 pwkrb5_process(const char *username, int argc, char **argv) 68 { 69 krb5_context context; 70 krb5_error_code ret; 71 krb5_get_init_creds_opt *opt; 72 krb5_principal principal; 73 krb5_creds cred; 74 int result_code; 75 krb5_data result_code_string, result_string; 76 char pwbuf[BUFSIZ]; 77 int ch; 78 const char *errtxt; 79 80 while ((ch = getopt(argc, argv, "5ku:")) != -1) { 81 switch (ch) { 82 case '5': 83 /* 84 * Compatibility option that historically 85 * specified to use Kerberos 5. Silently 86 * ignore it. 87 */ 88 break; 89 90 case 'k': 91 /* 92 * Absorb the -k that may have gotten us here. 93 */ 94 break; 95 96 case 'u': 97 /* 98 * Historical option to specify principal. 99 */ 100 username = optarg; 101 break; 102 103 default: 104 usage(); 105 /* NOTREACHED */ 106 } 107 } 108 109 argc -= optind; 110 argv += optind; 111 112 switch (argc) { 113 case 0: 114 /* username already provided */ 115 break; 116 case 1: 117 /* overrides -u <principal> */ 118 username = argv[0]; 119 break; 120 default: 121 usage(); 122 /* NOTREACHED */ 123 } 124 125 ret = krb5_init_context(&context); 126 if (ret != 0) { 127 if (ret == ENXIO) 128 errx(1, "Kerberos 5 not in use."); 129 errx(1, "Unable to initialize Kerberos 5: %s", strerror(ret)); 130 } 131 132 ret = krb5_get_init_creds_opt_alloc(context, &opt); 133 if (ret) { 134 errtxt = krb5_get_error_message(context, ret); 135 if (errtxt != NULL) { 136 warnx("failed to allocate opts: %s", errtxt); 137 krb5_free_error_message(context, errtxt); 138 } else { 139 warnx("failed to allocate opts: %d", ret); 140 } 141 goto bad; 142 } 143 144 krb5_get_init_creds_opt_set_tkt_life(opt, 300L); 145 krb5_get_init_creds_opt_set_forwardable(opt, FALSE); 146 krb5_get_init_creds_opt_set_proxiable(opt, FALSE); 147 148 ret = krb5_parse_name(context, username, &principal); 149 if (ret) { 150 errtxt = krb5_get_error_message(context, ret); 151 if (errtxt != NULL) { 152 warnx("failed to parse principal: %s", errtxt); 153 krb5_free_error_message(context, errtxt); 154 } else { 155 warnx("failed to parse principal: %d", ret); 156 } 157 goto bad; 158 } 159 160 ret = krb5_get_init_creds_password(context, 161 &cred, 162 principal, 163 NULL, 164 krb5_prompter_posix, 165 NULL, 166 0L, 167 "kadmin/changepw", 168 opt); 169 170 171 switch (ret) { 172 case 0: 173 break; 174 175 case KRB5_LIBOS_PWDINTR : 176 /* XXX */ 177 goto bad; 178 179 case KRB5KRB_AP_ERR_BAD_INTEGRITY : 180 case KRB5KRB_AP_ERR_MODIFIED : 181 fprintf(stderr, "Password incorrect\n"); 182 goto bad; 183 184 default: 185 errtxt = krb5_get_error_message(context, ret); 186 if (errtxt != NULL) { 187 warnx("failed to get credentials: %s", errtxt); 188 krb5_free_error_message(context, errtxt); 189 } else { 190 warnx("failed to get credentials: %d", ret); 191 } 192 goto bad; 193 } 194 195 krb5_data_zero(&result_code_string); 196 krb5_data_zero(&result_string); 197 198 /* XXX use getpass? It has a broken interface. */ 199 if (UI_UTIL_read_pw_string(pwbuf, sizeof(pwbuf), 200 "New password: ", 1) != 0) 201 goto bad; 202 203 ret = krb5_set_password(context, &cred, pwbuf, NULL, 204 &result_code, 205 &result_code_string, 206 &result_string); 207 if (ret) { 208 errtxt = krb5_get_error_message(context, ret); 209 if (errtxt != NULL) { 210 warnx("unable to set password: %s", errtxt); 211 krb5_free_error_message(context, errtxt); 212 } else { 213 warnx("unable to set password: %d", ret); 214 } 215 goto bad; 216 } 217 218 printf("%s%s%.*s\n", 219 krb5_passwd_result_to_string(context, result_code), 220 result_string.length > 0 ? " : " : "", 221 (int)result_string.length, 222 result_string.length > 0 ? (char *)result_string.data : ""); 223 224 krb5_data_free(&result_code_string); 225 krb5_data_free(&result_string); 226 227 krb5_free_cred_contents(context, &cred); 228 krb5_free_context(context); 229 if (result_code) 230 exit(1); 231 return; 232 233 bad: 234 krb5_free_context(context); 235 exit(1); 236 } 237 238 #else /* ! USE_PAM */ 239 240 static krb5_context defcontext; 241 static krb5_principal defprinc; 242 static int kusage = PW_USE; 243 244 int 245 krb5_init(const char *progname) 246 { 247 return krb5_init_context(&defcontext); 248 } 249 250 int 251 krb5_arg (char ch, const char *opt) 252 { 253 krb5_error_code ret; 254 switch(ch) { 255 case '5': 256 case 'k': 257 kusage = PW_USE_FORCE; 258 return 1; 259 case 'u': 260 ret = krb5_parse_name(defcontext, opt, &defprinc); 261 if(ret) { 262 krb5_warn(defcontext, ret, "%s", opt); 263 return 0; 264 } 265 return 1; 266 } 267 return 0; 268 } 269 270 int 271 krb5_arg_end(void) 272 { 273 return kusage; 274 } 275 276 void 277 krb5_end(void) 278 { 279 if (defcontext == NULL) 280 return; 281 if(defprinc) 282 krb5_free_principal(defcontext, defprinc); 283 krb5_free_context(defcontext); 284 } 285 286 287 int 288 krb5_chpw(const char *username) 289 { 290 krb5_error_code ret; 291 krb5_context context; 292 krb5_principal principal; 293 krb5_get_init_creds_opt opt; 294 krb5_creds cred; 295 int result_code; 296 krb5_data result_code_string, result_string; 297 char pwbuf[BUFSIZ]; 298 const char *errtxt; 299 300 ret = krb5_init_context (&context); 301 if (ret) { 302 errtxt = krb5_get_error_message(context, ret); 303 if (errtxt != NULL) { 304 warnx("failed kerberos initialisation: %s", errtxt); 305 krb5_free_error_message(context, errtxt); 306 } else { 307 warnx("failed kerberos initialisation: %d", ret); 308 } 309 return 1; 310 } 311 312 krb5_get_init_creds_opt_init (&opt); 313 314 krb5_get_init_creds_opt_set_tkt_life (&opt, 300); 315 krb5_get_init_creds_opt_set_forwardable (&opt, FALSE); 316 krb5_get_init_creds_opt_set_proxiable (&opt, FALSE); 317 318 if(username != NULL) { 319 ret = krb5_parse_name (context, username, &principal); 320 if (ret) { 321 errtxt = krb5_get_error_message(context, ret); 322 if (errtxt != NULL) { 323 warnx("failed to parse principal: %s", errtxt); 324 krb5_free_error_message(context, errtxt); 325 } else { 326 warnx("failed to parse principal: %d", ret); 327 } 328 return 1; 329 } 330 } else 331 principal = defprinc; 332 333 ret = krb5_get_init_creds_password (context, 334 &cred, 335 principal, 336 NULL, 337 krb5_prompter_posix, 338 NULL, 339 0, 340 "kadmin/changepw", 341 &opt); 342 343 switch (ret) { 344 case 0: 345 break; 346 case KRB5_LIBOS_PWDINTR : 347 /* XXX */ 348 return 1; 349 case KRB5KRB_AP_ERR_BAD_INTEGRITY : 350 case KRB5KRB_AP_ERR_MODIFIED : 351 fprintf(stderr, "Password incorrect\n"); 352 return 1; 353 break; 354 default: 355 errtxt = krb5_get_error_message(context, ret); 356 if (errtxt != NULL) { 357 warnx("failed to get credentials: %s", errtxt); 358 krb5_free_error_message(context, errtxt); 359 } else { 360 warnx("failed to get credentials: %d", ret); 361 } 362 return 1; 363 } 364 krb5_data_zero (&result_code_string); 365 krb5_data_zero (&result_string); 366 367 /* XXX use getpass? It has a broken interface. */ 368 if(UI_UTIL_read_pw_string(pwbuf, sizeof(pwbuf), "New password: ", 1) != 0) 369 return 1; 370 371 ret = krb5_set_password (context, &cred, pwbuf, NULL, 372 &result_code, 373 &result_code_string, 374 &result_string); 375 if (ret) 376 krb5_err (context, 1, ret, "krb5_set_password"); 377 378 printf ("%s%s%.*s\n", krb5_passwd_result_to_string(context, result_code), 379 result_string.length > 0 ? " : " : "", 380 (int)result_string.length, 381 result_string.length > 0 ? (char *)result_string.data : ""); 382 383 krb5_data_free (&result_code_string); 384 krb5_data_free (&result_string); 385 386 krb5_free_cred_contents (context, &cred); 387 krb5_free_context (context); 388 return result_code; 389 } 390 391 #endif /* USE_PAM */ 392