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	krb5_ret_principal(sp, &source);
138	krb5_unparse_name(scontext, source, &name1);
139	printf("    %s\n", name1);
140	free(name1);
141	krb5_free_principal(scontext, source);
142	break;
143    case kadm_rename:
144	ret = krb5_data_alloc(&data, len);
145	if (ret)
146	    krb5_err (scontext, 1, ret, "kadm_rename: data alloc: %d", len);
147	krb5_ret_principal(sp, &source);
148	krb5_storage_read(sp, data.data, data.length);
149	hdb_value2entry(scontext, &data, &ent);
150	krb5_unparse_name(scontext, source, &name1);
151	krb5_unparse_name(scontext, ent.principal, &name2);
152	printf("    %s -> %s\n", name1, name2);
153	free(name1);
154	free(name2);
155	krb5_free_principal(scontext, source);
156	free_hdb_entry(&ent);
157	break;
158    case kadm_create:
159	ret = krb5_data_alloc(&data, len);
160	if (ret)
161	    krb5_err (scontext, 1, ret, "kadm_create: data alloc: %d", len);
162	krb5_storage_read(sp, data.data, data.length);
163	ret = hdb_value2entry(scontext, &data, &ent);
164	if(ret)
165	    abort();
166	mask = ~0;
167	goto foo;
168    case kadm_modify:
169	ret = krb5_data_alloc(&data, len);
170	if (ret)
171	    krb5_err (scontext, 1, ret, "kadm_modify: data alloc: %d", len);
172	krb5_ret_int32(sp, &mask);
173	krb5_storage_read(sp, data.data, data.length);
174	ret = hdb_value2entry(scontext, &data, &ent);
175	if(ret)
176	    abort();
177    foo:
178	if(ent.principal /* mask & KADM5_PRINCIPAL */) {
179	    krb5_unparse_name(scontext, ent.principal, &name1);
180	    printf("    principal = %s\n", name1);
181	    free(name1);
182	}
183	if(mask & KADM5_PRINC_EXPIRE_TIME) {
184	    if(ent.valid_end == NULL) {
185		strlcpy(t, "never", sizeof(t));
186	    } else {
187		strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S",
188			 localtime(ent.valid_end));
189	    }
190	    printf("    expires = %s\n", t);
191	}
192	if(mask & KADM5_PW_EXPIRATION) {
193	    if(ent.pw_end == NULL) {
194		strlcpy(t, "never", sizeof(t));
195	    } else {
196		strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S",
197			 localtime(ent.pw_end));
198	    }
199	    printf("    password exp = %s\n", t);
200	}
201	if(mask & KADM5_LAST_PWD_CHANGE) {
202	}
203	if(mask & KADM5_ATTRIBUTES) {
204	    unparse_flags(HDBFlags2int(ent.flags),
205			  asn1_HDBFlags_units(), t, sizeof(t));
206	    printf("    attributes = %s\n", t);
207	}
208	if(mask & KADM5_MAX_LIFE) {
209	    if(ent.max_life == NULL)
210		strlcpy(t, "for ever", sizeof(t));
211	    else
212		unparse_time(*ent.max_life, t, sizeof(t));
213	    printf("    max life = %s\n", t);
214	}
215	if(mask & KADM5_MAX_RLIFE) {
216	    if(ent.max_renew == NULL)
217		strlcpy(t, "for ever", sizeof(t));
218	    else
219		unparse_time(*ent.max_renew, t, sizeof(t));
220	    printf("    max rlife = %s\n", t);
221	}
222	if(mask & KADM5_MOD_TIME) {
223	    printf("    mod time\n");
224	}
225	if(mask & KADM5_MOD_NAME) {
226	    printf("    mod name\n");
227	}
228	if(mask & KADM5_KVNO) {
229	    printf("    kvno = %d\n", ent.kvno);
230	}
231	if(mask & KADM5_MKVNO) {
232	    printf("    mkvno\n");
233	}
234	if(mask & KADM5_AUX_ATTRIBUTES) {
235	    printf("    aux attributes\n");
236	}
237	if(mask & KADM5_POLICY) {
238	    printf("    policy\n");
239	}
240	if(mask & KADM5_POLICY_CLR) {
241	    printf("    mod time\n");
242	}
243	if(mask & KADM5_LAST_SUCCESS) {
244	    printf("    last success\n");
245	}
246	if(mask & KADM5_LAST_FAILED) {
247	    printf("    last failed\n");
248	}
249	if(mask & KADM5_FAIL_AUTH_COUNT) {
250	    printf("    fail auth count\n");
251	}
252	if(mask & KADM5_KEY_DATA) {
253	    printf("    key data\n");
254	}
255	if(mask & KADM5_TL_DATA) {
256	    printf("    tl data\n");
257	}
258	free_hdb_entry(&ent);
259	break;
260    case kadm_nop :
261	break;
262    default:
263	abort();
264    }
265    krb5_storage_seek(sp, end, SEEK_SET);
266}
267
268int
269iprop_dump(struct dump_options *opt, int argc, char **argv)
270{
271    kadm5_server_context *server_context;
272    krb5_error_code ret;
273
274    server_context = get_kadmin_context(opt->config_file_string,
275					opt->realm_string);
276
277    ret = kadm5_log_init (server_context);
278    if (ret)
279	krb5_err (context, 1, ret, "kadm5_log_init");
280
281    ret = kadm5_log_foreach (server_context, print_entry, NULL);
282    if(ret)
283	krb5_warn(context, ret, "kadm5_log_foreach");
284
285    ret = kadm5_log_end (server_context);
286    if (ret)
287	krb5_warn(context, ret, "kadm5_log_end");
288    return 0;
289}
290
291int
292iprop_truncate(struct truncate_options *opt, int argc, char **argv)
293{
294    kadm5_server_context *server_context;
295    krb5_error_code ret;
296
297    server_context = get_kadmin_context(opt->config_file_string,
298					opt->realm_string);
299
300    ret = kadm5_log_truncate (server_context);
301    if (ret)
302	krb5_err (context, 1, ret, "kadm5_log_truncate");
303
304    return 0;
305}
306
307int
308last_version(struct last_version_options *opt, int argc, char **argv)
309{
310    kadm5_server_context *server_context;
311    krb5_error_code ret;
312    uint32_t version;
313
314    server_context = get_kadmin_context(opt->config_file_string,
315					opt->realm_string);
316
317    ret = kadm5_log_init (server_context);
318    if (ret)
319	krb5_err (context, 1, ret, "kadm5_log_init");
320
321    ret = kadm5_log_get_version (server_context, &version);
322    if (ret)
323	krb5_err (context, 1, ret, "kadm5_log_get_version");
324
325    ret = kadm5_log_end (server_context);
326    if (ret)
327	krb5_warn(context, ret, "kadm5_log_end");
328
329    printf("version: %lu\n", (unsigned long)version);
330
331    return 0;
332}
333
334/*
335 * Replay log
336 */
337
338int start_version = -1;
339int end_version = -1;
340
341static void
342apply_entry(kadm5_server_context *server_context,
343	    uint32_t ver,
344	    time_t timestamp,
345	    enum kadm_ops op,
346	    uint32_t len,
347	    krb5_storage *sp,
348	    void *ctx)
349{
350    struct replay_options *opt = ctx;
351    krb5_error_code ret;
352
353    if((opt->start_version_integer != -1 && ver < (uint32_t)opt->start_version_integer) ||
354       (opt->end_version_integer != -1 && ver > (uint32_t)opt->end_version_integer)) {
355	/* XXX skip this entry */
356	krb5_storage_seek(sp, len, SEEK_CUR);
357	return;
358    }
359    printf ("ver %u... ", ver);
360    fflush (stdout);
361
362    ret = kadm5_log_replay (server_context,
363			    op, ver, len, sp);
364    if (ret)
365	krb5_warn (server_context->context, ret, "kadm5_log_replay");
366
367    printf ("done\n");
368}
369
370int
371iprop_replay(struct replay_options *opt, int argc, char **argv)
372{
373    kadm5_server_context *server_context;
374    krb5_error_code ret;
375
376    server_context = get_kadmin_context(opt->config_file_string,
377					opt->realm_string);
378
379    ret = server_context->db->hdb_open(context,
380				       server_context->db,
381				       O_RDWR | O_CREAT, 0600);
382    if (ret)
383	krb5_err (context, 1, ret, "db->open");
384
385    ret = kadm5_log_init (server_context);
386    if (ret)
387	krb5_err (context, 1, ret, "kadm5_log_init");
388
389    ret = kadm5_log_foreach (server_context, apply_entry, opt);
390    if(ret)
391	krb5_warn(context, ret, "kadm5_log_foreach");
392    ret = kadm5_log_end (server_context);
393    if (ret)
394	krb5_warn(context, ret, "kadm5_log_end");
395    ret = server_context->db->hdb_close (context, server_context->db);
396    if (ret)
397	krb5_err (context, 1, ret, "db->close");
398
399    return 0;
400}
401
402static int help_flag;
403static int version_flag;
404
405static struct getargs args[] = {
406    { "version", 	0,	arg_flag, 	&version_flag,
407      NULL,		NULL
408    },
409    { "help", 	'h', 	arg_flag, 	&help_flag,
410      NULL, NULL
411    }
412};
413
414static int num_args = sizeof(args) / sizeof(args[0]);
415
416int
417help(void *opt, int argc, char **argv)
418{
419    if(argc == 0) {
420	sl_help(commands, 1, argv - 1 /* XXX */);
421    } else {
422	SL_cmd *c = sl_match (commands, argv[0], 0);
423 	if(c == NULL) {
424	    fprintf (stderr, "No such command: %s. "
425		     "Try \"help\" for a list of commands\n",
426		     argv[0]);
427	} else {
428	    if(c->func) {
429		static char shelp[] = "--help";
430		char *fake[3];
431		fake[0] = argv[0];
432		fake[1] = shelp;
433		fake[2] = NULL;
434		(*c->func)(2, fake);
435		fprintf(stderr, "\n");
436	    }
437	    if(c->help && *c->help)
438		fprintf (stderr, "%s\n", c->help);
439	    if((++c)->name && c->func == NULL) {
440		int f = 0;
441		fprintf (stderr, "Synonyms:");
442		while (c->name && c->func == NULL) {
443		    fprintf (stderr, "%s%s", f ? ", " : " ", (c++)->name);
444		    f = 1;
445		}
446		fprintf (stderr, "\n");
447	    }
448	}
449    }
450    return 0;
451}
452
453static void
454usage(int status)
455{
456    arg_printusage(args, num_args, NULL, "command");
457    exit(status);
458}
459
460int
461main(int argc, char **argv)
462{
463    int optidx = 0;
464    krb5_error_code ret;
465
466    setprogname(argv[0]);
467
468    if(getarg(args, num_args, argc, argv, &optidx))
469	usage(1);
470    if(help_flag)
471	usage(0);
472    if(version_flag) {
473	print_version(NULL);
474	exit(0);
475    }
476    argc -= optidx;
477    argv += optidx;
478    if(argc == 0)
479	usage(1);
480
481    ret = krb5_init_context(&context);
482    if (ret)
483	errx(1, "krb5_init_context failed with: %d\n", ret);
484
485    ret = sl_command(commands, argc, argv);
486    if(ret == -1)
487	warnx ("unrecognized command: %s", argv[0]);
488    return ret;
489}
490