xref: /freebsd/crypto/krb5/src/plugins/preauth/securid_sam2/securid2.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/preauth/securid_sam2/securid2.c */
3 /*
4  * Copyright (C) 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 #ifdef ARL_SECURID_PREAUTH
40 
41 #include "k5-int.h"
42 #include <kdb.h>
43 #include <stdio.h>
44 #include <adm_proto.h>
45 #include <syslog.h>
46 #include <acexport.h>
47 #include <sdi_defs.h>
48 #include "extern.h"
49 
50 #define KRB5_SAM_SECURID_NEXT_CHALLENGE_MAGIC 0x5ec1d000
51 struct securid_track_data {
52     SDI_HANDLE handle;
53     char state;
54     char passcode[LENPRNST+1];
55     long hostid;
56 };
57 
58 #define SECURID_STATE_NEW_PIN           1       /* Ask for a new pin */
59 #define SECURID_STATE_NEW_PIN_AGAIN     2       /* Ask for new pin again */
60 #define SECURID_STATE_NEXT_CODE         3       /* Ask for the next pin code */
61 #define SECURID_STATE_INITIAL           4
62 
63 static char *PASSCODE_message =         "SecurID Passcode";
64 static char *NEXT_PASSCODE_message =    "Next Passcode";
65 static char *NEW_PIN_AGAIN_message =    "New PIN Again";
66 static char PIN_message[64];            /* Max length should be 50 chars */
67 
68 /*
69  * krb5_error_code get_securid_key():
70  *   inputs:  context:  from KDC process
71  *            client:   database entry of client executing
72  *                      SecurID SAM preauthentication
73  *   outputs: client_securid_key: pointer to krb5_keyblock
74  *                      which is key for the client's SecurID
75  *                      database entry.
76  *   returns: 0 on success
77  *            KRB5 error codes otherwise
78  *
79  *   builds principal name with final instance of "SECURID" and
80  *   finds the database entry, decrypts the key out of the database
81  *   and passes the key back to the calling process
82  */
83 
84 static krb5_error_code
get_securid_key(krb5_context context,krb5_db_entry * client,krb5_keyblock * client_securid_key)85 get_securid_key(krb5_context context, krb5_db_entry *client,
86                 krb5_keyblock *client_securid_key)
87 {
88     krb5_db_entry *sam_securid_entry = NULL;
89     krb5_key_data *client_securid_key_data = NULL;
90     int sam_type = PA_SAM_TYPE_SECURID;
91     krb5_error_code retval = 0;
92 
93     if (!client_securid_key)
94         return KRB5_PREAUTH_NO_KEY;
95 
96     retval = sam_get_db_entry(context, client->princ,
97                               &sam_type, &sam_securid_entry);
98     if (retval)
99         return KRB5_PREAUTH_NO_KEY;
100 
101     /* Find key with key_type = salt_type = kvno = -1.  This finds the  */
102     /* latest kvno in the list.                                         */
103 
104     retval = krb5_dbe_find_enctype(context, sam_securid_entry,
105                                    -1, -1, -1, &client_securid_key_data);
106     if (retval) {
107         com_err("krb5kdc", retval,
108                 "while getting key from client's SAM SecurID entry");
109         goto cleanup;
110     }
111     retval = krb5_dbe_decrypt_key_data(context, NULL, client_securid_key_data,
112                                        client_securid_key, NULL);
113     if (retval) {
114         com_err("krb5kdc", retval,
115                 "while decrypting key from client's SAM SecurID entry");
116         goto cleanup;
117     }
118 cleanup:
119     if (sam_securid_entry)
120         krb5_db_free_principal(context, sam_securid_entry);
121     return retval;
122 }
123 
124 static krb5_error_code
securid_decrypt_track_data_2(krb5_context context,krb5_db_entry * client,krb5_data * enc_track_data,krb5_data * output)125 securid_decrypt_track_data_2(krb5_context context, krb5_db_entry *client,
126                              krb5_data *enc_track_data, krb5_data *output)
127 {
128     krb5_error_code retval;
129     krb5_keyblock sam_key;
130     krb5_enc_data tmp_enc_data;
131     sam_key.contents = NULL;
132 
133     retval = get_securid_key(context, client, &sam_key);
134     if (retval != 0)
135         return retval;
136 
137     tmp_enc_data.ciphertext = *enc_track_data;
138     tmp_enc_data.enctype = ENCTYPE_UNKNOWN;
139     tmp_enc_data.kvno = 0;
140 
141     output->length = tmp_enc_data.ciphertext.length;
142     free(output->data);
143     output->data = k5alloc(output->length, &retval);
144     if (output->data == NULL)
145         goto cleanup;
146     retval = krb5_c_decrypt(context, &sam_key,
147                             KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0,
148                             &tmp_enc_data, output);
149 cleanup:
150     krb5_free_keyblock_contents(context, &sam_key);
151 
152     if (retval) {
153         output->length = 0;
154         free(output->data);
155         output->data = NULL;
156         return retval;
157     }
158 
159     return 0;
160 }
161 
162 static krb5_error_code
securid_encrypt_track_data_2(krb5_context context,krb5_db_entry * client,krb5_data * track_data,krb5_data * output)163 securid_encrypt_track_data_2(krb5_context context, krb5_db_entry *client,
164                              krb5_data *track_data, krb5_data *output)
165 {
166     krb5_error_code retval;
167     size_t olen;
168     krb5_keyblock sam_key;
169     krb5_enc_data tmp_enc_data;
170 
171     output->data = NULL;
172 
173     retval = get_securid_key(context,client, &sam_key);
174     if (retval != 0)
175         return retval;
176 
177     retval = krb5_c_encrypt_length(context, sam_key.enctype,
178                                    track_data->length, &olen);
179     if (retval  != 0)
180         goto cleanup;
181     assert(olen <= 65536);
182     output->length = olen;
183     output->data = k5alloc(output->length, &retval);
184     if (retval)
185         goto cleanup;
186     tmp_enc_data.ciphertext = *output;
187     tmp_enc_data.enctype = sam_key.enctype;
188     tmp_enc_data.kvno = 0;
189 
190     retval = krb5_c_encrypt(context, &sam_key,
191                             KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0,
192                             track_data, &tmp_enc_data);
193 cleanup:
194     krb5_free_keyblock_contents(context, &sam_key);
195 
196     if (retval) {
197         output->length = 0;
198         free(output->data);
199         output->data = NULL;
200         return retval;
201     }
202     return 0;
203 }
204 
205 
206 krb5_error_code
get_securid_edata_2(krb5_context context,krb5_db_entry * client,krb5_keyblock * client_key,krb5_sam_challenge_2 * sc2)207 get_securid_edata_2(krb5_context context, krb5_db_entry *client,
208                     krb5_keyblock *client_key, krb5_sam_challenge_2 *sc2)
209 {
210     krb5_error_code retval;
211     krb5_data scratch, track_id = empty_data();
212     char *user = NULL;
213     char *def_user = "<unknown user>";
214     struct securid_track_data sid_track_data;
215     krb5_data tmp_data;
216     krb5_sam_challenge_2_body sc2b;
217 
218     scratch.data = NULL;
219 
220     retval = krb5_unparse_name(context, client->princ, &user);
221     if (retval)
222         goto cleanup;
223 
224     memset(&sc2b, 0, sizeof(sc2b));
225     sc2b.magic = KV5M_SAM_CHALLENGE_2;
226     sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;
227     sc2b.sam_type_name.length = 0;
228     sc2b.sam_challenge_label.length = 0;
229     sc2b.sam_challenge.length = 0;
230     sc2b.sam_response_prompt.data = PASSCODE_message;
231     sc2b.sam_response_prompt.length = strlen(sc2b.sam_response_prompt.data);
232     sc2b.sam_pk_for_sad.length = 0;
233     sc2b.sam_type = PA_SAM_TYPE_SECURID;
234 
235     sid_track_data.state = SECURID_STATE_INITIAL;
236     sid_track_data.hostid = gethostid();
237     tmp_data.data = (char *)&sid_track_data;
238     tmp_data.length = sizeof(sid_track_data);
239     retval = securid_encrypt_track_data_2(context, client, &tmp_data,
240                                           &track_id);
241     if (retval != 0) {
242         com_err("krb5kdc", retval, "while encrypting nonce track data");
243         goto cleanup;
244     }
245     sc2b.sam_track_id = track_id;
246 
247     scratch.data = (char *)&sc2b.sam_nonce;
248     scratch.length = sizeof(sc2b.sam_nonce);
249     retval = krb5_c_random_make_octets(context, &scratch);
250     if (retval) {
251         com_err("krb5kdc", retval,
252                 "while generating nonce data in get_securid_edata_2 (%s)",
253                 user ? user : def_user);
254         goto cleanup;
255     }
256 
257     /* Get the client's key */
258     sc2b.sam_etype = client_key->enctype;
259 
260     retval = sam_make_challenge(context, &sc2b, client_key, sc2);
261     if (retval) {
262         com_err("krb5kdc", retval,
263                 "while making SAM_CHALLENGE_2 checksum (%s)",
264                 user ? user : def_user);
265     }
266 
267 cleanup:
268     free(user);
269     krb5_free_data_contents(context, &track_id);
270     return retval;
271 }
272 
273 krb5_error_code
verify_securid_data_2(krb5_context context,krb5_db_entry * client,krb5_sam_response_2 * sr2,krb5_enc_tkt_part * enc_tkt_reply,krb5_pa_data * pa,krb5_sam_challenge_2 ** sc2_out)274 verify_securid_data_2(krb5_context context, krb5_db_entry *client,
275                       krb5_sam_response_2 *sr2,
276                       krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa,
277                       krb5_sam_challenge_2 **sc2_out)
278 {
279     krb5_error_code retval;
280     int new_pin = 0;
281     krb5_key_data *client_key_data = NULL;
282     krb5_keyblock client_key;
283     krb5_data scratch;
284     krb5_enc_sam_response_enc_2 *esre2 = NULL;
285     struct securid_track_data sid_track_data, *trackp = NULL;
286     krb5_data tmp_data;
287     SDI_HANDLE sd_handle = SDI_HANDLE_NONE;
288     krb5_sam_challenge_2 *sc2p = NULL;
289     char *cp, *user = NULL;
290     char *securid_user = NULL;
291     char passcode[LENPRNST+1];
292     char max_pin_len, min_pin_len, alpha_pin;
293 
294     memset(&client_key, 0, sizeof(client_key));
295     memset(&scratch, 0, sizeof(scratch));
296     *sc2_out = NULL;
297 
298     retval = krb5_unparse_name(context, client->princ, &user);
299     if (retval != 0) {
300         com_err("krb5kdc", retval,
301                 "while unparsing client name in verify_securid_data_2");
302         return retval;
303     }
304 
305     if ((sr2->sam_enc_nonce_or_sad.ciphertext.data == NULL) ||
306         (sr2->sam_enc_nonce_or_sad.ciphertext.length <= 0)) {
307         retval = KRB5KDC_ERR_PREAUTH_FAILED;
308         k5_setmsg(context, retval,
309                   "No preauth data supplied in verify_securid_data_2 (%s)",
310                   user);
311         goto cleanup;
312     }
313 
314     retval = krb5_dbe_find_enctype(context, client,
315                                    sr2->sam_enc_nonce_or_sad.enctype, -1,
316                                    sr2->sam_enc_nonce_or_sad.kvno,
317                                    &client_key_data);
318     if (retval) {
319         com_err("krb5kdc", retval,
320                 "while getting client key in verify_securid_data_2 (%s)",
321                 user);
322         goto cleanup;
323     }
324 
325     retval = krb5_dbe_decrypt_key_data(context, NULL, client_key_data,
326                                        &client_key, NULL);
327     if (retval != 0) {
328         com_err("krb5kdc", retval,
329                 "while decrypting client key in verify_securid_data_2 (%s)",
330                 user);
331         goto cleanup;
332     }
333 
334     scratch.length = sr2->sam_enc_nonce_or_sad.ciphertext.length;
335     scratch.data = k5alloc(scratch.length, &retval);
336     if (retval)
337         goto cleanup;
338     retval = krb5_c_decrypt(context, &client_key,
339                             KRB5_KEYUSAGE_PA_SAM_RESPONSE, 0,
340                             &sr2->sam_enc_nonce_or_sad, &scratch);
341     if (retval) {
342         com_err("krb5kdc", retval,
343                 "while decrypting SAD in verify_securid_data_2 (%s)", user);
344         goto cleanup;
345     }
346 
347     retval = decode_krb5_enc_sam_response_enc_2(&scratch, &esre2);
348     if (retval) {
349         com_err("krb5kdc", retval,
350                 "while decoding SAD in verify_securid_data_2 (%s)", user);
351         esre2 = NULL;
352         goto cleanup;
353     }
354 
355     if (sr2->sam_nonce != esre2->sam_nonce) {
356         com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED,
357                 "while checking nonce in verify_securid_data_2 (%s)", user);
358         retval = KRB5KDC_ERR_PREAUTH_FAILED;
359         goto cleanup;
360     }
361 
362     if (esre2->sam_sad.length == 0 || esre2->sam_sad.data == NULL) {
363         com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED,
364                 "No SecurID passcode in verify_securid_data_2 (%s)", user);
365         retval = KRB5KDC_ERR_PREAUTH_FAILED;
366         goto cleanup;
367     }
368 
369     /* Copy out SAD to null-terminated buffer */
370     memset(passcode, 0, sizeof(passcode));
371     if (esre2->sam_sad.length > (sizeof(passcode) - 1)) {
372         retval = KRB5KDC_ERR_PREAUTH_FAILED;
373         com_err("krb5kdc", retval,
374                 "SecurID passcode/PIN too long (%d bytes) in "
375                 "verify_securid_data_2 (%s)",
376                 esre2->sam_sad.length, user);
377         goto cleanup;
378     }
379     if (esre2->sam_sad.length > 0)
380         memcpy(passcode, esre2->sam_sad.data, esre2->sam_sad.length);
381 
382     securid_user = strdup(user);
383     if (!securid_user) {
384         retval = ENOMEM;
385         com_err("krb5kdc", ENOMEM,
386                 "while copying user name in verify_securid_data_2 (%s)", user);
387         goto cleanup;
388     }
389     cp = strchr(securid_user, '@');
390     if (cp != NULL)
391         *cp = '\0';
392 
393     /* Check for any track_id data that may have state from a previous attempt
394      * at SecurID authentication. */
395 
396     if (sr2->sam_track_id.data && (sr2->sam_track_id.length > 0)) {
397         krb5_data track_id_data;
398 
399         memset(&track_id_data, 0, sizeof(track_id_data));
400         retval = securid_decrypt_track_data_2(context, client,
401                                               &sr2->sam_track_id,
402                                               &track_id_data);
403         if (retval) {
404             com_err("krb5kdc", retval,
405                     "while decrypting SecurID trackID in "
406                     "verify_securid_data_2 (%s)", user);
407             goto cleanup;
408         }
409         if (track_id_data.length < sizeof (struct securid_track_data)) {
410             retval = KRB5KDC_ERR_PREAUTH_FAILED;
411             com_err("krb5kdc", retval, "Length of track data incorrect");
412             goto cleanup;
413         }
414         trackp = (struct securid_track_data *)track_id_data.data;
415 
416         if(trackp->hostid != gethostid()) {
417             krb5_klog_syslog(LOG_INFO, "Unexpected challenge response");
418             retval = KRB5KDC_ERR_DISCARD;
419             goto cleanup;
420         }
421 
422         switch(trackp->state) {
423         case SECURID_STATE_INITIAL:
424             goto initial;
425             break;
426         case SECURID_STATE_NEW_PIN_AGAIN:
427         {
428             int pin1_len, pin2_len;
429 
430             trackp->handle = ntohl(trackp->handle);
431             pin2_len = strlen(passcode);
432             pin1_len = strlen(trackp->passcode);
433 
434             if ((pin1_len != pin2_len) ||
435                 (memcmp(passcode, trackp->passcode, pin1_len) != 0)) {
436                 retval = KRB5KDC_ERR_PREAUTH_FAILED;
437                 krb5_klog_syslog(LOG_INFO, "New SecurID PIN Failed for user "
438                                  "%s: PIN mismatch", user);
439                 break;
440             }
441             retval = SD_Pin(trackp->handle, passcode);
442             SD_Close(trackp->handle);
443             if (retval == ACM_NEW_PIN_ACCEPTED) {
444                 enc_tkt_reply->flags|=  TKT_FLG_HW_AUTH;
445                 enc_tkt_reply->flags|=  TKT_FLG_PRE_AUTH;
446                 krb5_klog_syslog(LOG_INFO, "SecurID PIN Accepted for %s in "
447                                  "verify_securid_data_2",
448                                  securid_user);
449                 retval = 0;
450             } else {
451                 retval = KRB5KDC_ERR_PREAUTH_FAILED;
452                 krb5_klog_syslog(LOG_INFO,
453                                  "SecurID PIN Failed for user %s (AceServer "
454                                  "returns %d) in verify_securid_data_2",
455                                  user, retval);
456             }
457             break;
458         }
459         case SECURID_STATE_NEW_PIN: {
460             krb5_sam_challenge_2_body sc2b;
461             sc2p = k5alloc(sizeof *sc2p, &retval);
462             if (retval)
463                 goto cleanup;
464             memset(sc2p, 0, sizeof(*sc2p));
465             memset(&sc2b, 0, sizeof(sc2b));
466             sc2b.sam_type = PA_SAM_TYPE_SECURID;
467             sc2b.sam_response_prompt.data = NEW_PIN_AGAIN_message;
468             sc2b.sam_response_prompt.length =
469                 strlen(sc2b.sam_response_prompt.data);
470             sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;
471             sc2b.sam_etype = client_key.enctype;
472 
473             tmp_data.data = (char *)&sc2b.sam_nonce;
474             tmp_data.length = sizeof(sc2b.sam_nonce);
475             if ((retval = krb5_c_random_make_octets(context, &tmp_data))) {
476                 com_err("krb5kdc", retval,
477                         "while making nonce for SecurID new "
478                         "PIN2 SAM_CHALLENGE_2 (%s)", user);
479                 goto cleanup;
480             }
481             sid_track_data.state = SECURID_STATE_NEW_PIN_AGAIN;
482             sid_track_data.handle = trackp->handle;
483             sid_track_data.hostid = gethostid();
484             /* Should we complain if sizes don't work ??  */
485             memcpy(sid_track_data.passcode, passcode,
486                    sizeof(sid_track_data.passcode));
487             tmp_data.data = (char *)&sid_track_data;
488             tmp_data.length = sizeof(sid_track_data);
489             if ((retval = securid_encrypt_track_data_2(context, client,
490                                                        &tmp_data,
491                                                        &sc2b.sam_track_id))) {
492                 com_err("krb5kdc", retval,
493                         "while encrypting NEW PIN2 SecurID "
494                         "track data for SAM_CHALLENGE_2 (%s)",
495                         securid_user);
496                 goto cleanup;
497             }
498             retval = sam_make_challenge(context, &sc2b, &client_key, sc2p);
499             if (retval) {
500                 com_err("krb5kdc", retval,
501                         "while making cksum for "
502                         "SAM_CHALLENGE_2 (new PIN2) (%s)", securid_user);
503                 goto cleanup;
504             }
505             krb5_klog_syslog(LOG_INFO,
506                              "Requesting verification of new PIN for user %s",
507                              securid_user);
508             *sc2_out = sc2p;
509             sc2p = NULL;
510             /*sc2_out may be set even on error path*/
511             retval = KRB5KDC_ERR_PREAUTH_REQUIRED;
512             goto cleanup;
513         }
514         case SECURID_STATE_NEXT_CODE:
515             trackp->handle = ntohl(trackp->handle);
516             retval = SD_Next(trackp->handle, passcode);
517             SD_Close(trackp->handle);
518             if (retval == ACM_OK) {
519                 enc_tkt_reply->flags |=  TKT_FLG_HW_AUTH | TKT_FLG_PRE_AUTH;
520 
521                 krb5_klog_syslog(LOG_INFO, "Next SecurID Code Accepted for "
522                                  "user %s", securid_user);
523                 retval = 0;
524             } else {
525                 krb5_klog_syslog(LOG_INFO, "Next SecurID Code Failed for user "
526                                  "%s (AceServer returns %d) in "
527                                  "verify_securid_data_2", user, retval);
528                 retval = KRB5KDC_ERR_PREAUTH_FAILED;
529             }
530             break;
531         }
532     } else {            /* No track data, this is first of N attempts */
533     initial:
534         retval = SD_Init(&sd_handle);
535         if (retval) {
536             com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED,
537                     "SD_Init() returns error %d in verify_securid_data_2 (%s)",
538                     retval, securid_user);
539             retval = KRB5KDC_ERR_PREAUTH_FAILED;
540             goto cleanup;
541         }
542 
543         retval = SD_Lock(sd_handle, securid_user);
544         if (retval != ACM_OK) {
545             SD_Close(sd_handle);
546             retval = KRB5KDC_ERR_PREAUTH_FAILED;
547             krb5_klog_syslog(LOG_INFO,
548                              "SD_Lock() failed (AceServer returns %d) for %s",
549                              retval, securid_user);
550             goto cleanup;
551         }
552 
553         retval = SD_Check(sd_handle, passcode, securid_user);
554         switch (retval) {
555         case ACM_OK:
556             SD_Close(sd_handle);
557             enc_tkt_reply->flags|=  TKT_FLG_HW_AUTH;
558             enc_tkt_reply->flags|=  TKT_FLG_PRE_AUTH;
559             krb5_klog_syslog(LOG_INFO, "SecurID passcode accepted for user %s",
560                              user);
561             retval = 0;
562             break;
563         case ACM_ACCESS_DENIED:
564             SD_Close(sd_handle);
565             retval = KRB5KDC_ERR_PREAUTH_FAILED;
566             krb5_klog_syslog(LOG_INFO, "AceServer returns Access Denied for "
567                              "user %s (SAM2)", user);
568             goto cleanup;
569         case ACM_NEW_PIN_REQUIRED:
570             new_pin = 1;
571             /*fall through*/
572         case ACM_NEXT_CODE_REQUIRED: {
573             krb5_sam_challenge_2_body sc2b;
574             sc2p = k5alloc(sizeof *sc2p, &retval);
575             if (retval)
576                 goto cleanup;
577 
578             memset(sc2p, 0, sizeof(*sc2p));
579             memset(&sc2b, 0, sizeof(sc2b));
580 
581             sc2b.sam_type = PA_SAM_TYPE_SECURID;
582             sc2b.sam_response_prompt.data = NEXT_PASSCODE_message;
583             sc2b.sam_response_prompt.length =
584                 strlen(sc2b.sam_response_prompt.data);
585             sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;
586             sc2b.sam_etype = client_key.enctype;
587             if (new_pin) {
588                 if ((AceGetMaxPinLen(sd_handle, &max_pin_len) == ACE_SUCCESS)
589                     && (AceGetMinPinLen(sd_handle,
590                                         &min_pin_len) == ACE_SUCCESS)
591                     && (AceGetAlphanumeric(sd_handle,
592                                            &alpha_pin) == ACE_SUCCESS)) {
593                     sprintf(PIN_message,
594                             "New PIN must contain %d to %d %sdigits",
595                             min_pin_len, max_pin_len,
596                             (alpha_pin == 0) ? "" : "alphanumeric ");
597                     sc2b.sam_challenge_label.data = PIN_message;
598                     sc2b.sam_challenge_label.length =
599                         strlen(sc2b.sam_challenge_label.data);
600                 } else {
601                     sc2b.sam_challenge_label.length = 0;
602                 }
603             }
604 
605             tmp_data.data = (char *)&sc2b.sam_nonce;
606             tmp_data.length = sizeof(sc2b.sam_nonce);
607             if ((retval = krb5_c_random_make_octets(context, &tmp_data))) {
608                 com_err("krb5kdc", retval,
609                         "while making nonce for SecurID SAM_CHALLENGE_2 (%s)",
610                         user);
611                 goto cleanup;
612             }
613             if (new_pin)
614                 sid_track_data.state = SECURID_STATE_NEW_PIN;
615             else
616                 sid_track_data.state = SECURID_STATE_NEXT_CODE;
617             sid_track_data.handle = htonl(sd_handle);
618             sid_track_data.hostid = gethostid();
619             tmp_data.data = (char *)&sid_track_data;
620             tmp_data.length = sizeof(sid_track_data);
621             retval = securid_encrypt_track_data_2(context, client, &tmp_data,
622                                                   &sc2b.sam_track_id);
623             if (retval) {
624                 com_err("krb5kdc", retval,
625                         "while encrypting SecurID track "
626                         "data for SAM_CHALLENGE_2 (%s)",
627                         securid_user);
628                 goto cleanup;
629             }
630             retval = sam_make_challenge(context, &sc2b, &client_key, sc2p);
631             if (retval) {
632                 com_err("krb5kdc", retval,
633                         "while making cksum for SAM_CHALLENGE_2 (%s)",
634                         securid_user);
635             }
636             if (new_pin)
637                 krb5_klog_syslog(LOG_INFO, "New SecurID PIN required for "
638                                  "user %s", securid_user);
639             else
640                 krb5_klog_syslog(LOG_INFO, "Next SecurID passcode required "
641                                  "for user %s", securid_user);
642             *sc2_out = sc2p;
643             sc2p = NULL;
644             retval = KRB5KDC_ERR_PREAUTH_REQUIRED;
645             /*sc2_out is permitted as an output on error path*/
646             goto cleanup;
647         }
648         default:
649             com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED,
650                     "AceServer returns unknown error code %d "
651                     "in verify_securid_data_2\n", retval);
652             retval = KRB5KDC_ERR_PREAUTH_FAILED;
653             goto cleanup;
654         }
655     }   /* no track_id data */
656 
657 cleanup:
658     krb5_free_keyblock_contents(context, &client_key);
659     free(scratch.data);
660     krb5_free_enc_sam_response_enc_2(context, esre2);
661     free(user);
662     free(securid_user);
663     free(trackp);
664     krb5_free_sam_challenge_2(context, sc2p);
665     return retval;
666 }
667 
668 #endif /* ARL_SECURID_PREAUTH */
669