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