1/*	$NetBSD: keytab_any.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
2
3/*
4 * Copyright (c) 2001-2002 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 "krb5_locl.h"
37
38struct any_data {
39    krb5_keytab kt;
40    char *name;
41    struct any_data *next;
42};
43
44static void
45free_list (krb5_context context, struct any_data *a)
46{
47    struct any_data *next;
48
49    for (; a != NULL; a = next) {
50	next = a->next;
51	free (a->name);
52	if(a->kt)
53	    krb5_kt_close(context, a->kt);
54	free (a);
55    }
56}
57
58static krb5_error_code KRB5_CALLCONV
59any_resolve(krb5_context context, const char *name, krb5_keytab id)
60{
61    struct any_data *a, *a0 = NULL, *prev = NULL;
62    krb5_error_code ret;
63    char buf[256];
64
65    while (strsep_copy(&name, ",", buf, sizeof(buf)) != -1) {
66	a = calloc(1, sizeof(*a));
67	if (a == NULL) {
68	    ret = krb5_enomem(context);
69	    goto fail;
70	}
71	if (a0 == NULL) {
72	    a0 = a;
73	    a->name = strdup(buf);
74	    if (a->name == NULL) {
75		ret = krb5_enomem(context);
76		goto fail;
77	    }
78	} else
79	    a->name = NULL;
80	if (prev != NULL)
81	    prev->next = a;
82	a->next = NULL;
83	ret = krb5_kt_resolve (context, buf, &a->kt);
84	if (ret)
85	    goto fail;
86	prev = a;
87    }
88    if (a0 == NULL) {
89	krb5_set_error_message(context, ENOENT, N_("empty ANY: keytab", ""));
90	return ENOENT;
91    }
92    id->data = a0;
93    return 0;
94 fail:
95    free_list (context, a0);
96    return ret;
97}
98
99static krb5_error_code KRB5_CALLCONV
100any_get_name (krb5_context context,
101	      krb5_keytab id,
102	      char *name,
103	      size_t namesize)
104{
105    struct any_data *a = id->data;
106    strlcpy(name, a->name, namesize);
107    return 0;
108}
109
110static krb5_error_code KRB5_CALLCONV
111any_close (krb5_context context,
112	   krb5_keytab id)
113{
114    struct any_data *a = id->data;
115
116    free_list (context, a);
117    return 0;
118}
119
120struct any_cursor_extra_data {
121    struct any_data *a;
122    krb5_kt_cursor cursor;
123};
124
125static krb5_error_code KRB5_CALLCONV
126any_start_seq_get(krb5_context context,
127		  krb5_keytab id,
128		  krb5_kt_cursor *c)
129{
130    struct any_data *a = id->data;
131    struct any_cursor_extra_data *ed;
132    krb5_error_code ret;
133
134    c->data = malloc (sizeof(struct any_cursor_extra_data));
135    if(c->data == NULL)
136	return krb5_enomem(context);
137    ed = (struct any_cursor_extra_data *)c->data;
138    for (ed->a = a; ed->a != NULL; ed->a = ed->a->next) {
139	ret = krb5_kt_start_seq_get(context, ed->a->kt, &ed->cursor);
140	if (ret == 0)
141	    break;
142    }
143    if (ed->a == NULL) {
144	free (c->data);
145	c->data = NULL;
146	krb5_clear_error_message (context);
147	return KRB5_KT_END;
148    }
149    return 0;
150}
151
152static krb5_error_code KRB5_CALLCONV
153any_next_entry (krb5_context context,
154		krb5_keytab id,
155		krb5_keytab_entry *entry,
156		krb5_kt_cursor *cursor)
157{
158    krb5_error_code ret, ret2;
159    struct any_cursor_extra_data *ed;
160
161    ed = (struct any_cursor_extra_data *)cursor->data;
162    do {
163	ret = krb5_kt_next_entry(context, ed->a->kt, entry, &ed->cursor);
164	if (ret == 0)
165	    return 0;
166	else if (ret != KRB5_KT_END)
167	    return ret;
168
169	ret2 = krb5_kt_end_seq_get (context, ed->a->kt, &ed->cursor);
170	if (ret2)
171	    return ret2;
172	while ((ed->a = ed->a->next) != NULL) {
173	    ret2 = krb5_kt_start_seq_get(context, ed->a->kt, &ed->cursor);
174	    if (ret2 == 0)
175		break;
176	}
177	if (ed->a == NULL) {
178	    krb5_clear_error_message (context);
179	    return KRB5_KT_END;
180	}
181    } while (1);
182}
183
184static krb5_error_code KRB5_CALLCONV
185any_end_seq_get(krb5_context context,
186		krb5_keytab id,
187		krb5_kt_cursor *cursor)
188{
189    krb5_error_code ret = 0;
190    struct any_cursor_extra_data *ed;
191
192    ed = (struct any_cursor_extra_data *)cursor->data;
193    if (ed->a != NULL)
194	ret = krb5_kt_end_seq_get(context, ed->a->kt, &ed->cursor);
195    free (ed);
196    cursor->data = NULL;
197    return ret;
198}
199
200static krb5_error_code KRB5_CALLCONV
201any_add_entry(krb5_context context,
202	      krb5_keytab id,
203	      krb5_keytab_entry *entry)
204{
205    struct any_data *a = id->data;
206    krb5_error_code ret;
207    while(a != NULL) {
208	ret = krb5_kt_add_entry(context, a->kt, entry);
209	if(ret != 0 && ret != KRB5_KT_NOWRITE) {
210	    krb5_set_error_message(context, ret,
211				   N_("failed to add entry to %s", ""),
212				   a->name);
213	    return ret;
214	}
215	a = a->next;
216    }
217    return 0;
218}
219
220static krb5_error_code KRB5_CALLCONV
221any_remove_entry(krb5_context context,
222		 krb5_keytab id,
223		 krb5_keytab_entry *entry)
224{
225    struct any_data *a = id->data;
226    krb5_error_code ret;
227    int found = 0;
228    while(a != NULL) {
229	ret = krb5_kt_remove_entry(context, a->kt, entry);
230	if(ret == 0)
231	    found++;
232	else {
233	    if(ret != KRB5_KT_NOWRITE && ret != KRB5_KT_NOTFOUND) {
234		krb5_set_error_message(context, ret,
235				       N_("Failed to remove keytab "
236					  "entry from %s", "keytab name"),
237				       a->name);
238		return ret;
239	    }
240	}
241	a = a->next;
242    }
243    if(!found)
244	return KRB5_KT_NOTFOUND;
245    return 0;
246}
247
248const krb5_kt_ops krb5_any_ops = {
249    "ANY",
250    any_resolve,
251    any_get_name,
252    any_close,
253    NULL, /* destroy */
254    NULL, /* get */
255    any_start_seq_get,
256    any_next_entry,
257    any_end_seq_get,
258    any_add_entry,
259    any_remove_entry,
260    NULL,
261    0
262};
263