1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* src/lib/krb5/krb/ai_authdata.c - auth-indicator authdata module */
3 /*
4 * Copyright (C) 2016 by Red Hat, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "k5-int.h"
34 #include "authdata.h"
35 #include "auth_con.h"
36 #include "int-proto.h"
37
38 struct authind_context {
39 krb5_data **indicators;
40 };
41
42 static krb5_error_code
authind_init(krb5_context kcontext,void ** plugin_context)43 authind_init(krb5_context kcontext, void **plugin_context)
44 {
45 *plugin_context = NULL;
46 return 0;
47 }
48
49 static void
authind_flags(krb5_context kcontext,void * plugin_context,krb5_authdatatype ad_type,krb5_flags * flags)50 authind_flags(krb5_context kcontext, void *plugin_context,
51 krb5_authdatatype ad_type, krb5_flags *flags)
52 {
53 *flags = AD_CAMMAC_PROTECTED;
54 }
55
56 static krb5_error_code
authind_request_init(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void ** request_context)57 authind_request_init(krb5_context kcontext, krb5_authdata_context context,
58 void *plugin_context, void **request_context)
59 {
60 krb5_error_code ret = 0;
61 struct authind_context *aictx;
62
63 *request_context = NULL;
64
65 aictx = k5alloc(sizeof(*aictx), &ret);
66 if (aictx == NULL)
67 return ret;
68 aictx->indicators = NULL;
69 *request_context = aictx;
70 return ret;
71 }
72
73 static krb5_error_code
authind_import_authdata(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,krb5_authdata ** authdata,krb5_boolean kdc_issued,krb5_const_principal kdc_issuer)74 authind_import_authdata(krb5_context kcontext, krb5_authdata_context context,
75 void *plugin_context, void *request_context,
76 krb5_authdata **authdata, krb5_boolean kdc_issued,
77 krb5_const_principal kdc_issuer)
78 {
79 struct authind_context *aictx = request_context;
80 krb5_error_code ret = 0;
81 krb5_data **indps = NULL;
82 size_t i;
83
84 for (i = 0; authdata != NULL && authdata[i] != NULL; i++) {
85 ret = k5_authind_decode(authdata[i], &indps);
86 if (ret)
87 goto cleanup;
88 }
89
90 if (indps != NULL && *indps != NULL) {
91 aictx->indicators = indps;
92 indps = NULL;
93 }
94
95 cleanup:
96 k5_free_data_ptr_list(indps);
97 return ret;
98 }
99
100 static void
authind_request_fini(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context)101 authind_request_fini(krb5_context kcontext, krb5_authdata_context context,
102 void *plugin_context, void *request_context)
103 {
104 struct authind_context *aictx = request_context;
105
106 if (aictx != NULL) {
107 k5_free_data_ptr_list(aictx->indicators);
108 free(aictx);
109 }
110 }
111
112 /* This is a non-URI "local attribute" that is implementation defined. */
113 static krb5_data authind_attr = {
114 KV5M_DATA,
115 sizeof("auth-indicators") - 1,
116 "auth-indicators"
117 };
118
119 static krb5_error_code
authind_get_attribute_types(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,krb5_data ** out_attrs)120 authind_get_attribute_types(krb5_context kcontext,
121 krb5_authdata_context context,
122 void *plugin_context, void *request_context,
123 krb5_data **out_attrs)
124 {
125 struct authind_context *aictx = request_context;
126 krb5_error_code ret;
127 krb5_data *attrs;
128
129 *out_attrs = NULL;
130
131 if (aictx->indicators == NULL || *aictx->indicators == NULL)
132 return ENOENT;
133
134 attrs = k5calloc(2, sizeof(*attrs), &ret);
135 if (attrs == NULL)
136 return ENOMEM;
137
138 ret = krb5int_copy_data_contents(kcontext, &authind_attr, &attrs[0]);
139 if (ret)
140 goto cleanup;
141
142 attrs[1].data = NULL;
143 attrs[1].length = 0;
144
145 *out_attrs = attrs;
146 attrs = NULL;
147
148 cleanup:
149 krb5int_free_data_list(kcontext, attrs);
150 return ret;
151 }
152
153 static krb5_error_code
authind_get_attribute(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,const krb5_data * attribute,krb5_boolean * authenticated,krb5_boolean * complete,krb5_data * value,krb5_data * display_value,int * more)154 authind_get_attribute(krb5_context kcontext, krb5_authdata_context context,
155 void *plugin_context, void *request_context,
156 const krb5_data *attribute, krb5_boolean *authenticated,
157 krb5_boolean *complete, krb5_data *value,
158 krb5_data *display_value, int *more)
159 {
160 struct authind_context *aictx = request_context;
161 krb5_error_code ret;
162 int ind;
163
164 if (!data_eq(*attribute, authind_attr))
165 return ENOENT;
166
167 /* *more will be -1 on the first call, or the next index on subsequent
168 * calls. */
169 ind = (*more < 0) ? 0 : *more;
170 if (aictx->indicators == NULL || aictx->indicators[ind] == NULL)
171 return ENOENT;
172
173 ret = krb5int_copy_data_contents(kcontext, aictx->indicators[ind], value);
174 if (ret)
175 return ret;
176
177 /* Set *more to the next index, or to 0 if there are no more. */
178 *more = (aictx->indicators[ind + 1] == NULL) ? 0 : ind + 1;
179
180 /* Indicators are delivered in a CAMMAC verified outside of this module,
181 * so these are authenticated values. */
182 *authenticated = TRUE;
183 *complete = TRUE;
184
185 return ret;
186 }
187
188 static krb5_error_code
authind_set_attribute(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,krb5_boolean complete,const krb5_data * attribute,const krb5_data * value)189 authind_set_attribute(krb5_context kcontext, krb5_authdata_context context,
190 void *plugin_context, void *request_context,
191 krb5_boolean complete, const krb5_data *attribute,
192 const krb5_data *value)
193 {
194 /* Indicators are imported from ticket authdata, not set by this module. */
195 if (!data_eq(*attribute, authind_attr))
196 return ENOENT;
197
198 return EPERM;
199 }
200
201 static krb5_error_code
authind_size(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,size_t * sizep)202 authind_size(krb5_context kcontext, krb5_authdata_context context,
203 void *plugin_context, void *request_context, size_t *sizep)
204 {
205 struct authind_context *aictx = request_context;
206 size_t i;
207
208 /* Add the indicator count. */
209 *sizep += sizeof(int32_t);
210
211 /* Add each indicator's length and value. */
212 for (i = 0; aictx->indicators != NULL && aictx->indicators[i] != NULL; i++)
213 *sizep += sizeof(int32_t) + aictx->indicators[i]->length;
214
215 return 0;
216 }
217
218 static krb5_error_code
authind_externalize(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,uint8_t ** buffer,size_t * lenremain)219 authind_externalize(krb5_context kcontext, krb5_authdata_context context,
220 void *plugin_context, void *request_context,
221 uint8_t **buffer, size_t *lenremain)
222 {
223 struct authind_context *aictx = request_context;
224 krb5_error_code ret = 0;
225 uint8_t *bp = *buffer;
226 size_t remain = *lenremain;
227 size_t i, count;
228
229 if (aictx->indicators == NULL)
230 return krb5_ser_pack_int32(0, buffer, lenremain);
231
232 /* Serialize the indicator count. */
233 for (count = 0; aictx->indicators[count] != NULL; count++);
234 ret = krb5_ser_pack_int32(count, &bp, &remain);
235 if (ret)
236 return ret;
237
238 for (i = 0; aictx->indicators[i] != NULL; i++) {
239 /* Serialize the length and indicator value. */
240 ret = krb5_ser_pack_int32(aictx->indicators[i]->length, &bp, &remain);
241 if (ret)
242 return ret;
243 ret = krb5_ser_pack_bytes((uint8_t *)aictx->indicators[i]->data,
244 aictx->indicators[i]->length, &bp, &remain);
245 if (ret)
246 return ret;
247 }
248
249 *buffer = bp;
250 *lenremain = remain;
251 return ret;
252 }
253
254
255 static krb5_error_code
authind_internalize(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,uint8_t ** buffer,size_t * lenremain)256 authind_internalize(krb5_context kcontext, krb5_authdata_context context,
257 void *plugin_context, void *request_context,
258 uint8_t **buffer, size_t *lenremain)
259 {
260 struct authind_context *aictx = request_context;
261 krb5_error_code ret;
262 int32_t count, len, i;
263 uint8_t *bp = *buffer;
264 size_t remain = *lenremain;
265 krb5_data **inds = NULL;
266
267 /* Get the count. */
268 ret = krb5_ser_unpack_int32(&count, &bp, &remain);
269 if (ret)
270 return ret;
271
272 if (count < 0 || (size_t)count > remain)
273 return ERANGE;
274
275 if (count > 0) {
276 inds = k5calloc(count + 1, sizeof(*inds), &ret);
277 if (inds == NULL)
278 return errno;
279 }
280
281 for (i = 0; i < count; i++) {
282 /* Get the length. */
283 ret = krb5_ser_unpack_int32(&len, &bp, &remain);
284 if (ret)
285 goto cleanup;
286 if (len < 0 || (size_t)len > remain) {
287 ret = ERANGE;
288 goto cleanup;
289 }
290
291 /* Get the indicator. */
292 inds[i] = k5alloc(sizeof(*inds[i]), &ret);
293 if (inds[i] == NULL)
294 goto cleanup;
295 ret = alloc_data(inds[i], len);
296 if (ret)
297 goto cleanup;
298 ret = krb5_ser_unpack_bytes((uint8_t *)inds[i]->data, len, &bp,
299 &remain);
300 if (ret)
301 goto cleanup;
302 }
303
304 k5_free_data_ptr_list(aictx->indicators);
305 aictx->indicators = inds;
306 inds = NULL;
307
308 *buffer = bp;
309 *lenremain = remain;
310
311 cleanup:
312 k5_free_data_ptr_list(inds);
313 return ret;
314 }
315
316 static krb5_authdatatype authind_ad_types[] = {
317 KRB5_AUTHDATA_AUTH_INDICATOR, 0
318 };
319
320 krb5plugin_authdata_client_ftable_v0 k5_authind_ad_client_ftable = {
321 "authentication-indicators",
322 authind_ad_types,
323 authind_init,
324 NULL, /* fini */
325 authind_flags,
326 authind_request_init,
327 authind_request_fini,
328 authind_get_attribute_types,
329 authind_get_attribute,
330 authind_set_attribute,
331 NULL, /* delete_attribute_proc */
332 NULL, /* export_authdata */
333 authind_import_authdata,
334 NULL, /* export_internal */
335 NULL, /* free_internal */
336 NULL, /* verify */
337 authind_size,
338 authind_externalize,
339 authind_internalize,
340 NULL /* authind_copy */
341 };
342