0
|
1 /*
|
|
2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
|
3 * Use is subject to license terms.
|
|
4 */
|
|
5
|
|
6 #pragma ident "@(#)kdc_preauth.c 1.11 06/09/27 SMI"
|
|
7
|
|
8 /*
|
|
9 * kdc/kdc_preauth.c
|
|
10 *
|
|
11 * Copyright 1995, 2003 by the Massachusetts Institute of Technology.
|
|
12 * All Rights Reserved.
|
|
13 *
|
|
14 * Export of this software from the United States of America may
|
|
15 * require a specific license from the United States Government.
|
|
16 * It is the responsibility of any person or organization contemplating
|
|
17 * export to obtain such a license before exporting.
|
|
18 *
|
|
19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
|
|
20 * distribute this software and its documentation for any purpose and
|
|
21 * without fee is hereby granted, provided that the above copyright
|
|
22 * notice appear in all copies and that both that copyright notice and
|
|
23 * this permission notice appear in supporting documentation, and that
|
|
24 * the name of M.I.T. not be used in advertising or publicity pertaining
|
|
25 * to distribution of the software without specific, written prior
|
|
26 * permission. Furthermore if you modify this software you must label
|
|
27 * your software as modified software and not distribute it in such a
|
|
28 * fashion that it might be confused with the original M.I.T. software.
|
|
29 * M.I.T. makes no representations about the suitability of
|
|
30 * this software for any purpose. It is provided "as is" without express
|
|
31 * or implied warranty.
|
|
32 *
|
|
33 * Preauthentication routines for the KDC.
|
|
34 */
|
|
35
|
|
36 /*
|
|
37 * Copyright (C) 1998 by the FundsXpress, INC.
|
|
38 *
|
|
39 * All rights reserved.
|
|
40 *
|
|
41 * Export of this software from the United States of America may require
|
|
42 * a specific license from the United States Government. It is the
|
|
43 * responsibility of any person or organization contemplating export to
|
|
44 * obtain such a license before exporting.
|
|
45 *
|
|
46 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
|
|
47 * distribute this software and its documentation for any purpose and
|
|
48 * without fee is hereby granted, provided that the above copyright
|
|
49 * notice appear in all copies and that both that copyright notice and
|
|
50 * this permission notice appear in supporting documentation, and that
|
|
51 * the name of FundsXpress. not be used in advertising or publicity pertaining
|
|
52 * to distribution of the software without specific, written prior
|
|
53 * permission. FundsXpress makes no representations about the suitability of
|
|
54 * this software for any purpose. It is provided "as is" without express
|
|
55 * or implied warranty.
|
|
56 *
|
|
57 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
|
58 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
|
59 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
60 */
|
|
61
|
|
62 #include "k5-int.h"
|
|
63 #include "kdc_util.h"
|
|
64 #include "extern.h"
|
|
65 #include "com_err.h"
|
|
66 #include <assert.h>
|
|
67 #include <stdio.h>
|
|
68 #include "adm_proto.h"
|
|
69 #include <libintl.h>
|
|
70 #include <syslog.h>
|
|
71
|
|
72 #include <assert.h>
|
|
73
|
|
74 /* XXX This is ugly and should be in a header file somewhere */
|
|
75 #ifndef KRB5INT_DES_TYPES_DEFINED
|
|
76 #define KRB5INT_DES_TYPES_DEFINED
|
|
77 typedef unsigned char des_cblock[8]; /* crypto-block size */
|
|
78 #endif
|
|
79 typedef des_cblock mit_des_cblock;
|
|
80 extern void mit_des_fixup_key_parity (mit_des_cblock );
|
|
81 extern int mit_des_is_weak_key (mit_des_cblock );
|
|
82
|
|
83 typedef krb5_error_code (*verify_proc)
|
|
84 (krb5_context, krb5_db_entry *client,
|
|
85 krb5_kdc_req *request,
|
|
86 krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data);
|
|
87
|
|
88 typedef krb5_error_code (*edata_proc)
|
|
89 (krb5_context, krb5_kdc_req *request,
|
|
90 krb5_db_entry *client, krb5_db_entry *server,
|
|
91 krb5_pa_data *data);
|
|
92
|
|
93 typedef krb5_error_code (*return_proc)
|
|
94 (krb5_context, krb5_pa_data * padata,
|
|
95 krb5_db_entry *client,
|
|
96 krb5_kdc_req *request, krb5_kdc_rep *reply,
|
|
97 krb5_key_data *client_key,
|
|
98 krb5_keyblock *encrypting_key,
|
|
99 krb5_pa_data **send_pa);
|
|
100
|
|
101 typedef struct _krb5_preauth_systems {
|
|
102 char * name;
|
|
103 int type;
|
|
104 int flags;
|
|
105 edata_proc get_edata;
|
|
106 verify_proc verify_padata;
|
|
107 return_proc return_padata;
|
|
108 } krb5_preauth_systems;
|
|
109
|
|
110 static krb5_error_code verify_enc_timestamp
|
|
111 (krb5_context, krb5_db_entry *client,
|
|
112 krb5_kdc_req *request,
|
|
113 krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data);
|
|
114
|
|
115 static krb5_error_code get_etype_info
|
|
116 (krb5_context, krb5_kdc_req *request,
|
|
117 krb5_db_entry *client, krb5_db_entry *server,
|
|
118 krb5_pa_data *data);
|
|
119 static krb5_error_code
|
|
120 get_etype_info2(krb5_context context, krb5_kdc_req *request,
|
|
121 krb5_db_entry *client, krb5_db_entry *server,
|
|
122 krb5_pa_data *pa_data);
|
|
123 static krb5_error_code
|
|
124 return_etype_info2(krb5_context, krb5_pa_data * padata,
|
|
125 krb5_db_entry *client,
|
|
126 krb5_kdc_req *request, krb5_kdc_rep *reply,
|
|
127 krb5_key_data *client_key,
|
|
128 krb5_keyblock *encrypting_key,
|
|
129 krb5_pa_data **send_pa);
|
|
130
|
|
131 static krb5_error_code return_pw_salt
|
|
132 (krb5_context, krb5_pa_data * padata,
|
|
133 krb5_db_entry *client,
|
|
134 krb5_kdc_req *request, krb5_kdc_rep *reply,
|
|
135 krb5_key_data *client_key,
|
|
136 krb5_keyblock *encrypting_key,
|
|
137 krb5_pa_data **send_pa);
|
|
138
|
|
139 /* SAM preauth support */
|
|
140 static krb5_error_code verify_sam_response
|
|
141 (krb5_context, krb5_db_entry *client,
|
|
142 krb5_kdc_req *request,
|
|
143 krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data);
|
|
144
|
|
145 static krb5_error_code get_sam_edata
|
|
146 (krb5_context, krb5_kdc_req *request,
|
|
147 krb5_db_entry *client, krb5_db_entry *server,
|
|
148 krb5_pa_data *data);
|
|
149 static krb5_error_code return_sam_data
|
|
150 (krb5_context, krb5_pa_data * padata,
|
|
151 krb5_db_entry *client,
|
|
152 krb5_kdc_req *request, krb5_kdc_rep *reply,
|
|
153 krb5_key_data *client_key,
|
|
154 krb5_keyblock *encrypting_key,
|
|
155 krb5_pa_data **send_pa);
|
|
156 /*
|
|
157 * Preauth property flags
|
|
158 */
|
|
159 #define PA_HARDWARE 0x00000001
|
|
160 #define PA_REQUIRED 0x00000002
|
|
161 #define PA_SUFFICIENT 0x00000004
|
|
162 /* Not really a padata, so don't include it in the etype list*/
|
|
163 #define PA_PSEUDO 0x00000008
|
|
164
|
|
165 static krb5_preauth_systems preauth_systems[] = {
|
|
166 {
|
|
167 "timestamp",
|
|
168 KRB5_PADATA_ENC_TIMESTAMP,
|
|
169 0,
|
|
170 0,
|
|
171 verify_enc_timestamp,
|
|
172 0
|
|
173 },
|
|
174 {
|
|
175 "etype-info",
|
|
176 KRB5_PADATA_ETYPE_INFO,
|
|
177 0,
|
|
178 get_etype_info,
|
|
179 0,
|
|
180 0
|
|
181 },
|
|
182 {
|
|
183 "etype-info2",
|
|
184 KRB5_PADATA_ETYPE_INFO2,
|
|
185 0,
|
|
186 get_etype_info2,
|
|
187 0,
|
|
188 return_etype_info2
|
|
189 },
|
|
190 {
|
|
191 "pw-salt",
|
|
192 KRB5_PADATA_PW_SALT,
|
|
193 PA_PSEUDO, /* Don't include this in the error list */
|
|
194 0,
|
|
195 0,
|
|
196 return_pw_salt
|
|
197 },
|
|
198 {
|
|
199 "sam-response",
|
|
200 KRB5_PADATA_SAM_RESPONSE,
|
|
201 0,
|
|
202 0,
|
|
203 verify_sam_response,
|
|
204 return_sam_data
|
|
205 },
|
|
206 {
|
|
207 "sam-challenge",
|
|
208 KRB5_PADATA_SAM_CHALLENGE,
|
|
209 PA_HARDWARE, /* causes get_preauth_hint_list to use this */
|
|
210 get_sam_edata,
|
|
211 0,
|
|
212 0
|
|
213 },
|
|
214 { "[end]", -1,}
|
|
215 };
|
|
216
|
|
217 #define MAX_PREAUTH_SYSTEMS (sizeof(preauth_systems)/sizeof(preauth_systems[0]))
|
|
218
|
|
219 static krb5_error_code
|
|
220 find_pa_system(int type, krb5_preauth_systems **preauth)
|
|
221 {
|
|
222 krb5_preauth_systems *ap = preauth_systems;
|
|
223
|
|
224 while ((ap->type != -1) && (ap->type != type))
|
|
225 ap++;
|
|
226 if (ap->type == -1)
|
|
227 return(KRB5_PREAUTH_BAD_TYPE);
|
|
228 *preauth = ap;
|
|
229 return 0;
|
|
230 }
|
|
231
|
|
232 const char *missing_required_preauth(krb5_db_entry *client,
|
|
233 krb5_db_entry *server,
|
|
234 krb5_enc_tkt_part *enc_tkt_reply)
|
|
235 {
|
|
236 #if 0
|
|
237 /*
|
|
238 * If this is the pwchange service, and the pre-auth bit is set,
|
|
239 * allow it even if the HW preauth would normally be required.
|
|
240 *
|
|
241 * Sandia national labs wanted this for some strange reason... we
|
|
242 * leave it disabled normally.
|
|
243 */
|
|
244 if (isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE) &&
|
|
245 isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
|
|
246 return 0;
|
|
247 #endif
|
|
248
|
|
249 #ifdef DEBUG
|
|
250 krb5_klog_syslog (LOG_DEBUG,
|
|
251 "client needs %spreauth, %shw preauth; request has %spreauth, %shw preauth",
|
|
252 isflagset (client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) ? "" : "no ",
|
|
253 isflagset (client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) ? "" : "no ",
|
|
254 isflagset (enc_tkt_reply->flags, TKT_FLG_PRE_AUTH) ? "" : "no ",
|
|
255 isflagset (enc_tkt_reply->flags, TKT_FLG_HW_AUTH) ? "" : "no ");
|
|
256 #endif
|
|
257
|
|
258 if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
|
|
259 !isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
|
|
260 return "NEEDED_PREAUTH";
|
|
261
|
|
262 if (isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
|
|
263 !isflagset(enc_tkt_reply->flags, TKT_FLG_HW_AUTH))
|
|
264 return "NEEDED_HW_PREAUTH";
|
|
265
|
|
266 return 0;
|
|
267 }
|
|
268
|
|
269 void get_preauth_hint_list(krb5_kdc_req *request, krb5_db_entry *client,
|
|
270 krb5_db_entry *server, krb5_data *e_data)
|
|
271 {
|
|
272 int hw_only;
|
|
273 krb5_preauth_systems *ap;
|
|
274 krb5_pa_data **pa_data, **pa;
|
|
275 krb5_data *edat;
|
|
276 krb5_error_code retval;
|
|
277
|
|
278 /* Zero these out in case we need to abort */
|
|
279 e_data->length = 0;
|
|
280 e_data->data = 0;
|
|
281
|
|
282 hw_only = isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH);
|
|
283 pa_data = malloc(sizeof(krb5_pa_data *) * (MAX_PREAUTH_SYSTEMS+1));
|
|
284 if (pa_data == 0)
|
|
285 return;
|
|
286 memset(pa_data, 0, sizeof(krb5_pa_data *) * (MAX_PREAUTH_SYSTEMS+1));
|
|
287 pa = pa_data;
|
|
288
|
|
289 for (ap = preauth_systems; ap->type != -1; ap++) {
|
|
290 if (hw_only && !(ap->flags & PA_HARDWARE))
|
|
291 continue;
|
|
292 if (ap->flags & PA_PSEUDO)
|
|
293 continue;
|
|
294 *pa = malloc(sizeof(krb5_pa_data));
|
|
295 if (*pa == 0)
|
|
296 goto errout;
|
|
297 memset(*pa, 0, sizeof(krb5_pa_data));
|
|
298 (*pa)->magic = KV5M_PA_DATA;
|
|
299 (*pa)->pa_type = ap->type;
|
|
300 if (ap->get_edata) {
|
|
301 retval = (ap->get_edata)(kdc_context, request, client, server, *pa);
|
|
302 if (retval) {
|
|
303 /* just failed on this type, continue */
|
|
304 free(*pa);
|
|
305 *pa = 0;
|
|
306 continue;
|
|
307 }
|
|
308 }
|
|
309 pa++;
|
|
310 }
|
|
311 if (pa_data[0] == 0) {
|
|
312 krb5_klog_syslog (LOG_INFO,
|
|
313 "%spreauth required but hint list is empty",
|
|
314 hw_only ? "hw" : "");
|
|
315 }
|
|
316 retval = encode_krb5_padata_sequence((const krb5_pa_data **) pa_data,
|
|
317 &edat);
|
|
318 if (retval)
|
|
319 goto errout;
|
|
320 *e_data = *edat;
|
|
321 free(edat);
|
|
322
|
|
323 errout:
|
|
324 krb5_free_pa_data(kdc_context, pa_data);
|
|
325 return;
|
|
326 }
|
|
327
|
|
328 /*
|
|
329 * This routine is called to verify the preauthentication information
|
|
330 * for a V5 request.
|
|
331 *
|
|
332 * Returns 0 if the pre-authentication is valid, non-zero to indicate
|
|
333 * an error code of some sort.
|
|
334 */
|
|
335
|
|
336 krb5_error_code
|
|
337 check_padata (krb5_context context, krb5_db_entry *client,
|
|
338 krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply)
|
|
339 {
|
|
340 krb5_error_code retval = 0;
|
|
341 krb5_pa_data **padata;
|
|
342 krb5_preauth_systems *pa_sys;
|
|
343 int pa_ok = 0, pa_found = 0;
|
|
344
|
|
345 if (request->padata == 0)
|
|
346 return 0;
|
|
347
|
|
348 #ifdef DEBUG
|
|
349 krb5_klog_syslog (LOG_DEBUG, "checking padata");
|
|
350 #endif
|
|
351 for (padata = request->padata; *padata; padata++) {
|
|
352 #ifdef DEBUG
|
|
353 krb5_klog_syslog (LOG_DEBUG, ".. pa_type 0x%x", (*padata)->pa_type);
|
|
354 #endif
|
|
355 if (find_pa_system((*padata)->pa_type, &pa_sys))
|
|
356 continue;
|
|
357 #ifdef DEBUG
|
|
358 krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", pa_sys->name);
|
|
359 #endif
|
|
360 if (pa_sys->verify_padata == 0)
|
|
361 continue;
|
|
362 pa_found++;
|
|
363 retval = pa_sys->verify_padata(context, client, request,
|
|
364 enc_tkt_reply, *padata);
|
|
365 if (retval) {
|
|
366 krb5_klog_syslog (LOG_INFO, "preauth (%s) verify failure: %s",
|
|
367 pa_sys->name, error_message (retval));
|
|
368 if (pa_sys->flags & PA_REQUIRED) {
|
|
369 pa_ok = 0;
|
|
370 break;
|
|
371 }
|
|
372 } else {
|
|
373 #ifdef DEBUG
|
|
374 krb5_klog_syslog (LOG_DEBUG, ".. .. ok");
|
|
375 #endif
|
|
376 pa_ok = 1;
|
|
377 if (pa_sys->flags & PA_SUFFICIENT)
|
|
378 break;
|
|
379 }
|
|
380 }
|
|
381 if (pa_ok)
|
|
382 return 0;
|
|
383
|
|
384 /* pa system was not found, but principal doesn't require preauth */
|
|
385 if (!pa_found &&
|
|
386 !isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
|
|
387 !isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH))
|
|
388 return 0;
|
|
389
|
|
390 if (!pa_found)
|
|
391 krb5_klog_syslog (LOG_INFO, "no valid preauth type found: %s",
|
|
392 error_message (retval));
|
|
393 /* The following switch statement allows us
|
|
394 * to return some preauth system errors back to the client.
|
|
395 */
|
|
396 switch(retval) {
|
|
397 case KRB5KRB_AP_ERR_BAD_INTEGRITY:
|
|
398 case KRB5KRB_AP_ERR_SKEW:
|
|
399 return retval;
|
|
400 default:
|
|
401 return KRB5KDC_ERR_PREAUTH_FAILED;
|
|
402 }
|
|
403 }
|
|
404
|
|
405 /*
|
|
406 * return_padata creates any necessary preauthentication
|
|
407 * structures which should be returned by the KDC to the client
|
|
408 */
|
|
409 krb5_error_code
|
|
410 return_padata(krb5_context context, krb5_db_entry *client,
|
|
411 krb5_kdc_req *request, krb5_kdc_rep *reply,
|
|
412 krb5_key_data *client_key, krb5_keyblock *encrypting_key)
|
|
413 {
|
|
414 krb5_error_code retval;
|
|
415 krb5_pa_data ** padata;
|
|
416 krb5_pa_data ** send_pa_list;
|
|
417 krb5_pa_data ** send_pa;
|
|
418 krb5_pa_data * pa = 0;
|
|
419 krb5_preauth_systems * ap;
|
|
420 int size = 0;
|
|
421
|
|
422 for (ap = preauth_systems; ap->type != -1; ap++) {
|
|
423 if (ap->return_padata)
|
|
424 size++;
|
|
425 }
|
|
426
|
|
427 if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL)
|
|
428 return ENOMEM;
|
|
429
|
|
430 send_pa = send_pa_list;
|
|
431 *send_pa = 0;
|
|
432
|
|
433 for (ap = preauth_systems; ap->type != -1; ap++) {
|
|
434 if (ap->return_padata == 0)
|
|
435 continue;
|
|
436 pa = 0;
|
|
437 if (request->padata) {
|
|
438 for (padata = request->padata; *padata; padata++) {
|
|
439 if ((*padata)->pa_type == ap->type) {
|
|
440 pa = *padata;
|
|
441 break;
|
|
442 }
|
|
443 }
|
|
444 }
|
|
445 if ((retval = ap->return_padata(context, pa, client, request, reply,
|
|
446 client_key, encrypting_key, send_pa)))
|
|
447 goto cleanup;
|
|
448
|
|
449 if (*send_pa)
|
|
450 send_pa++;
|
|
451 *send_pa = 0;
|
|
452 }
|
|
453
|
|
454 retval = 0;
|
|
455
|
|
456 if (send_pa_list[0]) {
|
|
457 reply->padata = send_pa_list;
|
|
458 send_pa_list = 0;
|
|
459 }
|
|
460
|
|
461 cleanup:
|
|
462 if (send_pa_list)
|
|
463 krb5_free_pa_data(context, send_pa_list);
|
|
464 return (retval);
|
|
465 }
|
|
466
|
|
467 static krb5_boolean
|
|
468 enctype_requires_etype_info_2(krb5_enctype enctype)
|
|
469 {
|
|
470 switch(enctype) {
|
|
471 case ENCTYPE_DES_CBC_CRC:
|
|
472 case ENCTYPE_DES_CBC_MD4:
|
|
473 case ENCTYPE_DES_CBC_MD5:
|
|
474 case ENCTYPE_DES3_CBC_SHA1:
|
|
475 case ENCTYPE_DES3_CBC_RAW:
|
|
476 case ENCTYPE_ARCFOUR_HMAC:
|
|
477 case ENCTYPE_ARCFOUR_HMAC_EXP :
|
|
478 return 0;
|
|
479 default:
|
|
480 if (krb5_c_valid_enctype(enctype))
|
|
481 return 1;
|
|
482 else return 0;
|
|
483 }
|
|
484 }
|
|
485
|
|
486 static krb5_boolean
|
|
487 request_contains_enctype (krb5_context context, const krb5_kdc_req *request,
|
|
488 krb5_enctype enctype)
|
|
489 {
|
|
490 int i;
|
|
491 for (i =0; i < request->nktypes; i++)
|
|
492 if (request->ktype[i] == enctype)
|
|
493 return 1;
|
|
494 return 0;
|
|
495 }
|
|
496
|
|
497
|
|
498 static krb5_error_code
|
|
499 verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
|
|
500 krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
|
|
501 krb5_pa_data *pa)
|
|
502 {
|
|
503 krb5_pa_enc_ts * pa_enc = 0;
|
|
504 krb5_error_code retval;
|
|
505 krb5_data scratch;
|
|
506 krb5_data enc_ts_data;
|
|
507 krb5_enc_data *enc_data = 0;
|
|
508 krb5_keyblock key;
|
|
509 krb5_key_data * client_key;
|
|
510 krb5_int32 start;
|
|
511 krb5_timestamp timenow;
|
|
512 krb5_error_code decrypt_err;
|
|
513
|
|
514 (void) memset(&key, 0, sizeof(krb5_keyblock));
|
|
515 scratch.data = (char *) pa->contents;
|
|
516 scratch.length = pa->length;
|
|
517
|
|
518 enc_ts_data.data = 0;
|
|
519
|
|
520 if ((retval = decode_krb5_enc_data(&scratch, &enc_data)) != 0)
|
|
521 goto cleanup;
|
|
522
|
|
523 enc_ts_data.length = enc_data->ciphertext.length;
|
|
524 if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL)
|
|
525 goto cleanup;
|
|
526
|
|
527 start = 0;
|
|
528 decrypt_err = 0;
|
|
529 while (1) {
|
|
530 if ((retval = krb5_dbe_search_enctype(context, client,
|
|
531 &start, enc_data->enctype,
|
|
532 -1, 0, &client_key)))
|
|
533 goto cleanup;
|
|
534
|
|
535 if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
|
|
536 client_key, &key, NULL)))
|
|
537 goto cleanup;
|
|
538
|
|
539 key.enctype = enc_data->enctype;
|
|
540
|
|
541 retval = krb5_c_decrypt(context, &key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
|
|
542 0, enc_data, &enc_ts_data);
|
|
543 krb5_free_keyblock_contents(context, &key);
|
|
544 if (retval == 0)
|
|
545 break;
|
|
546 else
|
|
547 decrypt_err = retval;
|
|
548 }
|
|
549
|
|
550 if ((retval = decode_krb5_pa_enc_ts(&enc_ts_data, &pa_enc)) != 0)
|
|
551 goto cleanup;
|
|
552
|
|
553 if ((retval = krb5_timeofday(context, &timenow)) != 0)
|
|
554 goto cleanup;
|
|
555
|
|
556 if (labs(timenow - pa_enc->patimestamp) > context->clockskew) {
|
|
557 retval = KRB5KRB_AP_ERR_SKEW;
|
|
558 goto cleanup;
|
|
559 }
|
|
560
|
|
561 setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
|
|
562
|
|
563 retval = 0;
|
|
564
|
|
565 cleanup:
|
|
566 if (enc_data) {
|
|
567 krb5_free_data_contents(context, &enc_data->ciphertext);
|
|
568 free(enc_data);
|
|
569 }
|
|
570 krb5_free_data_contents(context, &enc_ts_data);
|
|
571 if (pa_enc)
|
|
572 free(pa_enc);
|
|
573 /*
|
|
574 * If we get NO_MATCHING_KEY and decryption previously failed, and
|
|
575 * we failed to find any other keys of the correct enctype after
|
|
576 * that failed decryption, it probably means that the password was
|
|
577 * incorrect.
|
|
578 */
|
|
579 if (retval == KRB5_KDB_NO_MATCHING_KEY && decrypt_err != 0)
|
|
580 retval = decrypt_err;
|
|
581 return retval;
|
|
582 }
|
|
583
|
|
584 static krb5_error_code
|
|
585 _make_etype_info_entry(krb5_context context,
|
|
586 krb5_kdc_req *request, krb5_key_data *client_key,
|
|
587 krb5_enctype etype, krb5_etype_info_entry **entry,
|
|
588 int etype_info2)
|
|
589 {
|
|
590 krb5_data salt;
|
|
591 krb5_etype_info_entry * tmp_entry;
|
|
592 krb5_error_code retval;
|
|
593
|
|
594 if ((tmp_entry = malloc(sizeof(krb5_etype_info_entry))) == NULL)
|
|
595 return ENOMEM;
|
|
596
|
|
597 salt.data = 0;
|
|
598
|
|
599 tmp_entry->magic = KV5M_ETYPE_INFO_ENTRY;
|
|
600 tmp_entry->etype = etype;
|
|
601 tmp_entry->length = KRB5_ETYPE_NO_SALT;
|
|
602 tmp_entry->salt = 0;
|
|
603 tmp_entry->s2kparams.data = NULL;
|
|
604 tmp_entry->s2kparams.length = 0;
|
|
605 retval = get_salt_from_key(context, request->client,
|
|
606 client_key, &salt);
|
|
607 if (retval)
|
|
608 goto fail;
|
|
609 if (etype_info2 && client_key->key_data_ver > 1 &&
|
|
610 client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_AFS3) {
|
|
611 switch (etype) {
|
|
612 case ENCTYPE_DES_CBC_CRC:
|
|
613 case ENCTYPE_DES_CBC_MD4:
|
|
614 case ENCTYPE_DES_CBC_MD5:
|
|
615 tmp_entry->s2kparams.data = malloc(1);
|
|
616 if (tmp_entry->s2kparams.data == NULL) {
|
|
617 retval = ENOMEM;
|
|
618 goto fail;
|
|
619 }
|
|
620 tmp_entry->s2kparams.length = 1;
|
|
621 tmp_entry->s2kparams.data[0] = 1;
|
|
622 break;
|
|
623 default:
|
|
624 break;
|
|
625 }
|
|
626 }
|
|
627
|
|
628 if (salt.length >= 0) {
|
|
629 tmp_entry->length = salt.length;
|
|
630 tmp_entry->salt = (unsigned char *) salt.data;
|
|
631 salt.data = 0;
|
|
632 }
|
|
633 *entry = tmp_entry;
|
|
634 return 0;
|
|
635
|
|
636 fail:
|
|
637 if (tmp_entry) {
|
|
638 if (tmp_entry->s2kparams.data)
|
|
639 free(tmp_entry->s2kparams.data);
|
|
640 free(tmp_entry);
|
|
641 }
|
|
642 if (salt.data)
|
|
643 free(salt.data);
|
|
644 return retval;
|
|
645 }
|
|
646 /*
|
|
647 * This function returns the etype information for a particular
|
|
648 * client, to be passed back in the preauth list in the KRB_ERROR
|
|
649 * message. It supports generating both etype_info and etype_info2
|
|
650 * as most of the work is the same.
|
|
651 */
|
|
652 static krb5_error_code
|
|
653 etype_info_helper(krb5_context context, krb5_kdc_req *request,
|
|
654 krb5_db_entry *client, krb5_db_entry *server,
|
|
655 krb5_pa_data *pa_data, int etype_info2)
|
|
656 {
|
|
657 krb5_etype_info_entry ** entry = 0;
|
|
658 krb5_key_data *client_key;
|
|
659 krb5_error_code retval;
|
|
660 krb5_data * scratch;
|
|
661 krb5_enctype db_etype;
|
|
662 int i = 0;
|
|
663 int start = 0;
|
|
664 int seen_des = 0;
|
|
665
|
|
666 entry = malloc((client->n_key_data * 2 + 1) * sizeof(krb5_etype_info_entry *));
|
|
667 if (entry == NULL)
|
|
668 return ENOMEM;
|
|
669 entry[0] = NULL;
|
|
670
|
|
671 while (1) {
|
|
672 retval = krb5_dbe_search_enctype(context, client, &start, -1,
|
|
673 -1, 0, &client_key);
|
|
674 if (retval == KRB5_KDB_NO_MATCHING_KEY)
|
|
675 break;
|
|
676 if (retval)
|
|
677 goto cleanup;
|
|
678 db_etype = client_key->key_data_type[0];
|
|
679 if (db_etype == ENCTYPE_DES_CBC_MD4)
|
|
680 db_etype = ENCTYPE_DES_CBC_MD5;
|
|
681
|
|
682 if (request_contains_enctype(context, request, db_etype)) {
|
|
683 assert(etype_info2 ||
|
|
684 !enctype_requires_etype_info_2(db_etype));
|
|
685 if ((retval = _make_etype_info_entry(context, request, client_key,
|
|
686 db_etype, &entry[i], etype_info2)) != 0) {
|
|
687 goto cleanup;
|
|
688 }
|
|
689 entry[i+1] = 0;
|
|
690 i++;
|
|
691 }
|
|
692
|
|
693 /*
|
|
694 * If there is a des key in the kdb, try the "similar" enctypes,
|
|
695 * avoid duplicate entries.
|
|
696 */
|
|
697 if (!seen_des) {
|
|
698 switch (db_etype) {
|
|
699 case ENCTYPE_DES_CBC_MD5:
|
|
700 db_etype = ENCTYPE_DES_CBC_CRC;
|
|
701 break;
|
|
702 case ENCTYPE_DES_CBC_CRC:
|
|
703 db_etype = ENCTYPE_DES_CBC_MD5;
|
|
704 break;
|
|
705 default:
|
|
706 continue;
|
|
707
|
|
708 }
|
|
709 if (request_contains_enctype(context, request, db_etype)) {
|
|
710 if ((retval = _make_etype_info_entry(context, request,
|
|
711 client_key, db_etype, &entry[i], etype_info2)) != 0) {
|
|
712 goto cleanup;
|
|
713 }
|
|
714 entry[i+1] = 0;
|
|
715 i++;
|
|
716 }
|
|
717 seen_des++;
|
|
718 }
|
|
719 }
|
|
720 if (etype_info2)
|
|
721 retval = encode_krb5_etype_info2((const krb5_etype_info_entry **) entry,
|
|
722 &scratch);
|
|
723 else retval = encode_krb5_etype_info((const krb5_etype_info_entry **) entry,
|
|
724 &scratch);
|
|
725 if (retval)
|
|
726 goto cleanup;
|
|
727 pa_data->contents = (unsigned char *)scratch->data;
|
|
728 pa_data->length = scratch->length;
|
|
729 /*
|
|
730 * note, don't free scratch->data as it is in use (don't use
|
|
731 * krb5_free_data() either).
|
|
732 */
|
|
733 free(scratch);
|
|
734
|
|
735 retval = 0;
|
|
736
|
|
737 cleanup:
|
|
738 if (entry)
|
|
739 krb5_free_etype_info(context, entry);
|
|
740 return retval;
|
|
741 }
|
|
742
|
|
743 static krb5_error_code
|
|
744 get_etype_info(krb5_context context, krb5_kdc_req *request,
|
|
745 krb5_db_entry *client, krb5_db_entry *server,
|
|
746 krb5_pa_data *pa_data)
|
|
747 {
|
|
748 int i;
|
|
749 for (i=0; i < request->nktypes; i++) {
|
|
750 if (enctype_requires_etype_info_2(request->ktype[i]))
|
|
751 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP ;;;; /*Caller will
|
|
752 * skip this
|
|
753 * type*/
|
|
754 }
|
|
755 return etype_info_helper(context, request, client, server, pa_data, 0);
|
|
756 }
|
|
757
|
|
758 static krb5_error_code
|
|
759 get_etype_info2(krb5_context context, krb5_kdc_req *request,
|
|
760 krb5_db_entry *client, krb5_db_entry *server,
|
|
761 krb5_pa_data *pa_data)
|
|
762 {
|
|
763 return etype_info_helper( context, request, client, server, pa_data, 1);
|
|
764 }
|
|
765
|
|
766 static krb5_error_code
|
|
767 return_etype_info2(krb5_context context, krb5_pa_data * padata,
|
|
768 krb5_db_entry *client,
|
|
769 krb5_kdc_req *request, krb5_kdc_rep *reply,
|
|
770 krb5_key_data *client_key,
|
|
771 krb5_keyblock *encrypting_key,
|
|
772 krb5_pa_data **send_pa)
|
|
773 {
|
|
774 krb5_error_code retval;
|
|
775 krb5_pa_data *tmp_padata;
|
|
776 krb5_etype_info_entry **entry = NULL;
|
|
777 krb5_data *scratch = NULL;
|
|
778 tmp_padata = malloc( sizeof(krb5_pa_data));
|
|
779 if (tmp_padata == NULL)
|
|
780 return ENOMEM;
|
|
781 tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO2;
|
|
782 entry = malloc(2 * sizeof(krb5_etype_info_entry *));
|
|
783 if (entry == NULL) {
|
|
784 retval = ENOMEM;
|
|
785 goto cleanup;
|
|
786 }
|
|
787 entry[0] = NULL;
|
|
788 entry[1] = NULL;
|
|
789 /* using encrypting_key->enctype as this is specified in rfc4120 */
|
|
790 retval = _make_etype_info_entry(context, request, client_key, encrypting_key->enctype,
|
|
791 entry, 1);
|
|
792 if (retval)
|
|
793 goto cleanup;
|
|
794 retval = encode_krb5_etype_info2((const krb5_etype_info_entry **) entry, &scratch);
|
|
795 if (retval)
|
|
796 goto cleanup;
|
|
797 tmp_padata->contents = (uchar_t *)scratch->data;
|
|
798 tmp_padata->length = scratch->length;
|
|
799 *send_pa = tmp_padata;
|
|
800
|
|
801 /* For cleanup - we no longer own the contents of the krb5_data
|
|
802 * only to pointer to the krb5_data
|
|
803 */
|
|
804 scratch->data = 0;
|
|
805
|
|
806 cleanup:
|
|
807 if (entry)
|
|
808 krb5_free_etype_info(context, entry);
|
|
809 if (retval) {
|
|
810 if (tmp_padata)
|
|
811 free(tmp_padata);
|
|
812 }
|
|
813 if (scratch)
|
|
814 krb5_free_data(context, scratch);
|
|
815 return retval;
|
|
816 }
|
|
817
|
|
818
|
|
819 static krb5_error_code
|
|
820 return_pw_salt(krb5_context context, krb5_pa_data *in_padata,
|
|
821 krb5_db_entry *client, krb5_kdc_req *request,
|
|
822 krb5_kdc_rep *reply, krb5_key_data *client_key,
|
|
823 krb5_keyblock *encrypting_key, krb5_pa_data **send_pa)
|
|
824 {
|
|
825 krb5_error_code retval;
|
|
826 krb5_pa_data * padata;
|
|
827 krb5_data * scratch;
|
|
828 krb5_data salt_data;
|
|
829 int i;
|
|
830
|
|
831 for (i = 0; i < request->nktypes; i++) {
|
|
832 if (enctype_requires_etype_info_2(request->ktype[i]))
|
|
833 return 0;
|
|
834 }
|
|
835 if (client_key->key_data_ver == 1 ||
|
|
836 client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)
|
|
837 return 0;
|
|
838
|
|
839 if ((padata = malloc(sizeof(krb5_pa_data))) == NULL)
|
|
840 return ENOMEM;
|
|
841 padata->magic = KV5M_PA_DATA;
|
|
842 padata->pa_type = KRB5_PADATA_PW_SALT;
|
|
843
|
|
844 switch (client_key->key_data_type[1]) {
|
|
845 case KRB5_KDB_SALTTYPE_V4:
|
|
846 /* send an empty (V4) salt */
|
|
847 padata->contents = 0;
|
|
848 padata->length = 0;
|
|
849 break;
|
|
850 case KRB5_KDB_SALTTYPE_NOREALM:
|
|
851 if ((retval = krb5_principal2salt_norealm(kdc_context,
|
|
852 request->client,
|
|
853 &salt_data)))
|
|
854 goto cleanup;
|
|
855 padata->contents = (krb5_octet *)salt_data.data;
|
|
856 padata->length = salt_data.length;
|
|
857 break;
|
|
858 case KRB5_KDB_SALTTYPE_AFS3:
|
|
859 /* send an AFS style realm-based salt */
|
|
860 /* for now, just pass the realm back and let the client
|
|
861 do the work. In the future, add a kdc configuration
|
|
862 variable that specifies the old cell name. */
|
|
863 padata->pa_type = KRB5_PADATA_AFS3_SALT;
|
|
864 /* it would be just like ONLYREALM, but we need to pass the 0 */
|
|
865 scratch = krb5_princ_realm(kdc_context, request->client);
|
|
866 if ((padata->contents = malloc(scratch->length+1)) == NULL) {
|
|
867 retval = ENOMEM;
|
|
868 goto cleanup;
|
|
869 }
|
|
870 memcpy(padata->contents, scratch->data, scratch->length);
|
|
871 padata->length = scratch->length+1;
|
|
872 padata->contents[scratch->length] = 0;
|
|
873 break;
|
|
874 case KRB5_KDB_SALTTYPE_ONLYREALM:
|
|
875 scratch = krb5_princ_realm(kdc_context, request->client);
|
|
876 if ((padata->contents = malloc(scratch->length)) == NULL) {
|
|
877 retval = ENOMEM;
|
|
878 goto cleanup;
|
|
879 }
|
|
880 memcpy(padata->contents, scratch->data, scratch->length);
|
|
881 padata->length = scratch->length;
|
|
882 break;
|
|
883 case KRB5_KDB_SALTTYPE_SPECIAL:
|
|
884 if ((padata->contents = malloc(client_key->key_data_length[1]))
|
|
885 == NULL) {
|
|
886 retval = ENOMEM;
|
|
887 goto cleanup;
|
|
888 }
|
|
889 memcpy(padata->contents, client_key->key_data_contents[1],
|
|
890 client_key->key_data_length[1]);
|
|
891 padata->length = client_key->key_data_length[1];
|
|
892 break;
|
|
893 default:
|
|
894 free(padata);
|
|
895 return 0;
|
|
896 }
|
|
897
|
|
898 *send_pa = padata;
|
|
899 return 0;
|
|
900
|
|
901 cleanup:
|
|
902 free(padata);
|
|
903 return retval;
|
|
904 }
|
|
905
|
|
906 static krb5_error_code
|
|
907 return_sam_data(krb5_context context, krb5_pa_data *in_padata,
|
|
908 krb5_db_entry *client, krb5_kdc_req *request,
|
|
909 krb5_kdc_rep *reply, krb5_key_data *client_key,
|
|
910 krb5_keyblock *encrypting_key, krb5_pa_data **send_pa)
|
|
911 {
|
|
912 krb5_error_code retval;
|
|
913 krb5_data scratch;
|
|
914 int i;
|
|
915
|
|
916 krb5_sam_response *sr = 0;
|
|
917 krb5_predicted_sam_response *psr = 0;
|
|
918
|
|
919 if (in_padata == 0)
|
|
920 return 0;
|
|
921
|
|
922 /*
|
|
923 * We start by doing the same thing verify_sam_response() does:
|
|
924 * extract the psr from the padata (which is an sr). Nothing
|
|
925 * here should generate errors! We've already successfully done
|
|
926 * all this once.
|
|
927 */
|
|
928
|
|
929 scratch.data = (char *) in_padata->contents; /* SUNWresync121 XXX */
|
|
930 scratch.length = in_padata->length;
|
|
931
|
|
932 if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
|
|
933 com_err("krb5kdc", retval,
|
|
934 gettext("return_sam_data(): decode_krb5_sam_response failed"));
|
|
935 goto cleanup;
|
|
936 }
|
|
937
|
|
938 {
|
|
939 krb5_enc_data tmpdata;
|
|
940
|
|
941 tmpdata.enctype = ENCTYPE_UNKNOWN;
|
|
942 tmpdata.ciphertext = sr->sam_track_id;
|
|
943
|
|
944 scratch.length = tmpdata.ciphertext.length;
|
|
945 if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
|
|
946 retval = ENOMEM;
|
|
947 goto cleanup;
|
|
948 }
|
|
949
|
|
950 if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
|
|
951 &tmpdata, &scratch))) {
|
|
952 com_err("krb5kdc", retval,
|
|
953 gettext("return_sam_data(): decrypt track_id failed"));
|
|
954 free(scratch.data);
|
|
955 goto cleanup;
|
|
956 }
|
|
957 }
|
|
958
|
|
959 if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
|
|
960 com_err("krb5kdc", retval,
|
|
961 gettext(
|
|
962 "return_sam_data(): decode_krb5_predicted_sam_response failed"));
|
|
963 free(scratch.data);
|
|
964 goto cleanup;
|
|
965 }
|
|
966
|
|
967 /* We could use sr->sam_flags, but it may be absent or altered. */
|
|
968 if (psr->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
|
|
969 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
|
|
970 gettext("Unsupported SAM flag must-pk-encrypt-sad"));
|
|
971 goto cleanup;
|
|
972 }
|
|
973 if (psr->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
|
|
974 /* No key munging */
|
|
975 goto cleanup;
|
|
976 }
|
|
977 if (psr->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
|
|
978 /* Use sam_key instead of client key */
|
|
979 krb5_free_keyblock_contents(context, encrypting_key);
|
|
980 krb5_copy_keyblock_contents(context, &psr->sam_key, encrypting_key);
|
|
981 /* XXX Attach a useful pa_data */
|
|
982 goto cleanup;
|
|
983 }
|
|
984
|
|
985 /* Otherwise (no flags set), we XOR the keys */
|
|
986 /* XXX The passwords-04 draft is underspecified here wrt different
|
|
987 key types. We will do what I hope to get into the -05 draft. */
|
|
988 {
|
|
989 krb5_octet *p = encrypting_key->contents;
|
|
990 krb5_octet *q = psr->sam_key.contents;
|
|
991 int length = ((encrypting_key->length < psr->sam_key.length)
|
|
992 ? encrypting_key->length
|
|
993 : psr->sam_key.length);
|
|
994
|
|
995 for (i = 0; i < length; i++)
|
|
996 p[i] ^= q[i];
|
|
997 }
|
|
998
|
|
999 /* Post-mixing key correction */
|
|
1000 switch (encrypting_key->enctype) {
|
|
1001 case ENCTYPE_DES_CBC_CRC:
|
|
1002 case ENCTYPE_DES_CBC_MD4:
|
|
1003 case ENCTYPE_DES_CBC_MD5:
|
|
1004 case ENCTYPE_DES_CBC_RAW:
|
|
1005 mit_des_fixup_key_parity(encrypting_key->contents);
|
|
1006 if (mit_des_is_weak_key(encrypting_key->contents))
|
|
1007 ((krb5_octet *) encrypting_key->contents)[7] ^= 0xf0;
|
|
1008 break;
|
|
1009
|
|
1010 /* XXX case ENCTYPE_DES3_CBC_MD5: listed in 1510bis-04 draft */
|
|
1011 case ENCTYPE_DES3_CBC_SHA: /* XXX deprecated? */
|
|
1012 case ENCTYPE_DES3_CBC_RAW:
|
|
1013 case ENCTYPE_DES3_CBC_SHA1:
|
|
1014 for (i = 0; i < 3; i++) {
|
|
1015 mit_des_fixup_key_parity(encrypting_key->contents + i * 8);
|
|
1016 if (mit_des_is_weak_key(encrypting_key->contents + i * 8))
|
|
1017 ((krb5_octet *) encrypting_key->contents)[7 + i * 8] ^= 0xf0;
|
|
1018 }
|
|
1019 break;
|
|
1020
|
|
1021 default:
|
|
1022 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
|
|
1023 gettext("Unimplemented keytype for SAM key mixing"));
|
|
1024 goto cleanup;
|
|
1025 }
|
|
1026
|
|
1027 /* XXX Attach a useful pa_data */
|
|
1028 cleanup:
|
|
1029 if (sr)
|
|
1030 krb5_free_sam_response(context, sr);
|
|
1031 if (psr)
|
|
1032 krb5_free_predicted_sam_response(context, psr);
|
|
1033
|
|
1034 return retval;
|
|
1035 }
|
|
1036
|
|
1037 static struct {
|
|
1038 char* name;
|
|
1039 int sam_type;
|
|
1040 } *sam_ptr, sam_inst_map[] = {
|
|
1041 #if 0 /* SUNWresync121 - unsupported hardware and kdc.log annoyance */
|
|
1042 { "SNK4", PA_SAM_TYPE_DIGI_PATH, },
|
|
1043 { "SECURID", PA_SAM_TYPE_SECURID, },
|
|
1044 { "GRAIL", PA_SAM_TYPE_GRAIL, },
|
|
1045 #endif
|
|
1046 { 0, 0 },
|
|
1047 };
|
|
1048
|
|
1049 static krb5_error_code
|
|
1050 get_sam_edata(krb5_context context, krb5_kdc_req *request,
|
|
1051 krb5_db_entry *client, krb5_db_entry *server,
|
|
1052 krb5_pa_data *pa_data)
|
|
1053 {
|
|
1054 krb5_error_code retval;
|
|
1055 krb5_sam_challenge sc;
|
|
1056 krb5_predicted_sam_response psr;
|
|
1057 krb5_data * scratch;
|
|
1058 krb5_keyblock encrypting_key;
|
|
1059 char response[9];
|
|
1060 char inputblock[8];
|
|
1061 krb5_data predict_response;
|
|
1062
|
|
1063 (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock));
|
|
1064 (void) memset(&sc, 0, sizeof(sc));
|
|
1065 (void) memset(&psr, 0, sizeof(psr));
|
|
1066
|
|
1067 /* Given the client name we can figure out what type of preauth
|
|
1068 they need. The spec is currently for querying the database for
|
|
1069 names that match the types of preauth used. Later we should
|
|
1070 make this mapping show up in kdc.conf. In the meantime, we
|
|
1071 hardcode the following:
|
|
1072 /SNK4 -- Digital Pathways SNK/4 preauth.
|
|
1073 /GRAIL -- experimental preauth
|
|
1074 The first one found is used. See sam_inst_map above.
|
|
1075
|
|
1076 For SNK4 in particular, the key in the database is the key for
|
|
1077 the device; kadmin needs a special interface for it.
|
|
1078 */
|
|
1079
|
|
1080 {
|
|
1081 int npr = 1;
|
|
1082 krb5_boolean more;
|
|
1083 krb5_db_entry assoc;
|
|
1084 krb5_key_data *assoc_key;
|
|
1085 krb5_principal newp;
|
|
1086 int probeslot;
|
|
1087
|
|
1088 sc.sam_type = 0;
|
|
1089
|
|
1090 retval = krb5_copy_principal(kdc_context, request->client, &newp);
|
|
1091 if (retval) {
|
|
1092 com_err(gettext("krb5kdc"),
|
|
1093 retval,
|
|
1094 gettext("copying client name for preauth probe"));
|
|
1095 return retval;
|
|
1096 }
|
|
1097
|
|
1098 probeslot = krb5_princ_size(context, newp)++;
|
|
1099 krb5_princ_name(kdc_context, newp) =
|
|
1100 realloc(krb5_princ_name(kdc_context, newp),
|
|
1101 krb5_princ_size(context, newp) * sizeof(krb5_data));
|
|
1102
|
|
1103 for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) {
|
|
1104 krb5_princ_component(kdc_context,newp,probeslot)->data = sam_ptr->name;
|
|
1105 krb5_princ_component(kdc_context,newp,probeslot)->length =
|
|
1106 strlen(sam_ptr->name);
|
|
1107 npr = 1;
|
|
1108 retval = krb5_db_get_principal(kdc_context, newp, &assoc, &npr, (uint *)&more);
|
|
1109 if(!retval && npr) {
|
|
1110 sc.sam_type = sam_ptr->sam_type;
|
|
1111 break;
|
|
1112 }
|
|
1113 }
|
|
1114
|
|
1115 krb5_princ_component(kdc_context,newp,probeslot)->data = 0;
|
|
1116 krb5_princ_component(kdc_context,newp,probeslot)->length = 0;
|
|
1117 krb5_princ_size(context, newp)--;
|
|
1118
|
|
1119 krb5_free_principal(kdc_context, newp);
|
|
1120
|
|
1121 /* if sc.sam_type is set, it worked */
|
|
1122 if (sc.sam_type) {
|
|
1123 /* so use assoc to get the key out! */
|
|
1124 {
|
|
1125 /* here's what do_tgs_req does */
|
|
1126 retval = krb5_dbe_find_enctype(kdc_context, &assoc,
|
|
1127 ENCTYPE_DES_CBC_RAW,
|
|
1128 KRB5_KDB_SALTTYPE_NORMAL,
|
|
1129 0, /* Get highest kvno */
|
|
1130 &assoc_key);
|
|
1131 if (retval) {
|
|
1132 char *sname;
|
|
1133 krb5_unparse_name(kdc_context, request->client, &sname);
|
|
1134 com_err(gettext("krb5kdc"),
|
|
1135 retval,
|
|
1136 gettext("snk4 finding the enctype and key <%s>"),
|
|
1137 sname);
|
|
1138 free(sname);
|
|
1139 return retval;
|
|
1140 }
|
|
1141 /* convert server.key into a real key */
|
|
1142 retval = krb5_dbekd_decrypt_key_data(kdc_context,
|
|
1143 &master_keyblock,
|
|
1144 assoc_key, &encrypting_key,
|
|
1145 NULL);
|
|
1146 if (retval) {
|
|
1147 com_err(gettext("krb5kdc"),
|
|
1148 retval,
|
|
1149 gettext("snk4 pulling out key entry"));
|
|
1150 return retval;
|
|
1151 }
|
|
1152 /* now we can use encrypting_key... */
|
|
1153 }
|
|
1154 } else {
|
|
1155 /* SAM is not an option - so don't return as hint */
|
|
1156 return KRB5_PREAUTH_BAD_TYPE;
|
|
1157 }
|
|
1158 }
|
|
1159 sc.magic = KV5M_SAM_CHALLENGE;
|
|
1160 psr.sam_flags = sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
|
|
1161
|
|
1162 /* Replay prevention */
|
|
1163 if ((retval = krb5_copy_principal(context, request->client, &psr.client)))
|
|
1164 return retval;
|
|
1165 #ifdef USE_RCACHE
|
|
1166 if ((retval = krb5_us_timeofday(context, &psr.stime, &psr.susec)))
|
|
1167 return retval;
|
|
1168 #endif /* USE_RCACHE */
|
|
1169
|
|
1170 switch (sc.sam_type) {
|
|
1171 case PA_SAM_TYPE_GRAIL:
|
|
1172 sc.sam_type_name.data = "Experimental System";
|
|
1173 sc.sam_type_name.length = strlen(sc.sam_type_name.data);
|
|
1174 sc.sam_challenge_label.data = "experimental challenge label";
|
|
1175 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
|
|
1176 sc.sam_challenge.data = "12345";
|
|
1177 sc.sam_challenge.length = strlen(sc.sam_challenge.data);
|
|
1178
|
|
1179 #if 0 /* Enable this to test "normal" (no flags set) mode. */
|
|
1180 psr.sam_flags = sc.sam_flags = 0;
|
|
1181 #endif
|
|
1182
|
|
1183 psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
|
|
1184 /* string2key on sc.sam_challenge goes in here */
|
|
1185 /* eblock is just to set the enctype */
|
|
1186 {
|
|
1187 const krb5_enctype type = ENCTYPE_DES_CBC_MD5;
|
|
1188
|
|
1189 if ((retval = krb5_c_string_to_key(context, type, &sc.sam_challenge,
|
|
1190 0 /* salt */, &psr.sam_key)))
|
|
1191 goto cleanup;
|
|
1192
|
|
1193 if ((retval = encode_krb5_predicted_sam_response(&psr, &scratch)))
|
|
1194 goto cleanup;
|
|
1195
|
|
1196 {
|
|
1197 size_t enclen;
|
|
1198 krb5_enc_data tmpdata;
|
|
1199
|
|
1200 if ((retval = krb5_c_encrypt_length(context,
|
|
1201 psr_key.enctype,
|
|
1202 scratch->length, &enclen)))
|
|
1203 goto cleanup;
|
|
1204
|
|
1205 if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
|
|
1206 retval = ENOMEM;
|
|
1207 goto cleanup;
|
|
1208 }
|
|
1209 tmpdata.ciphertext.length = enclen;
|
|
1210
|
|
1211 if ((retval = krb5_c_encrypt(context, &psr_key,
|
|
1212 /* XXX */ 0, 0, scratch, &tmpdata)))
|
|
1213 goto cleanup;
|
|
1214
|
|
1215 sc.sam_track_id = tmpdata.ciphertext;
|
|
1216 }
|
|
1217 }
|
|
1218
|
|
1219 sc.sam_response_prompt.data = "response prompt";
|
|
1220 sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
|
|
1221 sc.sam_pk_for_sad.length = 0;
|
|
1222 sc.sam_nonce = 0;
|
|
1223 /* Generate checksum */
|
|
1224 /*krb5_checksum_size(context, ctype)*/
|
|
1225 /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
|
|
1226 seed_length,outcksum) */
|
|
1227 /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
|
|
1228 seed_length) */
|
|
1229 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
|
|
1230 sc.sam_cksum.contents = (krb5_octet *)
|
|
1231 malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
|
|
1232 if (sc.sam_cksum.contents == NULL) return(ENOMEM);
|
|
1233
|
|
1234 retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
|
|
1235 sc.sam_challenge.data,
|
|
1236 sc.sam_challenge.length,
|
|
1237 psr.sam_key.contents, /* key */
|
|
1238 psr.sam_key.length, /* key length */
|
|
1239 &sc.sam_cksum);
|
|
1240 if (retval) { free(sc.sam_cksum.contents); return(retval); }
|
|
1241 #endif /* 0 */
|
|
1242
|
|
1243 retval = encode_krb5_sam_challenge(&sc, &scratch);
|
|
1244 if (retval) goto cleanup;
|
|
1245 pa_data->magic = KV5M_PA_DATA;
|
|
1246 pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
|
|
1247 pa_data->contents = (unsigned char *) scratch->data;
|
|
1248 pa_data->length = scratch->length;
|
|
1249
|
|
1250 retval = 0;
|
|
1251 break;
|
|
1252 case PA_SAM_TYPE_DIGI_PATH:
|
|
1253 sc.sam_type_name.data = "Digital Pathways";
|
|
1254 sc.sam_type_name.length = strlen(sc.sam_type_name.data);
|
|
1255 #if 1
|
|
1256 sc.sam_challenge_label.data = "Enter the following on your keypad";
|
|
1257 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
|
|
1258 #endif
|
|
1259 /* generate digit string, take it mod 1000000 (six digits.) */
|
|
1260 {
|
|
1261 int j;
|
|
1262 krb5_keyblock session_key;
|
|
1263 char outputblock[8];
|
|
1264 int i;
|
|
1265
|
|
1266 (void) memset(&session_key, 0, sizeof(krb5_keyblock));
|
|
1267
|
|
1268 (void) memset(inputblock, 0, 8);
|
|
1269
|
|
1270 retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC,
|
|
1271 &session_key);
|
|
1272
|
|
1273 if (retval) {
|
|
1274 /* random key failed */
|
|
1275 com_err(gettext("krb5kdc"),
|
|
1276 retval,
|
|
1277 gettext("generating random challenge for preauth"));
|
|
1278 return retval;
|
|
1279 }
|
|
1280 /* now session_key has a key which we can pick bits out of */
|
|
1281 /* we need six decimal digits. Grab 6 bytes, div 2, mod 10 each. */
|
|
1282 if (session_key.length != 8) {
|
|
1283 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
|
|
1284 com_err(gettext("krb5kdc"),
|
|
1285 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
|
|
1286 gettext("keytype didn't match code expectations"));
|
|
1287 return retval;
|
|
1288 }
|
|
1289 for(i = 0; i<6; i++) {
|
|
1290 inputblock[i] = '0' + ((session_key.contents[i]/2) % 10);
|
|
1291 }
|
|
1292 if (session_key.contents)
|
|
1293 krb5_free_keyblock_contents(kdc_context, &session_key);
|
|
1294
|
|
1295 /* retval = krb5_finish_key(kdc_context, &eblock); */
|
|
1296 /* now we have inputblock containing the 8 byte input to DES... */
|
|
1297 sc.sam_challenge.data = inputblock;
|
|
1298 sc.sam_challenge.length = 6;
|
|
1299
|
|
1300 encrypting_key.enctype = ENCTYPE_DES_CBC_RAW;
|
|
1301
|
|
1302 if (retval) {
|
|
1303 com_err(gettext("krb5kdc"),
|
|
1304 retval,
|
|
1305 gettext("snk4 processing key"));
|
|
1306 }
|
|
1307
|
|
1308 {
|
|
1309 krb5_data plain;
|
|
1310 krb5_enc_data cipher;
|
|
1311
|
|
1312 plain.length = 8;
|
|
1313 plain.data = inputblock;
|
|
1314
|
|
1315 /* XXX I know this is enough because of the fixed raw enctype.
|
|
1316 if it's not, the underlying code will return a reasonable
|
|
1317 error, which should never happen */
|
|
1318 cipher.ciphertext.length = 8;
|
|
1319 cipher.ciphertext.data = outputblock;
|
|
1320
|
|
1321 if ((retval = krb5_c_encrypt(kdc_context, &encrypting_key,
|
|
1322 /* XXX */ 0, 0, &plain, &cipher))) {
|
|
1323 com_err(gettext("krb5kdc"),
|
|
1324 retval,
|
|
1325 gettext("snk4 response generation failed"));
|
|
1326 return retval;
|
|
1327 }
|
|
1328 }
|
|
1329
|
|
1330 /* now output block is the raw bits of the response; convert it
|
|
1331 to display form */
|
|
1332 for (j=0; j<4; j++) {
|
|
1333 char n[2];
|
|
1334 int k;
|
|
1335 n[0] = outputblock[j] & 0xf;
|
|
1336 n[1] = (outputblock[j]>>4) & 0xf;
|
|
1337 for (k=0; k<2; k++) {
|
|
1338 if(n[k] > 9) n[k] = ((n[k]-1)>>2);
|
|
1339 /* This is equivalent to:
|
|
1340 if(n[k]>=0xa && n[k]<=0xc) n[k] = 2;
|
|
1341 if(n[k]>=0xd && n[k]<=0xf) n[k] = 3;
|
|
1342 */
|
|
1343 }
|
|
1344 /* for v4, we keygen: *(j+(char*)&key1) = (n[1]<<4) | n[0]; */
|
|
1345 /* for v5, we just generate a string */
|
|
1346 response[2*j+0] = '0' + n[1];
|
|
1347 response[2*j+1] = '0' + n[0];
|
|
1348 /* and now, response has what we work with. */
|
|
1349 }
|
|
1350 response[8] = 0;
|
|
1351 predict_response.data = response;
|
|
1352 predict_response.length = 8;
|
|
1353 #if 0 /* for debugging, hack the output too! */
|
|
1354 sc.sam_challenge_label.data = response;
|
|
1355 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
|
|
1356 #endif
|
|
1357 }
|
|
1358
|
|
1359 psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
|
|
1360 /* string2key on sc.sam_challenge goes in here */
|
|
1361 /* eblock is just to set the enctype */
|
|
1362 {
|
|
1363 retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
|
|
1364 &predict_response, 0 /* salt */,
|
|
1365 &psr.sam_key);
|
|
1366 if (retval) goto cleanup;
|
|
1367
|
|
1368 retval = encode_krb5_predicted_sam_response(&psr, &scratch);
|
|
1369 if (retval) goto cleanup;
|
|
1370
|
|
1371 {
|
|
1372 size_t enclen;
|
|
1373 krb5_enc_data tmpdata;
|
|
1374
|
|
1375 if ((retval = krb5_c_encrypt_length(context,
|
|
1376 psr_key.enctype,
|
|
1377 scratch->length, &enclen)))
|
|
1378 goto cleanup;
|
|
1379
|
|
1380 if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
|
|
1381 retval = ENOMEM;
|
|
1382 goto cleanup;
|
|
1383 }
|
|
1384 tmpdata.ciphertext.length = enclen;
|
|
1385
|
|
1386 if ((retval = krb5_c_encrypt(context, &psr_key,
|
|
1387 /* XXX */ 0, 0, scratch, &tmpdata)))
|
|
1388 goto cleanup;
|
|
1389
|
|
1390 sc.sam_track_id = tmpdata.ciphertext;
|
|
1391 }
|
|
1392 if (retval) goto cleanup;
|
|
1393 }
|
|
1394
|
|
1395 sc.sam_response_prompt.data = "Enter the displayed response";
|
|
1396 sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
|
|
1397 sc.sam_pk_for_sad.length = 0;
|
|
1398 sc.sam_nonce = 0;
|
|
1399 /* Generate checksum */
|
|
1400 /*krb5_checksum_size(context, ctype)*/
|
|
1401 /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
|
|
1402 seed_length,outcksum) */
|
|
1403 /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
|
|
1404 seed_length) */
|
|
1405 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
|
|
1406 sc.sam_cksum.contents = (krb5_octet *)
|
|
1407 malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
|
|
1408 if (sc.sam_cksum.contents == NULL) return(ENOMEM);
|
|
1409
|
|
1410 retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
|
|
1411 sc.sam_challenge.data,
|
|
1412 sc.sam_challenge.length,
|
|
1413 psr.sam_key.contents, /* key */
|
|
1414 psr.sam_key.length, /* key length */
|
|
1415 &sc.sam_cksum);
|
|
1416 if (retval) { free(sc.sam_cksum.contents); return(retval); }
|
|
1417 #endif /* 0 */
|
|
1418
|
|
1419 retval = encode_krb5_sam_challenge(&sc, &scratch);
|
|
1420 if (retval) goto cleanup;
|
|
1421 pa_data->magic = KV5M_PA_DATA;
|
|
1422 pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
|
|
1423 pa_data->contents = (unsigned char *) scratch->data;
|
|
1424 pa_data->length = scratch->length;
|
|
1425
|
|
1426 retval = 0;
|
|
1427 break;
|
|
1428 }
|
|
1429
|
|
1430 cleanup:
|
|
1431 krb5_free_keyblock_contents(context, &encrypting_key);
|
|
1432 return retval;
|
|
1433 }
|
|
1434
|
|
1435 static krb5_error_code
|
|
1436 verify_sam_response(krb5_context context, krb5_db_entry *client,
|
|
1437 krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
|
|
1438 krb5_pa_data *pa)
|
|
1439 {
|
|
1440 krb5_error_code retval;
|
|
1441 krb5_data scratch;
|
|
1442 krb5_sam_response *sr = 0;
|
|
1443 krb5_predicted_sam_response *psr = 0;
|
|
1444 krb5_enc_sam_response_enc *esre = 0;
|
|
1445 krb5_timestamp timenow;
|
|
1446 char *princ_req = 0, *princ_psr = 0;
|
|
1447
|
|
1448 scratch.data = (char *) pa->contents;
|
|
1449 scratch.length = pa->length;
|
|
1450
|
|
1451 if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
|
|
1452 scratch.data = 0;
|
|
1453 com_err("krb5kdc", retval, gettext("decode_krb5_sam_response failed"));
|
|
1454 goto cleanup;
|
|
1455 }
|
|
1456
|
|
1457 /* XXX We can only handle the challenge/response model of SAM.
|
|
1458 See passwords-04, par 4.1, 4.2 */
|
|
1459 {
|
|
1460 krb5_enc_data tmpdata;
|
|
1461
|
|
1462 tmpdata.enctype = ENCTYPE_UNKNOWN;
|
|
1463 tmpdata.ciphertext = sr->sam_track_id;
|
|
1464
|
|
1465 scratch.length = tmpdata.ciphertext.length;
|
|
1466 if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
|
|
1467 retval = ENOMEM;
|
|
1468 goto cleanup;
|
|
1469 }
|
|
1470
|
|
1471 if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
|
|
1472 &tmpdata, &scratch))) {
|
|
1473 com_err(gettext("krb5kdc"),
|
|
1474 retval,
|
|
1475 gettext("decrypt track_id failed"));
|
|
1476 goto cleanup;
|
|
1477 }
|
|
1478 }
|
|
1479
|
|
1480 if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
|
|
1481 com_err(gettext("krb5kdc"), retval,
|
|
1482 gettext("decode_krb5_predicted_sam_response failed -- replay attack?"));
|
|
1483 goto cleanup;
|
|
1484 }
|
|
1485
|
|
1486 /* Replay detection */
|
|
1487 if ((retval = krb5_unparse_name(context, request->client, &princ_req)))
|
|
1488 goto cleanup;
|
|
1489 if ((retval = krb5_unparse_name(context, psr->client, &princ_psr)))
|
|
1490 goto cleanup;
|
|
1491 if (strcmp(princ_req, princ_psr) != 0) {
|
|
1492 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
|
|
1493 gettext("Principal mismatch in SAM psr! -- replay attack?"));
|
|
1494 goto cleanup;
|
|
1495 }
|
|
1496
|
|
1497 if ((retval = krb5_timeofday(context, &timenow)))
|
|
1498 goto cleanup;
|
|
1499
|
|
1500 #ifdef USE_RCACHE
|
|
1501 {
|
|
1502 krb5_donot_replay rep;
|
|
1503 extern krb5_deltat rc_lifetime;
|
|
1504 /*
|
|
1505 * Verify this response came back in a timely manner.
|
|
1506 * We do this b/c otherwise very old (expunged from the rcache)
|
|
1507 * psr's would be able to be replayed.
|
|
1508 */
|
|
1509 if (timenow - psr->stime > rc_lifetime) {
|
|
1510 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
|
|
1511 gettext("SAM psr came back too late! -- replay attack?"));
|
|
1512 goto cleanup;
|
|
1513 }
|
|
1514
|
|
1515 /* Now check the replay cache. */
|
|
1516 rep.client = princ_psr;
|
|
1517 rep.server = "SAM/rc"; /* Should not match any principal name. */
|
|
1518 rep.ctime = psr->stime;
|
|
1519 rep.cusec = psr->susec;
|
|
1520 retval = krb5_rc_store(kdc_context, kdc_rcache, &rep);
|
|
1521 if (retval) {
|
|
1522 com_err("krb5kdc", retval, gettext("SAM psr replay attack!"));
|
|
1523 goto cleanup;
|
|
1524 }
|
|
1525 }
|
|
1526 #endif /* USE_RCACHE */
|
|
1527
|
|
1528
|
|
1529 {
|
|
1530 free(scratch.data);
|
|
1531 scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
|
|
1532 if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
|
|
1533 retval = ENOMEM;
|
|
1534 goto cleanup;
|
|
1535 }
|
|
1536
|
|
1537 if ((retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0,
|
|
1538 0, &sr->sam_enc_nonce_or_ts, &scratch))) {
|
|
1539 com_err("krb5kdc", retval, gettext("decrypt nonce_or_ts failed"));
|
|
1540 goto cleanup;
|
|
1541 }
|
|
1542 }
|
|
1543
|
|
1544 if ((retval = decode_krb5_enc_sam_response_enc(&scratch, &esre))) {
|
|
1545 com_err("krb5kdc", retval, gettext("decode_krb5_enc_sam_response_enc failed"));
|
|
1546 goto cleanup;
|
|
1547 }
|
|
1548
|
|
1549 if (esre->sam_timestamp != sr->sam_patimestamp) {
|
|
1550 retval = KRB5KDC_ERR_PREAUTH_FAILED;
|
|
1551 goto cleanup;
|
|
1552 }
|
|
1553
|
|
1554 if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
|
|
1555 retval = KRB5KRB_AP_ERR_SKEW;
|
|
1556 goto cleanup;
|
|
1557 }
|
|
1558
|
|
1559 setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
|
|
1560
|
|
1561 cleanup:
|
|
1562 if (retval) com_err(gettext("krb5kdc"),
|
|
1563 retval,
|
|
1564 gettext("sam verify failure"));
|
|
1565 if (scratch.data) free(scratch.data);
|
|
1566 if (sr) free(sr);
|
|
1567 if (psr) free(psr);
|
|
1568 if (esre) free(esre);
|
|
1569 if (princ_psr) free(princ_psr);
|
|
1570 if (princ_req) free(princ_req);
|
|
1571
|
|
1572 return retval;
|
|
1573 }
|