1 /* 2 * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #define KRB5_DEPRECATED /* uses v4 functions that will die */ 35 36 #include "hprop.h" 37 38 static int version_flag; 39 static int help_flag; 40 static const char *ktname = HPROP_KEYTAB; 41 static const char *database; 42 static char *mkeyfile; 43 static int to_stdout; 44 static int verbose_flag; 45 static int encrypt_flag; 46 static int decrypt_flag; 47 static hdb_master_key mkey5; 48 49 static char *source_type; 50 51 static char *local_realm=NULL; 52 53 static int 54 open_socket(krb5_context context, const char *hostname, const char *port) 55 { 56 struct addrinfo *ai, *a; 57 struct addrinfo hints; 58 int error; 59 60 memset (&hints, 0, sizeof(hints)); 61 hints.ai_socktype = SOCK_STREAM; 62 hints.ai_protocol = IPPROTO_TCP; 63 64 error = getaddrinfo (hostname, port, &hints, &ai); 65 if (error) { 66 warnx ("%s: %s", hostname, gai_strerror(error)); 67 return -1; 68 } 69 70 for (a = ai; a != NULL; a = a->ai_next) { 71 int s; 72 73 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 74 if (s < 0) 75 continue; 76 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 77 warn ("connect(%s)", hostname); 78 close (s); 79 continue; 80 } 81 freeaddrinfo (ai); 82 return s; 83 } 84 warnx ("failed to contact %s", hostname); 85 freeaddrinfo (ai); 86 return -1; 87 } 88 89 krb5_error_code 90 v5_prop(krb5_context context, HDB *db, hdb_entry_ex *entry, void *appdata) 91 { 92 krb5_error_code ret; 93 struct prop_data *pd = appdata; 94 krb5_data data; 95 96 if(encrypt_flag) { 97 ret = hdb_seal_keys_mkey(context, &entry->entry, mkey5); 98 if (ret) { 99 krb5_warn(context, ret, "hdb_seal_keys_mkey"); 100 return ret; 101 } 102 } 103 if(decrypt_flag) { 104 ret = hdb_unseal_keys_mkey(context, &entry->entry, mkey5); 105 if (ret) { 106 krb5_warn(context, ret, "hdb_unseal_keys_mkey"); 107 return ret; 108 } 109 } 110 111 ret = hdb_entry2value(context, &entry->entry, &data); 112 if(ret) { 113 krb5_warn(context, ret, "hdb_entry2value"); 114 return ret; 115 } 116 117 if(to_stdout) 118 ret = krb5_write_message(context, &pd->sock, &data); 119 else 120 ret = krb5_write_priv_message(context, pd->auth_context, 121 &pd->sock, &data); 122 krb5_data_free(&data); 123 return ret; 124 } 125 126 struct getargs args[] = { 127 { "master-key", 'm', arg_string, &mkeyfile, "v5 master key file", "file" }, 128 { "database", 'd', arg_string, rk_UNCONST(&database), "database", "file" }, 129 { "source", 0, arg_string, &source_type, "type of database to read", 130 "heimdal" 131 "|mit-dump" 132 }, 133 134 { "keytab", 'k', arg_string, rk_UNCONST(&ktname), 135 "keytab to use for authentication", "keytab" }, 136 { "v5-realm", 'R', arg_string, &local_realm, "v5 realm to use", NULL }, 137 { "decrypt", 'D', arg_flag, &decrypt_flag, "decrypt keys", NULL }, 138 { "encrypt", 'E', arg_flag, &encrypt_flag, "encrypt keys", NULL }, 139 { "stdout", 'n', arg_flag, &to_stdout, "dump to stdout", NULL }, 140 { "verbose", 'v', arg_flag, &verbose_flag, NULL, NULL }, 141 { "version", 0, arg_flag, &version_flag, NULL, NULL }, 142 { "help", 'h', arg_flag, &help_flag, NULL, NULL } 143 }; 144 145 static int num_args = sizeof(args) / sizeof(args[0]); 146 147 static void 148 usage(int ret) 149 { 150 arg_printusage (args, num_args, NULL, "[host[:port]] ..."); 151 exit (ret); 152 } 153 154 static void 155 get_creds(krb5_context context, krb5_ccache *cache) 156 { 157 krb5_keytab keytab; 158 krb5_principal client; 159 krb5_error_code ret; 160 krb5_get_init_creds_opt *init_opts; 161 krb5_preauthtype preauth = KRB5_PADATA_ENC_TIMESTAMP; 162 krb5_creds creds; 163 164 ret = krb5_kt_register(context, &hdb_kt_ops); 165 if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); 166 167 ret = krb5_kt_resolve(context, ktname, &keytab); 168 if(ret) krb5_err(context, 1, ret, "krb5_kt_resolve"); 169 170 ret = krb5_make_principal(context, &client, NULL, 171 "kadmin", HPROP_NAME, NULL); 172 if(ret) krb5_err(context, 1, ret, "krb5_make_principal"); 173 174 ret = krb5_get_init_creds_opt_alloc(context, &init_opts); 175 if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc"); 176 krb5_get_init_creds_opt_set_preauth_list(init_opts, &preauth, 1); 177 178 ret = krb5_get_init_creds_keytab(context, &creds, client, keytab, 0, NULL, init_opts); 179 if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds"); 180 181 krb5_get_init_creds_opt_free(context, init_opts); 182 183 ret = krb5_kt_close(context, keytab); 184 if(ret) krb5_err(context, 1, ret, "krb5_kt_close"); 185 186 ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, cache); 187 if(ret) krb5_err(context, 1, ret, "krb5_cc_new_unique"); 188 189 ret = krb5_cc_initialize(context, *cache, client); 190 if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize"); 191 192 krb5_free_principal(context, client); 193 194 ret = krb5_cc_store_cred(context, *cache, &creds); 195 if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred"); 196 197 krb5_free_cred_contents(context, &creds); 198 } 199 200 enum hprop_source { 201 HPROP_HEIMDAL = 1, 202 HPROP_MIT_DUMP 203 }; 204 205 struct { 206 int type; 207 const char *name; 208 } types[] = { 209 { HPROP_HEIMDAL, "heimdal" }, 210 { HPROP_MIT_DUMP, "mit-dump" } 211 }; 212 213 static int 214 parse_source_type(const char *s) 215 { 216 size_t i; 217 for(i = 0; i < sizeof(types) / sizeof(types[0]); i++) { 218 if(strstr(types[i].name, s) == types[i].name) 219 return types[i].type; 220 } 221 return 0; 222 } 223 224 static int 225 iterate (krb5_context context, 226 const char *database_name, 227 HDB *db, 228 int type, 229 struct prop_data *pd) 230 { 231 int ret; 232 233 switch(type) { 234 case HPROP_MIT_DUMP: 235 ret = mit_prop_dump(pd, database_name); 236 if (ret) 237 krb5_warn(context, ret, "mit_prop_dump"); 238 break; 239 case HPROP_HEIMDAL: 240 ret = hdb_foreach(context, db, HDB_F_DECRYPT, v5_prop, pd); 241 if(ret) 242 krb5_warn(context, ret, "hdb_foreach"); 243 break; 244 default: 245 krb5_errx(context, 1, "unknown prop type: %d", type); 246 } 247 return ret; 248 } 249 250 static int 251 dump_database (krb5_context context, int type, 252 const char *database_name, HDB *db) 253 { 254 krb5_error_code ret; 255 struct prop_data pd; 256 krb5_data data; 257 258 pd.context = context; 259 pd.auth_context = NULL; 260 pd.sock = STDOUT_FILENO; 261 262 ret = iterate (context, database_name, db, type, &pd); 263 if (ret) 264 krb5_errx(context, 1, "iterate failure"); 265 krb5_data_zero (&data); 266 ret = krb5_write_message (context, &pd.sock, &data); 267 if (ret) 268 krb5_err(context, 1, ret, "krb5_write_message"); 269 270 return 0; 271 } 272 273 static int 274 propagate_database (krb5_context context, int type, 275 const char *database_name, 276 HDB *db, krb5_ccache ccache, 277 int optidx, int argc, char **argv) 278 { 279 krb5_principal server; 280 krb5_error_code ret; 281 int i, failed = 0; 282 283 for(i = optidx; i < argc; i++){ 284 krb5_auth_context auth_context; 285 int fd; 286 struct prop_data pd; 287 krb5_data data; 288 289 char *port, portstr[NI_MAXSERV]; 290 char *host = argv[i]; 291 292 port = strchr(host, ':'); 293 if(port == NULL) { 294 snprintf(portstr, sizeof(portstr), "%u", 295 ntohs(krb5_getportbyname (context, "hprop", "tcp", 296 HPROP_PORT))); 297 port = portstr; 298 } else 299 *port++ = '\0'; 300 301 fd = open_socket(context, host, port); 302 if(fd < 0) { 303 failed++; 304 krb5_warn (context, errno, "connect %s", host); 305 continue; 306 } 307 308 ret = krb5_sname_to_principal(context, argv[i], 309 HPROP_NAME, KRB5_NT_SRV_HST, &server); 310 if(ret) { 311 failed++; 312 krb5_warn(context, ret, "krb5_sname_to_principal(%s)", host); 313 close(fd); 314 continue; 315 } 316 317 if (local_realm) { 318 krb5_realm my_realm; 319 krb5_get_default_realm(context,&my_realm); 320 krb5_principal_set_realm(context,server,my_realm); 321 krb5_xfree(my_realm); 322 } 323 324 auth_context = NULL; 325 ret = krb5_sendauth(context, 326 &auth_context, 327 &fd, 328 HPROP_VERSION, 329 NULL, 330 server, 331 AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, 332 NULL, /* in_data */ 333 NULL, /* in_creds */ 334 ccache, 335 NULL, 336 NULL, 337 NULL); 338 339 krb5_free_principal(context, server); 340 341 if(ret) { 342 failed++; 343 krb5_warn(context, ret, "krb5_sendauth (%s)", host); 344 close(fd); 345 goto next_host; 346 } 347 348 pd.context = context; 349 pd.auth_context = auth_context; 350 pd.sock = fd; 351 352 ret = iterate (context, database_name, db, type, &pd); 353 if (ret) { 354 krb5_warnx(context, "iterate to host %s failed", host); 355 failed++; 356 goto next_host; 357 } 358 359 krb5_data_zero (&data); 360 ret = krb5_write_priv_message(context, auth_context, &fd, &data); 361 if(ret) { 362 krb5_warn(context, ret, "krb5_write_priv_message"); 363 failed++; 364 goto next_host; 365 } 366 367 ret = krb5_read_priv_message(context, auth_context, &fd, &data); 368 if(ret) { 369 krb5_warn(context, ret, "krb5_read_priv_message: %s", host); 370 failed++; 371 goto next_host; 372 } else 373 krb5_data_free (&data); 374 375 next_host: 376 krb5_auth_con_free(context, auth_context); 377 close(fd); 378 } 379 if (failed) 380 return 1; 381 return 0; 382 } 383 384 int 385 main(int argc, char **argv) 386 { 387 krb5_error_code ret; 388 krb5_context context; 389 krb5_ccache ccache = NULL; 390 HDB *db = NULL; 391 int optidx = 0; 392 393 int type, exit_code; 394 395 setprogname(argv[0]); 396 397 if(getarg(args, num_args, argc, argv, &optidx)) 398 usage(1); 399 400 if(help_flag) 401 usage(0); 402 403 if(version_flag){ 404 print_version(NULL); 405 exit(0); 406 } 407 408 ret = krb5_init_context(&context); 409 if(ret) 410 exit(1); 411 412 /* We may be reading an old database encrypted with a DES master key. */ 413 ret = krb5_allow_weak_crypto(context, 1); 414 if(ret) 415 krb5_err(context, 1, ret, "krb5_allow_weak_crypto"); 416 417 if(local_realm) 418 krb5_set_default_realm(context, local_realm); 419 420 if(encrypt_flag && decrypt_flag) 421 krb5_errx(context, 1, 422 "only one of `--encrypt' and `--decrypt' is meaningful"); 423 424 if(source_type != NULL) { 425 type = parse_source_type(source_type); 426 if(type == 0) 427 krb5_errx(context, 1, "unknown source type `%s'", source_type); 428 } else 429 type = HPROP_HEIMDAL; 430 431 if(!to_stdout) 432 get_creds(context, &ccache); 433 434 if(decrypt_flag || encrypt_flag) { 435 ret = hdb_read_master_key(context, mkeyfile, &mkey5); 436 if(ret && ret != ENOENT) 437 krb5_err(context, 1, ret, "hdb_read_master_key"); 438 if(ret) 439 krb5_errx(context, 1, "No master key file found"); 440 } 441 442 switch(type) { 443 case HPROP_MIT_DUMP: 444 if (database == NULL) 445 krb5_errx(context, 1, "no dump file specified"); 446 break; 447 case HPROP_HEIMDAL: 448 ret = hdb_create (context, &db, database); 449 if(ret) 450 krb5_err(context, 1, ret, "hdb_create: %s", database); 451 ret = db->hdb_open(context, db, O_RDONLY, 0); 452 if(ret) 453 krb5_err(context, 1, ret, "db->hdb_open"); 454 break; 455 default: 456 krb5_errx(context, 1, "unknown dump type `%d'", type); 457 break; 458 } 459 460 if (to_stdout) 461 exit_code = dump_database (context, type, database, db); 462 else 463 exit_code = propagate_database (context, type, database, 464 db, ccache, optidx, argc, argv); 465 466 if(ccache != NULL) 467 krb5_cc_destroy(context, ccache); 468 469 if(db != NULL) 470 (*db->hdb_destroy)(context, db); 471 472 krb5_free_context(context); 473 return exit_code; 474 } 475