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