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