1/*
2 * Copyright (c) 1997-2004 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 "ktutil_locl.h"
35
36RCSID("$Id$");
37
38
39static krb5_boolean
40compare_keyblock(const krb5_keyblock *a, const krb5_keyblock *b)
41{
42    if(a->keytype != b->keytype ||
43       a->keyvalue.length != b->keyvalue.length ||
44       memcmp(a->keyvalue.data, b->keyvalue.data, a->keyvalue.length) != 0)
45	return FALSE;
46    return TRUE;
47}
48
49int
50kt_copy (struct copy_options *opt, int argc, char **argv)
51{
52    krb5_keytab src_keytab = NULL, dst_keytab = NULL;
53    krb5_principal match_principal = NULL;
54    krb5_keytab_entry entry, dummy;
55    const char *from = argv[0];
56    const char *to = argv[1];
57    krb5_kt_cursor cursor;
58    krb5_error_code ret;
59
60    ret = krb5_kt_resolve (context, from, &src_keytab);
61    if (ret) {
62	krb5_warn (context, ret, "resolving src keytab `%s'", from);
63	goto out;
64    }
65
66    ret = krb5_kt_resolve (context, to, &dst_keytab);
67    if (ret) {
68	krb5_warn (context, ret, "resolving dst keytab `%s'", to);
69	goto out;
70    }
71
72    if (opt->match_principal_string) {
73	ret = krb5_parse_name(context,
74			      opt->match_principal_string,
75			      &match_principal);
76	if (ret) {
77	    krb5_warn (context, ret, "failed parsing match principal `%s'",
78		       opt->match_principal_string);
79	    goto out;
80	}
81	if (verbose_flag) {
82	    char *str = NULL;
83	    ret = krb5_unparse_name(context, match_principal, &str);
84	    if (ret == 0) {
85		fprintf(stderr, "matching on principal %s\n", str);
86		krb5_xfree(str);
87	    }
88	}
89    }
90
91    ret = krb5_kt_start_seq_get (context, src_keytab, &cursor);
92    if (ret) {
93	krb5_warn (context, ret, "krb5_kt_start_seq_get %s", keytab_string);
94	goto out;
95    }
96
97    if (verbose_flag)
98	fprintf(stderr, "copying %s to %s\n", from, to);
99
100    while((ret = krb5_kt_next_entry(context, src_keytab,
101				    &entry, &cursor)) == 0) {
102	char *name_str;
103	char *etype_str;
104	ret = krb5_unparse_name (context, entry.principal, &name_str);
105	if(ret) {
106	    krb5_warn(context, ret, "krb5_unparse_name");
107	    name_str = NULL; /* XXX */
108	}
109	ret = krb5_enctype_to_string(context, entry.keyblock.keytype, &etype_str);
110	if(ret) {
111	    krb5_warn(context, ret, "krb5_enctype_to_string");
112	    etype_str = NULL; /* XXX */
113	}
114
115	if (match_principal &&
116	    !krb5_principal_match(context, entry.principal, match_principal))
117	{
118	    if (verbose_flag) {
119		krb5_warnx(context, "skipping %s, keytype %s, kvno %d",
120			   name_str, etype_str, entry.vno);
121	    }
122	    free(name_str);
123	    free(etype_str);
124	    continue;
125	}
126
127	ret = krb5_kt_get_entry(context, dst_keytab,
128				entry.principal,
129				entry.vno,
130				entry.keyblock.keytype,
131				&dummy);
132	if(ret == 0) {
133	    /* this entry is already in the new keytab, so no need to
134               copy it; if the keyblocks are not the same, something
135               is weird, so complain about that */
136	    if(!compare_keyblock(&entry.keyblock, &dummy.keyblock)) {
137		krb5_warnx(context, "entry with different keyvalue "
138			   "already exists for %s, keytype %s, kvno %d",
139			   name_str, etype_str, entry.vno);
140	    }
141	    krb5_kt_free_entry(context, &dummy);
142	    krb5_kt_free_entry (context, &entry);
143	    free(name_str);
144	    free(etype_str);
145	    continue;
146	} else if(ret != KRB5_KT_NOTFOUND) {
147	    krb5_warn (context, ret, "%s: fetching %s/%s/%u",
148		       to, name_str, etype_str, entry.vno);
149	    krb5_kt_free_entry (context, &entry);
150	    free(name_str);
151	    free(etype_str);
152	    break;
153	}
154	if (verbose_flag)
155	    fprintf (stderr, "copying %s, keytype %s, kvno %d\n", name_str,
156		     etype_str, entry.vno);
157	ret = krb5_kt_add_entry (context, dst_keytab, &entry);
158	krb5_kt_free_entry (context, &entry);
159	if (ret) {
160	    krb5_warn (context, ret, "%s: adding %s/%s/%u",
161		       to, name_str, etype_str, entry.vno);
162	    free(name_str);
163	    free(etype_str);
164	    break;
165	}
166	free(name_str);
167	free(etype_str);
168    }
169    krb5_kt_end_seq_get (context, src_keytab, &cursor);
170
171  out:
172    if (match_principal)
173	krb5_free_principal(context, match_principal);
174    if (src_keytab)
175	krb5_kt_close (context, src_keytab);
176    if (dst_keytab)
177	krb5_kt_close (context, dst_keytab);
178    return ret != 0;
179}
180