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#include "iprop.h"
35#include <sl.h>
36#include <parse_time.h>
37#include "iprop-commands.h"
38
39RCSID("$Id$");
40
41static krb5_context context;
42
43static kadm5_server_context *
44get_kadmin_context(const char *config_file, char *realm)
45{
46    kadm5_config_params conf;
47    krb5_error_code ret;
48    void *kadm_handle;
49    char **files;
50
51    if (config_file == NULL) {
52	char *file;
53	asprintf(&file, "%s/kdc.conf", hdb_db_dir(context));
54	if (file == NULL)
55	    errx(1, "out of memory");
56	config_file = file;
57    }
58
59    ret = krb5_prepend_config_files_default(config_file, &files);
60    if (ret)
61	krb5_err(context, 1, ret, "getting configuration files");
62
63    ret = krb5_set_config_files(context, files);
64    krb5_free_config_files(files);
65    if (ret)
66	krb5_err(context, 1, ret, "reading configuration files");
67
68    memset(&conf, 0, sizeof(conf));
69    if(realm) {
70	conf.mask |= KADM5_CONFIG_REALM;
71	conf.realm = realm;
72    }
73
74    ret = kadm5_init_with_password_ctx (context,
75					KADM5_ADMIN_SERVICE,
76					NULL,
77					KADM5_ADMIN_SERVICE,
78					&conf, 0, 0,
79					&kadm_handle);
80    if (ret)
81	krb5_err (context, 1, ret, "kadm5_init_with_password_ctx");
82
83    return (kadm5_server_context *)kadm_handle;
84}
85
86/*
87 * dump log
88 */
89
90static const char *op_names[] = {
91    "get",
92    "delete",
93    "create",
94    "rename",
95    "chpass",
96    "modify",
97    "randkey",
98    "get_privs",
99    "get_princs",
100    "chpass_with_key",
101    "nop"
102};
103
104static void
105print_entry(kadm5_server_context *server_context,
106	    uint32_t ver,
107	    time_t timestamp,
108	    enum kadm_ops op,
109	    uint32_t len,
110	    krb5_storage *sp,
111	    void *ctx)
112{
113    char t[256];
114    int32_t mask;
115    hdb_entry ent;
116    krb5_principal source;
117    char *name1, *name2;
118    krb5_data data;
119    krb5_context scontext = server_context->context;
120
121    off_t end = krb5_storage_seek(sp, 0, SEEK_CUR) + len;
122
123    krb5_error_code ret;
124
125    strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S", localtime(&timestamp));
126
127    if((int)op < (int)kadm_get || (int)op > (int)kadm_nop) {
128	printf("unknown op: %d\n", op);
129	krb5_storage_seek(sp, end, SEEK_SET);
130	return;
131    }
132
133    printf ("%s: ver = %u, timestamp = %s, len = %u\n",
134	    op_names[op], ver, t, len);
135    switch(op) {
136    case kadm_delete:
137	ret = krb5_unparse_name(scontext, source, &name1);
138	if (ret == 0)
139	    ret = krb5_ret_principal(sp, &source);
140	if (ret)
141	    krb5_err(scontext, 1, ret, "Failed to read a delete record");
142	printf("    %s\n", name1);
143	free(name1);
144	krb5_free_principal(scontext, source);
145	break;
146    case kadm_rename:
147	ret = krb5_data_alloc(&data, len);
148	if (ret == 0)
149	    krb5_ret_principal(sp, &source);
150	if (ret == 0 && krb5_storage_read(sp, data.data, data.length))
151	    ret = errno;
152	if (ret == 0)
153	    ret = hdb_value2entry(scontext, &data, &ent);
154	if (ret == 0)
155	    ret = krb5_unparse_name(scontext, source, &name1);
156	if (ret == 0)
157	    ret = krb5_unparse_name(scontext, ent.principal, &name2);
158	if (ret)
159	    krb5_err (scontext, 1, ret, "kadm_rename: data alloc: %d", len);
160	printf("    %s -> %s\n", name1, name2);
161	free(name1);
162	free(name2);
163	krb5_free_principal(scontext, source);
164	free_hdb_entry(&ent);
165	break;
166    case kadm_create:
167	ret = krb5_data_alloc(&data, len);
168	if (ret == 0 && krb5_storage_read(sp, data.data, data.length))
169	    ret = errno;
170	if (ret == 0)
171	    ret = hdb_value2entry(scontext, &data, &ent);
172	if (ret)
173	    krb5_err (scontext, 1, ret, "kadm_create: data alloc: %d", len);
174	mask = ~0;
175	goto foo;
176    case kadm_modify:
177	ret = krb5_data_alloc(&data, len);
178	if (ret == 0)
179	    ret = krb5_ret_int32(sp, &mask);
180	if (ret == 0 && krb5_storage_read(sp, data.data, data.length))
181	    ret = errno;
182	if (ret == 0)
183	    ret = hdb_value2entry(scontext, &data, &ent);
184	if (ret)
185	    krb5_err (scontext, 1, ret, "kadm_modify: data alloc: %d", len);
186    foo:
187	if(ent.principal /* mask & KADM5_PRINCIPAL */) {
188	    ret = krb5_unparse_name(scontext, ent.principal, &name1);
189	    if (ret)
190		krb5_err(scontext, 1, ret,
191			 "Failed to process a create or modify record");
192	    printf("    principal = %s\n", name1);
193	    free(name1);
194	}
195	if(mask & KADM5_PRINC_EXPIRE_TIME) {
196	    if(ent.valid_end == NULL) {
197		strlcpy(t, "never", sizeof(t));
198	    } else {
199		strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S",
200			 localtime(ent.valid_end));
201	    }
202	    printf("    expires = %s\n", t);
203	}
204	if(mask & KADM5_PW_EXPIRATION) {
205	    if(ent.pw_end == NULL) {
206		strlcpy(t, "never", sizeof(t));
207	    } else {
208		strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S",
209			 localtime(ent.pw_end));
210	    }
211	    printf("    password exp = %s\n", t);
212	}
213	if(mask & KADM5_LAST_PWD_CHANGE) {
214	}
215	if(mask & KADM5_ATTRIBUTES) {
216	    unparse_flags(HDBFlags2int(ent.flags),
217			  asn1_HDBFlags_units(), t, sizeof(t));
218	    printf("    attributes = %s\n", t);
219	}
220	if(mask & KADM5_MAX_LIFE) {
221	    if(ent.max_life == NULL)
222		strlcpy(t, "for ever", sizeof(t));
223	    else
224		unparse_time(*ent.max_life, t, sizeof(t));
225	    printf("    max life = %s\n", t);
226	}
227	if(mask & KADM5_MAX_RLIFE) {
228	    if(ent.max_renew == NULL)
229		strlcpy(t, "for ever", sizeof(t));
230	    else
231		unparse_time(*ent.max_renew, t, sizeof(t));
232	    printf("    max rlife = %s\n", t);
233	}
234	if(mask & KADM5_MOD_TIME) {
235	    printf("    mod time\n");
236	}
237	if(mask & KADM5_MOD_NAME) {
238	    printf("    mod name\n");
239	}
240	if(mask & KADM5_KVNO) {
241	    printf("    kvno = %d\n", ent.kvno);
242	}
243	if(mask & KADM5_MKVNO) {
244	    printf("    mkvno\n");
245	}
246	if(mask & KADM5_AUX_ATTRIBUTES) {
247	    printf("    aux attributes\n");
248	}
249	if(mask & KADM5_POLICY) {
250	    printf("    policy\n");
251	}
252	if(mask & KADM5_POLICY_CLR) {
253	    printf("    mod time\n");
254	}
255	if(mask & KADM5_LAST_SUCCESS) {
256	    printf("    last success\n");
257	}
258	if(mask & KADM5_LAST_FAILED) {
259	    printf("    last failed\n");
260	}
261	if(mask & KADM5_FAIL_AUTH_COUNT) {
262	    printf("    fail auth count\n");
263	}
264	if(mask & KADM5_KEY_DATA) {
265	    printf("    key data\n");
266	}
267	if(mask & KADM5_TL_DATA) {
268	    printf("    tl data\n");
269	}
270	free_hdb_entry(&ent);
271	break;
272    case kadm_nop :
273	break;
274    default:
275	krb5_errx(scontext, 1, "Unknown record type");
276    }
277    krb5_storage_seek(sp, end, SEEK_SET);
278}
279
280int
281iprop_dump(struct dump_options *opt, int argc, char **argv)
282{
283    kadm5_server_context *server_context;
284    krb5_error_code ret;
285
286    server_context = get_kadmin_context(opt->config_file_string,
287					opt->realm_string);
288
289    ret = kadm5_log_init (server_context);
290    if (ret)
291	krb5_err (context, 1, ret, "kadm5_log_init");
292
293    ret = kadm5_log_foreach (server_context, print_entry, NULL);
294    if(ret)
295	krb5_warn(context, ret, "kadm5_log_foreach");
296
297    ret = kadm5_log_end (server_context);
298    if (ret)
299	krb5_warn(context, ret, "kadm5_log_end");
300    return 0;
301}
302
303int
304iprop_truncate(struct truncate_options *opt, int argc, char **argv)
305{
306    kadm5_server_context *server_context;
307    krb5_error_code ret;
308
309    server_context = get_kadmin_context(opt->config_file_string,
310					opt->realm_string);
311
312    ret = kadm5_log_truncate (server_context);
313    if (ret)
314	krb5_err (context, 1, ret, "kadm5_log_truncate");
315
316    return 0;
317}
318
319int
320last_version(struct last_version_options *opt, int argc, char **argv)
321{
322    kadm5_server_context *server_context;
323    krb5_error_code ret;
324    uint32_t version;
325
326    server_context = get_kadmin_context(opt->config_file_string,
327					opt->realm_string);
328
329    ret = kadm5_log_init (server_context);
330    if (ret)
331	krb5_err (context, 1, ret, "kadm5_log_init");
332
333    ret = kadm5_log_get_version (server_context, &version);
334    if (ret)
335	krb5_err (context, 1, ret, "kadm5_log_get_version");
336
337    ret = kadm5_log_end (server_context);
338    if (ret)
339	krb5_warn(context, ret, "kadm5_log_end");
340
341    printf("version: %lu\n", (unsigned long)version);
342
343    return 0;
344}
345
346/*
347 * Replay log
348 */
349
350int start_version = -1;
351int end_version = -1;
352
353static void
354apply_entry(kadm5_server_context *server_context,
355	    uint32_t ver,
356	    time_t timestamp,
357	    enum kadm_ops op,
358	    uint32_t len,
359	    krb5_storage *sp,
360	    void *ctx)
361{
362    struct replay_options *opt = ctx;
363    krb5_error_code ret;
364
365    if((opt->start_version_integer != -1 && ver < (uint32_t)opt->start_version_integer) ||
366       (opt->end_version_integer != -1 && ver > (uint32_t)opt->end_version_integer)) {
367	/* XXX skip this entry */
368	krb5_storage_seek(sp, len, SEEK_CUR);
369	return;
370    }
371    printf ("ver %u... ", ver);
372    fflush (stdout);
373
374    ret = kadm5_log_replay (server_context,
375			    op, ver, len, sp);
376    if (ret)
377	krb5_warn (server_context->context, ret, "kadm5_log_replay");
378
379    printf ("done\n");
380}
381
382int
383iprop_replay(struct replay_options *opt, int argc, char **argv)
384{
385    kadm5_server_context *server_context;
386    krb5_error_code ret;
387
388    server_context = get_kadmin_context(opt->config_file_string,
389					opt->realm_string);
390
391    ret = server_context->db->hdb_open(context,
392				       server_context->db,
393				       O_RDWR | O_CREAT, 0600);
394    if (ret)
395	krb5_err (context, 1, ret, "db->open");
396
397    ret = kadm5_log_init (server_context);
398    if (ret)
399	krb5_err (context, 1, ret, "kadm5_log_init");
400
401    ret = kadm5_log_foreach (server_context, apply_entry, opt);
402    if(ret)
403	krb5_warn(context, ret, "kadm5_log_foreach");
404    ret = kadm5_log_end (server_context);
405    if (ret)
406	krb5_warn(context, ret, "kadm5_log_end");
407    ret = server_context->db->hdb_close (context, server_context->db);
408    if (ret)
409	krb5_err (context, 1, ret, "db->close");
410
411    return 0;
412}
413
414static int help_flag;
415static int version_flag;
416
417static struct getargs args[] = {
418    { "version", 	0,	arg_flag, 	&version_flag,
419      NULL,		NULL
420    },
421    { "help", 	'h', 	arg_flag, 	&help_flag,
422      NULL, NULL
423    }
424};
425
426static int num_args = sizeof(args) / sizeof(args[0]);
427
428int
429help(void *opt, int argc, char **argv)
430{
431    if(argc == 0) {
432	sl_help(commands, 1, argv - 1 /* XXX */);
433    } else {
434	SL_cmd *c = sl_match (commands, argv[0], 0);
435 	if(c == NULL) {
436	    fprintf (stderr, "No such command: %s. "
437		     "Try \"help\" for a list of commands\n",
438		     argv[0]);
439	} else {
440	    if(c->func) {
441		static char shelp[] = "--help";
442		char *fake[3];
443		fake[0] = argv[0];
444		fake[1] = shelp;
445		fake[2] = NULL;
446		(*c->func)(2, fake);
447		fprintf(stderr, "\n");
448	    }
449	    if(c->help && *c->help)
450		fprintf (stderr, "%s\n", c->help);
451	    if((++c)->name && c->func == NULL) {
452		int f = 0;
453		fprintf (stderr, "Synonyms:");
454		while (c->name && c->func == NULL) {
455		    fprintf (stderr, "%s%s", f ? ", " : " ", (c++)->name);
456		    f = 1;
457		}
458		fprintf (stderr, "\n");
459	    }
460	}
461    }
462    return 0;
463}
464
465static void
466usage(int status)
467{
468    arg_printusage(args, num_args, NULL, "command");
469    exit(status);
470}
471
472int
473main(int argc, char **argv)
474{
475    int optidx = 0;
476    krb5_error_code ret;
477
478    setprogname(argv[0]);
479
480    if(getarg(args, num_args, argc, argv, &optidx))
481	usage(1);
482    if(help_flag)
483	usage(0);
484    if(version_flag) {
485	print_version(NULL);
486	exit(0);
487    }
488    argc -= optidx;
489    argv += optidx;
490    if(argc == 0)
491	usage(1);
492
493    ret = krb5_init_context(&context);
494    if (ret)
495	errx(1, "krb5_init_context failed with: %d\n", ret);
496
497    ret = sl_command(commands, argc, argv);
498    if(ret == -1)
499	warnx ("unrecognized command: %s", argv[0]);
500    return ret;
501}
502