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 38static int version_flag; 39static int help_flag; 40static const char *ktname = HPROP_KEYTAB; 41static const char *database; 42static char *mkeyfile; 43static int to_stdout; 44static int verbose_flag; 45static int encrypt_flag; 46static int decrypt_flag; 47static hdb_master_key mkey5; 48 49static char *source_type; 50 51static char *local_realm=NULL; 52 53static int 54open_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 socket_set_nopipe(s, 1); 77 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 78 warn ("connect(%s)", hostname); 79 close (s); 80 continue; 81 } 82 freeaddrinfo (ai); 83 return s; 84 } 85 warnx ("failed to contact %s", hostname); 86 freeaddrinfo (ai); 87 return -1; 88} 89 90krb5_error_code 91v5_prop(krb5_context context, HDB *db, hdb_entry_ex *entry, void *appdata) 92{ 93 krb5_error_code ret; 94 struct prop_data *pd = appdata; 95 krb5_data data; 96 97 if(encrypt_flag) { 98 ret = hdb_seal_keys_mkey(context, &entry->entry, mkey5); 99 if (ret) { 100 krb5_warn(context, ret, "hdb_seal_keys_mkey"); 101 return ret; 102 } 103 } 104 if(decrypt_flag) { 105 ret = hdb_unseal_keys_mkey(context, &entry->entry, mkey5); 106 if (ret) { 107 krb5_warn(context, ret, "hdb_unseal_keys_mkey"); 108 return ret; 109 } 110 } 111 112 ret = hdb_entry2value(context, &entry->entry, &data); 113 if(ret) { 114 krb5_warn(context, ret, "hdb_entry2value"); 115 return ret; 116 } 117 118 if(to_stdout) 119 ret = krb5_write_message(context, &pd->sock, &data); 120 else 121 ret = krb5_write_priv_message(context, pd->auth_context, 122 &pd->sock, &data); 123 krb5_data_free(&data); 124 return ret; 125} 126 127struct getargs args[] = { 128 { "master-key", 'm', arg_string, &mkeyfile, "v5 master key file", "file" }, 129 { "database", 'd', arg_string, rk_UNCONST(&database), "database", "file" }, 130 { "source", 0, arg_string, &source_type, "type of database to read", 131 "heimdal" 132 "|mit-dump" 133 }, 134 135 { "keytab", 'k', arg_string, rk_UNCONST(&ktname), 136 "keytab to use for authentication", "keytab" }, 137 { "v5-realm", 'R', arg_string, &local_realm, "v5 realm to use", NULL }, 138 { "decrypt", 'D', arg_flag, &decrypt_flag, "decrypt keys", NULL }, 139 { "encrypt", 'E', arg_flag, &encrypt_flag, "encrypt keys", NULL }, 140 { "stdout", 'n', arg_flag, &to_stdout, "dump to stdout", NULL }, 141 { "verbose", 'v', arg_flag, &verbose_flag, NULL, NULL }, 142 { "version", 0, arg_flag, &version_flag, NULL, NULL }, 143 { "help", 'h', arg_flag, &help_flag, NULL, NULL } 144}; 145 146static int num_args = sizeof(args) / sizeof(args[0]); 147 148static void 149usage(int ret) 150{ 151 arg_printusage (args, num_args, NULL, "[host[:port]] ..."); 152 exit (ret); 153} 154 155static void 156get_creds(krb5_context context, krb5_ccache *cache) 157{ 158 krb5_keytab keytab; 159 krb5_principal client; 160 krb5_error_code ret; 161 krb5_get_init_creds_opt *init_opts; 162 krb5_preauthtype preauth = KRB5_PADATA_ENC_TIMESTAMP; 163 krb5_creds creds; 164 165 ret = krb5_kt_register(context, &hdb_kt_ops); 166 if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); 167 168 ret = krb5_kt_resolve(context, ktname, &keytab); 169 if(ret) krb5_err(context, 1, ret, "krb5_kt_resolve"); 170 171 ret = krb5_make_principal(context, &client, NULL, 172 "kadmin", HPROP_NAME, NULL); 173 if(ret) krb5_err(context, 1, ret, "krb5_make_principal"); 174 175 ret = krb5_get_init_creds_opt_alloc(context, &init_opts); 176 if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc"); 177 krb5_get_init_creds_opt_set_preauth_list(init_opts, &preauth, 1); 178 179 ret = krb5_get_init_creds_keytab(context, &creds, client, keytab, 0, NULL, init_opts); 180 if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds"); 181 182 krb5_get_init_creds_opt_free(context, init_opts); 183 184 ret = krb5_kt_close(context, keytab); 185 if(ret) krb5_err(context, 1, ret, "krb5_kt_close"); 186 187 ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, cache); 188 if(ret) krb5_err(context, 1, ret, "krb5_cc_new_unique"); 189 190 ret = krb5_cc_initialize(context, *cache, client); 191 if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize"); 192 193 krb5_free_principal(context, client); 194 195 ret = krb5_cc_store_cred(context, *cache, &creds); 196 if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred"); 197 198 krb5_free_cred_contents(context, &creds); 199} 200 201enum hprop_source { 202 HPROP_HEIMDAL = 1, 203 HPROP_MIT_DUMP 204}; 205 206struct { 207 int type; 208 const char *name; 209} types[] = { 210 { HPROP_HEIMDAL, "heimdal" }, 211 { HPROP_MIT_DUMP, "mit-dump" } 212}; 213 214static int 215parse_source_type(const char *s) 216{ 217 size_t i; 218 for(i = 0; i < sizeof(types) / sizeof(types[0]); i++) { 219 if(strstr(types[i].name, s) == types[i].name) 220 return types[i].type; 221 } 222 return 0; 223} 224 225static int 226iterate (krb5_context context, 227 const char *database_name, 228 HDB *db, 229 int type, 230 struct prop_data *pd) 231{ 232 int ret; 233 234 switch(type) { 235 case HPROP_MIT_DUMP: 236 ret = hdb_mit_dump(context, database_name, v5_prop, pd); 237 if (ret) 238 krb5_warn(context, ret, "mit_prop_dump"); 239 break; 240 case HPROP_HEIMDAL: 241 ret = hdb_foreach(context, db, HDB_F_DECRYPT, v5_prop, pd); 242 if(ret) 243 krb5_warn(context, ret, "hdb_foreach"); 244 break; 245 default: 246 krb5_errx(context, 1, "unknown prop type: %d", type); 247 } 248 return ret; 249} 250 251static int 252dump_database (krb5_context context, int type, 253 const char *database_name, HDB *db) 254{ 255 krb5_error_code ret; 256 struct prop_data pd; 257 krb5_data data; 258 259 pd.context = context; 260 pd.auth_context = NULL; 261 pd.sock = STDOUT_FILENO; 262 263 ret = iterate (context, database_name, db, type, &pd); 264 if (ret) 265 krb5_errx(context, 1, "iterate failure"); 266 krb5_data_zero (&data); 267 ret = krb5_write_message (context, &pd.sock, &data); 268 if (ret) 269 krb5_err(context, 1, ret, "krb5_write_message"); 270 271 return 0; 272} 273 274static int 275propagate_database (krb5_context context, int type, 276 const char *database_name, 277 HDB *db, krb5_ccache ccache, 278 int optidx, int argc, char **argv) 279{ 280 krb5_principal server; 281 krb5_error_code ret; 282 int i, failed = 0; 283 284 for(i = optidx; i < argc; i++){ 285 krb5_auth_context auth_context; 286 int fd; 287 struct prop_data pd; 288 krb5_data data; 289 290 char *port, portstr[NI_MAXSERV]; 291 char *host = argv[i]; 292 293 port = strchr(host, ':'); 294 if(port == NULL) { 295 snprintf(portstr, sizeof(portstr), "%u", 296 ntohs(krb5_getportbyname (context, "hprop", "tcp", 297 HPROP_PORT))); 298 port = portstr; 299 } else 300 *port++ = '\0'; 301 302 fd = open_socket(context, host, port); 303 if(fd < 0) { 304 failed++; 305 krb5_warn (context, errno, "connect %s", host); 306 continue; 307 } 308 309 ret = krb5_sname_to_principal(context, argv[i], 310 HPROP_NAME, KRB5_NT_SRV_HST, &server); 311 if(ret) { 312 failed++; 313 krb5_warn(context, ret, "krb5_sname_to_principal(%s)", host); 314 close(fd); 315 continue; 316 } 317 318 if (local_realm) { 319 krb5_realm my_realm; 320 krb5_get_default_realm(context,&my_realm); 321 krb5_principal_set_realm(context,server,my_realm); 322 krb5_xfree(my_realm); 323 } 324 325 auth_context = NULL; 326 ret = krb5_sendauth(context, 327 &auth_context, 328 &fd, 329 HPROP_VERSION, 330 NULL, 331 server, 332 AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, 333 NULL, /* in_data */ 334 NULL, /* in_creds */ 335 ccache, 336 NULL, 337 NULL, 338 NULL); 339 340 krb5_free_principal(context, server); 341 342 if(ret) { 343 failed++; 344 krb5_warn(context, ret, "krb5_sendauth (%s)", host); 345 close(fd); 346 goto next_host; 347 } 348 349 pd.context = context; 350 pd.auth_context = auth_context; 351 pd.sock = fd; 352 353 ret = iterate (context, database_name, db, type, &pd); 354 if (ret) { 355 krb5_warnx(context, "iterate to host %s failed", host); 356 failed++; 357 goto next_host; 358 } 359 360 krb5_data_zero (&data); 361 ret = krb5_write_priv_message(context, auth_context, &fd, &data); 362 if(ret) { 363 krb5_warn(context, ret, "krb5_write_priv_message"); 364 failed++; 365 goto next_host; 366 } 367 368 ret = krb5_read_priv_message(context, auth_context, &fd, &data); 369 if(ret) { 370 krb5_warn(context, ret, "krb5_read_priv_message: %s", host); 371 failed++; 372 goto next_host; 373 } else 374 krb5_data_free (&data); 375 376 next_host: 377 krb5_auth_con_free(context, auth_context); 378 close(fd); 379 } 380 if (failed) 381 return 1; 382 return 0; 383} 384 385int 386main(int argc, char **argv) 387{ 388 krb5_error_code ret; 389 krb5_context context; 390 krb5_ccache ccache = NULL; 391 HDB *db = NULL; 392 int optidx = 0; 393 394 int type, exit_code; 395 396 setprogname(argv[0]); 397 398 if(getarg(args, num_args, argc, argv, &optidx)) 399 usage(1); 400 401 if(help_flag) 402 usage(0); 403 404 if(version_flag){ 405 print_version(NULL); 406 exit(0); 407 } 408 409 ret = krb5_init_context(&context); 410 if(ret) 411 exit(1); 412 413 /* We may be reading an old database encrypted with a DES master key. */ 414 ret = krb5_allow_weak_crypto(context, 1); 415 if(ret) 416 krb5_err(context, 1, ret, "krb5_allow_weak_crypto"); 417 418 if(local_realm) 419 krb5_set_default_realm(context, local_realm); 420 421 if(encrypt_flag && decrypt_flag) 422 krb5_errx(context, 1, 423 "only one of `--encrypt' and `--decrypt' is meaningful"); 424 425 if(source_type != NULL) { 426 type = parse_source_type(source_type); 427 if(type == 0) 428 krb5_errx(context, 1, "unknown source type `%s'", source_type); 429 } else 430 type = HPROP_HEIMDAL; 431 432 if(!to_stdout) 433 get_creds(context, &ccache); 434 435 if(decrypt_flag || encrypt_flag) { 436 ret = hdb_read_master_key(context, mkeyfile, &mkey5); 437 if(ret && ret != ENOENT) 438 krb5_err(context, 1, ret, "hdb_read_master_key"); 439 if(ret) 440 krb5_errx(context, 1, "No master key file found"); 441 } 442 443 switch(type) { 444 case HPROP_MIT_DUMP: 445 if (database == NULL) 446 krb5_errx(context, 1, "no dump file specified"); 447 break; 448 case HPROP_HEIMDAL: 449 ret = hdb_create (context, &db, database); 450 if(ret) 451 krb5_err(context, 1, ret, "hdb_create: %s", database); 452 ret = db->hdb_open(context, db, O_RDONLY, 0); 453 if(ret) 454 krb5_err(context, 1, ret, "db->hdb_open"); 455 break; 456 default: 457 krb5_errx(context, 1, "unknown dump type `%d'", type); 458 break; 459 } 460 461 if (to_stdout) 462 exit_code = dump_database (context, type, database, db); 463 else 464 exit_code = propagate_database (context, type, database, 465 db, ccache, optidx, argc, argv); 466 467 if(ccache != NULL) 468 krb5_cc_destroy(context, ccache); 469 470 if(db != NULL) 471 (*db->hdb_destroy)(context, db); 472 473 krb5_free_context(context); 474 return exit_code; 475} 476