/ kdc / hprop.c
hprop.c
  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  	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  
 90  krb5_error_code
 91  v5_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  
127  struct 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  
146  static int num_args = sizeof(args) / sizeof(args[0]);
147  
148  static void
149  usage(int ret)
150  {
151      arg_printusage (args, num_args, NULL, "[host[:port]] ...");
152      exit (ret);
153  }
154  
155  static void
156  get_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  
201  enum hprop_source {
202      HPROP_HEIMDAL = 1,
203      HPROP_MIT_DUMP
204  };
205  
206  struct {
207      int type;
208      const char *name;
209  } types[] = {
210      { HPROP_HEIMDAL,	"heimdal" },
211      { HPROP_MIT_DUMP,	"mit-dump" }
212  };
213  
214  static int
215  parse_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  
225  static int
226  iterate (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  
251  static int
252  dump_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  
274  static int
275  propagate_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  
385  int
386  main(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  }