xref: /freebsd/crypto/krb5/src/lib/krb5/krb/ai_authdata.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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