xref: /freebsd/crypto/krb5/src/lib/krb5/krb/authdata_dec.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/authdata_dec.c */
3 /*
4  * Copyright 1990 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 /*
27  * Copyright (c) 2006-2008, Novell, Inc.
28  * All rights reserved.
29  *
30  * Redistribution and use in source and binary forms, with or without
31  * modification, are permitted provided that the following conditions are met:
32  *
33  *   * Redistributions of source code must retain the above copyright notice,
34  *       this list of conditions and the following disclaimer.
35  *   * Redistributions in binary form must reproduce the above copyright
36  *       notice, this list of conditions and the following disclaimer in the
37  *       documentation and/or other materials provided with the distribution.
38  *   * The copyright holder's name is not used to endorse or promote products
39  *       derived from this software without specific prior written permission.
40  *
41  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
42  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
45  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
46  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
48  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
49  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
51  * POSSIBILITY OF SUCH DAMAGE.
52  */
53 
54 #include "k5-int.h"
55 #include "int-proto.h"
56 
57 krb5_error_code KRB5_CALLCONV
krb5_decode_authdata_container(krb5_context context,krb5_authdatatype type,const krb5_authdata * container,krb5_authdata *** authdata)58 krb5_decode_authdata_container(krb5_context context,
59                                krb5_authdatatype type,
60                                const krb5_authdata *container,
61                                krb5_authdata ***authdata)
62 {
63     krb5_error_code code;
64     krb5_data data;
65 
66     *authdata = NULL;
67 
68     if ((container->ad_type & AD_TYPE_FIELD_TYPE_MASK) != type)
69         return EINVAL;
70 
71     data.length = container->length;
72     data.data = (char *)container->contents;
73 
74     code = decode_krb5_authdata(&data, authdata);
75     if (code)
76         return code;
77 
78     return 0;
79 }
80 
81 struct find_authdata_context {
82     krb5_authdata **out;
83     size_t space;
84     size_t length;
85 };
86 
87 static krb5_error_code
grow_find_authdata(krb5_context context,struct find_authdata_context * fctx,krb5_authdata * elem)88 grow_find_authdata(krb5_context context, struct find_authdata_context *fctx,
89                    krb5_authdata *elem)
90 {
91     krb5_error_code retval = 0;
92     if (fctx->length == fctx->space) {
93         krb5_authdata **new;
94         if (fctx->space >= 256) {
95             k5_setmsg(context, ERANGE,
96                       "More than 256 authdata matched a query");
97             return ERANGE;
98         }
99         new       = realloc(fctx->out,
100                             sizeof (krb5_authdata *)*(2*fctx->space+1));
101         if (new == NULL)
102             return ENOMEM;
103         fctx->out = new;
104         fctx->space *=2;
105     }
106     fctx->out[fctx->length+1] = NULL;
107     retval = krb5int_copy_authdatum(context, elem,
108                                     &fctx->out[fctx->length]);
109     if (retval == 0)
110         fctx->length++;
111     return retval;
112 }
113 
114 static krb5_error_code
find_authdata_1(krb5_context context,krb5_authdata * const * in_authdat,krb5_authdatatype ad_type,struct find_authdata_context * fctx,int from_ap_req)115 find_authdata_1(krb5_context context, krb5_authdata *const *in_authdat,
116                 krb5_authdatatype ad_type, struct find_authdata_context *fctx,
117                 int from_ap_req)
118 {
119     size_t i = 0;
120     krb5_error_code retval = 0;
121 
122     for (i = 0; in_authdat[i] && retval == 0; i++) {
123         krb5_authdata *ad = in_authdat[i];
124         krb5_authdata **decoded_container;
125 
126         switch (ad->ad_type) {
127         case KRB5_AUTHDATA_IF_RELEVANT:
128             if (retval == 0)
129                 retval = krb5_decode_authdata_container(context,
130                                                         ad->ad_type,
131                                                         ad,
132                                                         &decoded_container);
133             if (retval == 0) {
134                 retval = find_authdata_1(context,
135                                          decoded_container,
136                                          ad_type,
137                                          fctx,
138                                          from_ap_req);
139                 krb5_free_authdata(context, decoded_container);
140             }
141             break;
142         case KRB5_AUTHDATA_SIGNTICKET:
143         case KRB5_AUTHDATA_KDC_ISSUED:
144         case KRB5_AUTHDATA_WIN2K_PAC:
145         case KRB5_AUTHDATA_CAMMAC:
146         case KRB5_AUTHDATA_AUTH_INDICATOR:
147             if (from_ap_req)
148                 continue;
149         default:
150             if (ad->ad_type == ad_type && retval == 0)
151                 retval = grow_find_authdata(context, fctx, ad);
152             break;
153         }
154     }
155 
156     return retval;
157 }
158 
159 krb5_error_code KRB5_CALLCONV
krb5_find_authdata(krb5_context context,krb5_authdata * const * ticket_authdata,krb5_authdata * const * ap_req_authdata,krb5_authdatatype ad_type,krb5_authdata *** results)160 krb5_find_authdata(krb5_context context,
161                    krb5_authdata *const *ticket_authdata,
162                    krb5_authdata *const *ap_req_authdata,
163                    krb5_authdatatype ad_type, krb5_authdata ***results)
164 {
165     krb5_error_code retval = 0;
166     struct find_authdata_context fctx;
167     fctx.length = 0;
168     fctx.space = 2;
169     fctx.out = calloc(fctx.space+1, sizeof (krb5_authdata *));
170     *results = NULL;
171     if (fctx.out == NULL)
172         return ENOMEM;
173     if (ticket_authdata)
174         retval = find_authdata_1( context, ticket_authdata, ad_type, &fctx, 0);
175     if ((retval==0) && ap_req_authdata)
176         retval = find_authdata_1( context, ap_req_authdata, ad_type, &fctx, 1);
177     if ((retval== 0) && fctx.length)
178         *results = fctx.out;
179     else krb5_free_authdata(context, fctx.out);
180     return retval;
181 }
182 
183 krb5_error_code KRB5_CALLCONV
krb5_verify_authdata_kdc_issued(krb5_context context,const krb5_keyblock * key,const krb5_authdata * ad_kdcissued,krb5_principal * issuer,krb5_authdata *** authdata)184 krb5_verify_authdata_kdc_issued(krb5_context context,
185                                 const krb5_keyblock *key,
186                                 const krb5_authdata *ad_kdcissued,
187                                 krb5_principal *issuer,
188                                 krb5_authdata ***authdata)
189 {
190     krb5_error_code code;
191     krb5_ad_kdcissued *ad_kdci;
192     krb5_data data, *data2;
193     krb5_boolean valid = FALSE;
194 
195     if ((ad_kdcissued->ad_type & AD_TYPE_FIELD_TYPE_MASK) !=
196         KRB5_AUTHDATA_KDC_ISSUED)
197         return EINVAL;
198 
199     if (issuer != NULL)
200         *issuer = NULL;
201     if (authdata != NULL)
202         *authdata = NULL;
203 
204     data.length = ad_kdcissued->length;
205     data.data = (char *)ad_kdcissued->contents;
206 
207     code = decode_krb5_ad_kdcissued(&data, &ad_kdci);
208     if (code != 0)
209         return code;
210 
211     if (!krb5_c_is_keyed_cksum(ad_kdci->ad_checksum.checksum_type)) {
212         krb5_free_ad_kdcissued(context, ad_kdci);
213         return KRB5KRB_AP_ERR_INAPP_CKSUM;
214     }
215 
216     code = encode_krb5_authdata(ad_kdci->elements, &data2);
217     if (code != 0) {
218         krb5_free_ad_kdcissued(context, ad_kdci);
219         return code;
220     }
221 
222     code = krb5_c_verify_checksum(context, key,
223                                   KRB5_KEYUSAGE_AD_KDCISSUED_CKSUM,
224                                   data2, &ad_kdci->ad_checksum, &valid);
225     if (code != 0) {
226         krb5_free_ad_kdcissued(context, ad_kdci);
227         krb5_free_data(context, data2);
228         return code;
229     }
230 
231     krb5_free_data(context, data2);
232 
233     if (valid == FALSE) {
234         krb5_free_ad_kdcissued(context, ad_kdci);
235         return KRB5KRB_AP_ERR_BAD_INTEGRITY;
236     }
237 
238     if (issuer != NULL) {
239         *issuer = ad_kdci->i_principal;
240         ad_kdci->i_principal = NULL;
241     }
242 
243     if (authdata != NULL) {
244         *authdata = ad_kdci->elements;
245         ad_kdci->elements = NULL;
246     }
247 
248     krb5_free_ad_kdcissued(context, ad_kdci);
249 
250     return 0;
251 }
252 
253 /*
254  * Decode authentication indicator strings from authdata and return as an
255  * allocated array of krb5_data pointers.  The caller must initialize
256  * *indicators to NULL for the first call, and successive calls will reallocate
257  * and append to the indicators array.
258  */
259 krb5_error_code
k5_authind_decode(const krb5_authdata * ad,krb5_data *** indicators)260 k5_authind_decode(const krb5_authdata *ad, krb5_data ***indicators)
261 {
262     krb5_error_code ret = 0;
263     krb5_data der_ad, **strdata = NULL, **ai_list = *indicators;
264     size_t count, scount;
265 
266     if (ad == NULL || ad->ad_type != KRB5_AUTHDATA_AUTH_INDICATOR)
267         goto cleanup;
268 
269     /* Count existing auth indicators. */
270     for (count = 0; ai_list != NULL && ai_list[count] != NULL; count++);
271 
272     der_ad = make_data(ad->contents, ad->length);
273     ret = decode_utf8_strings(&der_ad, &strdata);
274     if (ret)
275         return ret;
276 
277     /* Count new auth indicators. */
278     for (scount = 0; strdata[scount] != NULL; scount++);
279 
280     ai_list = realloc(ai_list, (count + scount + 1) * sizeof(*ai_list));
281     if (ai_list == NULL) {
282         ret = ENOMEM;
283         goto cleanup;
284     }
285     *indicators = ai_list;
286 
287     /* Steal decoder-allocated pointers and free the container array. */
288     memcpy(ai_list + count, strdata, scount * sizeof(*strdata));
289     ai_list[count + scount] = NULL;
290     free(strdata);
291     strdata = NULL;
292 
293 cleanup:
294     k5_free_data_ptr_list(strdata);
295     return ret;
296 }
297