1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/preauth/securid_sam2/securid_sam2_main.c */
3 /*
4  * Copyright (C) 2009, 2010 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) 2002 Naval Research Laboratory (NRL/CCS)
28  *
29  * Permission to use, copy, modify and distribute this software and its
30  * documentation is hereby granted, provided that both the copyright
31  * notice and this permission notice appear in all copies of the software,
32  * derivative works or modified versions, and any portions thereof.
33  *
34  * NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND
35  * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
36  * RESULTING FROM THE USE OF THIS SOFTWARE.
37  */
38 
39 #include <k5-int.h>
40 #include <krb5/preauth_plugin.h>
41 #include <kdb.h>
42 #include "extern.h"
43 
44 static struct {
45     char* name;
46     int   sam_type;
47 } *sam_ptr, sam_inst_map[] = {
48     { "SECURID", PA_SAM_TYPE_SECURID, },
49     { "GRAIL", PA_SAM_TYPE_GRAIL, },
50     { 0, 0 },
51 };
52 
53 krb5_error_code
sam_get_db_entry(krb5_context context,krb5_principal client,int * sam_type,struct _krb5_db_entry_new ** db_entry)54 sam_get_db_entry(krb5_context context, krb5_principal client,
55                  int *sam_type, struct _krb5_db_entry_new **db_entry)
56 {
57     struct _krb5_db_entry_new *assoc = NULL;
58     krb5_principal newp = NULL;
59     int probeslot;
60     void *ptr = NULL;
61     krb5_error_code retval;
62 
63     if (db_entry)
64         *db_entry = NULL;
65     retval = krb5_copy_principal(context, client, &newp);
66     if (retval) {
67         com_err("krb5kdc", retval, "copying client name for preauth probe");
68         return retval;
69     }
70 
71     probeslot = krb5_princ_size(context, newp)++;
72     ptr = realloc(krb5_princ_name(context, newp),
73                   krb5_princ_size(context, newp) * sizeof(krb5_data));
74     if (ptr == NULL) {
75         retval = ENOMEM;
76         goto cleanup;
77     }
78     krb5_princ_name(context, newp) = ptr;
79 
80     for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) {
81         if (*sam_type && *sam_type != sam_ptr->sam_type)
82             continue;
83 
84         krb5_princ_component(context,newp,probeslot)->data = sam_ptr->name;
85         krb5_princ_component(context,newp,probeslot)->length =
86             strlen(sam_ptr->name);
87         retval = krb5_db_get_principal(context, newp, 0, &assoc);
88         if (!retval)
89             break;
90     }
91 cleanup:
92     if (ptr) {
93         krb5_princ_component(context,newp,probeslot)->data = 0;
94         krb5_princ_component(context,newp,probeslot)->length = 0;
95         krb5_free_principal(context, newp);
96     }
97     if (probeslot)
98         krb5_princ_size(context, newp)--;
99     if (retval)
100         return retval;
101     if (sam_ptr->sam_type)  {
102         /* Found entry of type sam_ptr->sam_type */
103         if (sam_type)
104             *sam_type = sam_ptr->sam_type;
105         if (db_entry)
106             *db_entry = assoc;
107         else
108             krb5_db_free_principal(context, assoc);
109         return 0;
110     } else {
111         return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
112     }
113 }
114 
115 krb5_error_code
sam_make_challenge(krb5_context context,krb5_sam_challenge_2_body * sc2b,krb5_keyblock * cksum_key,krb5_sam_challenge_2 * sc2_out)116 sam_make_challenge(krb5_context context, krb5_sam_challenge_2_body *sc2b,
117                    krb5_keyblock *cksum_key, krb5_sam_challenge_2 *sc2_out)
118 {
119     krb5_error_code retval;
120     krb5_checksum **cksum_array = NULL;
121     krb5_checksum *cksum = NULL;
122     krb5_cksumtype cksumtype;
123     krb5_data *encoded_challenge_body = NULL;
124 
125     if (!cksum_key)
126         return KRB5_PREAUTH_NO_KEY;
127     if (!sc2_out || !sc2b)
128         return KRB5KDC_ERR_PREAUTH_FAILED;
129 
130     retval = encode_krb5_sam_challenge_2_body(sc2b, &encoded_challenge_body);
131     if (retval || !encoded_challenge_body) {
132         encoded_challenge_body = NULL;
133         goto cksum_cleanup;
134     }
135 
136     cksum_array = calloc(2, sizeof(krb5_checksum *));
137     if (!cksum_array) {
138         retval = ENOMEM;
139         goto cksum_cleanup;
140     }
141 
142     cksum = (krb5_checksum *)k5alloc(sizeof(krb5_checksum), &retval);
143     if (retval)
144         goto cksum_cleanup;
145     cksum_array[0] = cksum;
146     cksum_array[1] = NULL;
147 
148     retval = krb5int_c_mandatory_cksumtype(context, cksum_key->enctype,
149                                            &cksumtype);
150     if (retval)
151         goto cksum_cleanup;
152 
153     retval = krb5_c_make_checksum(context, cksumtype, cksum_key,
154                                   KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM,
155                                   encoded_challenge_body, cksum);
156     if (retval)
157         goto cksum_cleanup;
158 
159     sc2_out->sam_cksum = cksum_array;
160     sc2_out->sam_challenge_2_body = *encoded_challenge_body;
161     return 0;
162 
163 cksum_cleanup:
164     krb5_free_data(context, encoded_challenge_body);
165     free(cksum_array);
166     free(cksum);
167     return retval;
168 }
169 
170 static void
kdc_include_padata(krb5_context context,krb5_kdc_req * request,krb5_kdcpreauth_callbacks cb,krb5_kdcpreauth_rock rock,krb5_kdcpreauth_moddata moddata,krb5_preauthtype pa_type,krb5_kdcpreauth_edata_respond_fn respond,void * arg)171 kdc_include_padata(krb5_context context, krb5_kdc_req *request,
172                    krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
173                    krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
174                    krb5_kdcpreauth_edata_respond_fn respond, void *arg)
175 {
176     krb5_error_code retval;
177     krb5_keyblock *client_key = NULL;
178     krb5_sam_challenge_2 sc2;
179     int sam_type = 0;             /* unknown */
180     krb5_db_entry *sam_db_entry = NULL, *client;
181     krb5_data *encoded_challenge = NULL;
182     krb5_pa_data *pa_data = NULL;
183 
184     memset(&sc2, 0, sizeof(sc2));
185 
186     client = cb->client_entry(context, rock);
187     retval = sam_get_db_entry(context, client->princ, &sam_type,
188                               &sam_db_entry);
189     if (retval)
190         goto cleanup;
191     retval = cb->client_keys(context, rock, &client_key);
192     if (retval)
193         goto cleanup;
194     if (client_key->enctype == 0) {
195         retval = KRB5KDC_ERR_ETYPE_NOSUPP;
196         com_err("krb5kdc", retval,
197                 "No client keys found in processing SAM2 challenge");
198         goto cleanup;
199     }
200 
201     if (sam_type == 0) {
202         retval = KRB5_PREAUTH_BAD_TYPE;
203         goto cleanup;
204     }
205 
206     /*
207      * Defer getting the key for the SAM principal associated with the client
208      * until the mechanism-specific code.  The mechanism may want to get a
209      * specific keytype.
210      */
211 
212     switch (sam_type) {
213 #ifdef ARL_SECURID_PREAUTH
214     case PA_SAM_TYPE_SECURID:
215         retval = get_securid_edata_2(context, client, client_key, &sc2);
216         if (retval)
217             goto cleanup;
218         break;
219 #endif  /* ARL_SECURID_PREAUTH */
220 #ifdef GRAIL_PREAUTH
221     case PA_SAM_TYPE_GRAIL:
222         retval = get_grail_edata(context, client, client_key, &sc2);
223         if (retval)
224             goto cleanup;
225         break;
226 #endif /* GRAIL_PREAUTH */
227     default:
228         retval = KRB5_PREAUTH_BAD_TYPE;
229         goto cleanup;
230     }
231 
232     retval = encode_krb5_sam_challenge_2(&sc2, &encoded_challenge);
233     if (retval) {
234         com_err("krb5kdc", retval,
235                 "while encoding SECURID SAM_CHALLENGE_2");
236         goto cleanup;
237     }
238 
239     pa_data = k5alloc(sizeof(*pa_data), &retval);
240     if (pa_data == NULL)
241         goto cleanup;
242     pa_data->magic = KV5M_PA_DATA;
243     pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE_2;
244     pa_data->contents = (krb5_octet *)encoded_challenge->data;
245     pa_data->length = encoded_challenge->length;
246     encoded_challenge->data = NULL;
247 
248 cleanup:
249     krb5_free_data(context, encoded_challenge);
250     if (sam_db_entry)
251         krb5_db_free_principal(context, sam_db_entry);
252     cb->free_keys(context, rock, client_key);
253     (*respond)(arg, retval, pa_data);
254 }
255 
256 static void
kdc_verify_preauth(krb5_context context,krb5_data * req_pkt,krb5_kdc_req * request,krb5_enc_tkt_part * enc_tkt_reply,krb5_pa_data * pa_data,krb5_kdcpreauth_callbacks cb,krb5_kdcpreauth_rock rock,krb5_kdcpreauth_moddata moddata,krb5_kdcpreauth_verify_respond_fn respond,void * arg)257 kdc_verify_preauth(krb5_context context, krb5_data *req_pkt,
258                    krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
259                    krb5_pa_data *pa_data, krb5_kdcpreauth_callbacks cb,
260                    krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata,
261                    krb5_kdcpreauth_verify_respond_fn respond, void *arg)
262 {
263     krb5_error_code retval, saved_retval = 0;
264     krb5_sam_response_2 *sr2 = NULL;
265     krb5_data scratch, *scratch2, *e_data = NULL;
266     char *client_name = NULL;
267     krb5_sam_challenge_2 *out_sc2 = NULL;
268     krb5_db_entry *client = cb->client_entry(context, rock);
269 
270     scratch.data = (char *) pa_data->contents;
271     scratch.length = pa_data->length;
272 
273     retval = krb5_unparse_name(context, client->princ, &client_name);
274     if (retval)
275         goto cleanup;
276 
277     retval = decode_krb5_sam_response_2(&scratch, &sr2);
278     if (retval) {
279         com_err("krb5kdc",  retval,
280                 "while decoding SAM_RESPONSE_2 in verify_sam_response_2");
281         sr2 = NULL;
282         goto cleanup;
283     }
284 
285     switch (sr2->sam_type) {
286 #ifdef ARL_SECURID_PREAUTH
287     case PA_SAM_TYPE_SECURID:
288         retval = verify_securid_data_2(context, client, sr2, enc_tkt_reply,
289                                        pa_data, &out_sc2);
290         if (retval)
291             goto cleanup;
292         break;
293 #endif  /* ARL_SECURID_PREAUTH */
294 #ifdef GRAIL_PREAUTH
295     case PA_SAM_TYPE_GRAIL:
296         retval = verify_grail_data(context, client, sr2, enc_tkt_reply,
297                                    pa_data, &out_sc2);
298         if (retval)
299             goto cleanup;
300         break;
301 #endif /* GRAIL_PREAUTH */
302     default:
303         retval = KRB5_PREAUTH_BAD_TYPE;
304         com_err("krb5kdc", retval, "while verifying SAM 2 data");
305         break;
306     }
307 
308     /*
309      * It is up to the method-specific verify routine to set the
310      * ticket flags to indicate TKT_FLG_HW_AUTH and/or
311      * TKT_FLG_PRE_AUTH.  Some methods may require more than one round
312      * of dialog with the client and must return successfully from
313      * their verify routine.  If does not set the TGT flags, the
314      * required_preauth conditions will not be met and it will try
315      * again to get enough preauth data from the client.  Do not set
316      * TGT flags here.
317      */
318 cleanup:
319     /*
320      * Note that e_data is an output even in error conditions.  If we
321      * successfully encode the output e_data, we return whatever error is
322      * received above.  Otherwise we return the encoding error.
323      */
324     saved_retval = retval;
325     if (out_sc2) {
326         krb5_pa_data pa_out;
327         krb5_pa_data *pa_array[2];
328         pa_array[0] = &pa_out;
329         pa_array[1] = NULL;
330         pa_out.pa_type = KRB5_PADATA_SAM_CHALLENGE_2;
331         retval = encode_krb5_sam_challenge_2(out_sc2, &scratch2);
332         krb5_free_sam_challenge_2(context, out_sc2);
333         if (retval)
334             goto encode_error;
335         pa_out.contents = (krb5_octet *) scratch2->data;
336         pa_out.length = scratch2->length;
337         retval = encode_krb5_padata_sequence(pa_array, &e_data);
338         krb5_free_data(context, scratch2);
339     }
340 encode_error:
341     krb5_free_sam_response_2(context, sr2);
342     free(client_name);
343     if (retval == 0)
344         retval = saved_retval;
345 
346     (*respond)(arg, retval, NULL, NULL, NULL);
347 }
348 
349 
350 static int
kdc_preauth_flags(krb5_context context,krb5_preauthtype patype)351 kdc_preauth_flags(krb5_context context, krb5_preauthtype patype)
352 {
353     return PA_HARDWARE;
354 }
355 
356 krb5_preauthtype supported_pa_types[] = {
357     KRB5_PADATA_SAM_RESPONSE_2, 0};
358 
359 krb5_error_code
360 kdcpreauth_securid_sam2_initvt(krb5_context context, int maj_ver, int min_ver,
361                                krb5_plugin_vtable vtable);
362 
363 krb5_error_code
kdcpreauth_securid_sam2_initvt(krb5_context context,int maj_ver,int min_ver,krb5_plugin_vtable vtable)364 kdcpreauth_securid_sam2_initvt(krb5_context context, int maj_ver, int min_ver,
365                                krb5_plugin_vtable vtable)
366 {
367     krb5_kdcpreauth_vtable vt;
368 
369     if (maj_ver != 1)
370         return KRB5_PLUGIN_VER_NOTSUPP;
371     vt = (krb5_kdcpreauth_vtable)vtable;
372     vt->name = "securid_sam2";
373     vt->pa_type_list = supported_pa_types;
374     vt->flags = kdc_preauth_flags;
375     vt->edata = kdc_include_padata;
376     vt->verify = kdc_verify_preauth;
377     return 0;
378 }
379