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