xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/rd_cred.c (revision 2b24ab6b3865caeede9eeb9db6b83e1d89dcd1ea)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 
7 #include "k5-int.h"
8 #include "cleanup.h"
9 #include "auth_con.h"
10 
11 #include <stddef.h>           /* NULL */
12 #include <stdlib.h>           /* malloc */
13 #include <errno.h>            /* ENOMEM */
14 
15 /*-------------------- decrypt_credencdata --------------------*/
16 
17 /*
18  * decrypt the enc_part of a krb5_cred
19  */
20 /*ARGSUSED*/
21 static krb5_error_code
22 decrypt_credencdata(krb5_context context, krb5_cred *pcred, krb5_keyblock *pkeyblock, krb5_cred_enc_part *pcredenc)
23 {
24     krb5_cred_enc_part  * ppart = NULL;
25     krb5_error_code 	  retval;
26     krb5_data 		  scratch;
27 
28     scratch.length = pcred->enc_part.ciphertext.length;
29     if (!(scratch.data = (char *)malloc(scratch.length)))
30 	return ENOMEM;
31 
32     if (pkeyblock != NULL) {
33 	if ((retval = krb5_c_decrypt(context, pkeyblock,
34 				     KRB5_KEYUSAGE_KRB_CRED_ENCPART, 0,
35 				     &pcred->enc_part, &scratch)))
36 	    goto cleanup;
37     } else {
38 	/* Solaris Kerberos */
39 	(void) memcpy(scratch.data, pcred->enc_part.ciphertext.data, scratch.length);
40     }
41 
42     /*  now decode the decrypted stuff */
43     if ((retval = decode_krb5_enc_cred_part(&scratch, &ppart)))
44     	goto cleanup;
45 
46     *pcredenc = *ppart;
47     retval = 0;
48 
49 cleanup:
50     if (ppart != NULL) {
51 	memset(ppart, 0, sizeof(*ppart));
52 	krb5_xfree(ppart);
53     }
54     /* Solaris Kerberos */
55     (void) memset(scratch.data, 0, scratch.length);
56     krb5_xfree(scratch.data);
57 
58     return retval;
59 }
60 /*----------------------- krb5_rd_cred_basic -----------------------*/
61 
62 static krb5_error_code
63 krb5_rd_cred_basic(krb5_context context, krb5_data *pcreddata, krb5_keyblock *pkeyblock, krb5_replay_data *replaydata, krb5_creds ***pppcreds)
64 {
65     krb5_error_code       retval;
66     krb5_cred 		* pcred;
67     krb5_int32 		  ncreds;
68     krb5_int32 		  i = 0;
69     krb5_cred_enc_part 	  encpart;
70 
71     /* decode cred message */
72     if ((retval = decode_krb5_cred(pcreddata, &pcred)))
73     	return retval;
74 
75     /* Solaris Kerberos */
76     (void) memset(&encpart, 0, sizeof(encpart));
77 
78     if ((retval = decrypt_credencdata(context, pcred, pkeyblock, &encpart)))
79 	goto cleanup_cred;
80 
81 
82     replaydata->timestamp = encpart.timestamp;
83     replaydata->usec = encpart.usec;
84     replaydata->seq = encpart.nonce;
85 
86    /*
87     * Allocate the list of creds.  The memory is allocated so that
88     * krb5_free_tgt_creds can be used to free the list.
89     */
90     for (ncreds = 0; pcred->tickets[ncreds]; ncreds++);
91 
92     if ((*pppcreds =
93         (krb5_creds **)malloc((size_t)(sizeof(krb5_creds *) *
94 				       (ncreds + 1)))) == NULL) {
95         retval = ENOMEM;
96         goto cleanup_cred;
97     }
98     (*pppcreds)[0] = NULL;
99 
100     /*
101      * For each credential, create a strcture in the list of
102      * credentials and copy the information.
103      */
104     while (i < ncreds) {
105         krb5_cred_info 	* pinfo;
106         krb5_creds 	* pcur;
107 	krb5_data	* pdata;
108 
109         if ((pcur = (krb5_creds *)malloc(sizeof(krb5_creds))) == NULL) {
110 	    retval = ENOMEM;
111 	    goto cleanup;
112         }
113 
114         (*pppcreds)[i] = pcur;
115         (*pppcreds)[i+1] = 0;
116         pinfo = encpart.ticket_info[i++];
117 	/* Solaris Kerberos */
118         (void) memset(pcur, 0, sizeof(krb5_creds));
119 
120         if ((retval = krb5_copy_principal(context, pinfo->client,
121 					  &pcur->client)))
122 	    goto cleanup;
123 
124         if ((retval = krb5_copy_principal(context, pinfo->server,
125 					  &pcur->server)))
126 	    goto cleanup;
127 
128       	if ((retval = krb5_copy_keyblock_contents(context, pinfo->session,
129 						  &pcur->keyblock)))
130 	    goto cleanup;
131 
132         if ((retval = krb5_copy_addresses(context, pinfo->caddrs,
133 					  &pcur->addresses)))
134 	    goto cleanup;
135 
136         if ((retval = encode_krb5_ticket(pcred->tickets[i - 1], &pdata)))
137 	    goto cleanup;
138 
139 	pcur->ticket = *pdata;
140 	krb5_xfree(pdata);
141 
142 
143         pcur->is_skey = FALSE;
144         pcur->magic = KV5M_CREDS;
145         pcur->times = pinfo->times;
146         pcur->ticket_flags = pinfo->flags;
147         pcur->authdata = NULL;   /* not used */
148 	/* Solaris Kerberos */
149         (void) memset(&pcur->second_ticket, 0, sizeof(pcur->second_ticket));
150     }
151 
152     /*
153      * NULL terminate the list
154      */
155     (*pppcreds)[i] = NULL;
156 
157 cleanup:
158     if (retval)
159 	krb5_free_tgt_creds(context, *pppcreds);
160 
161 cleanup_cred:
162     krb5_free_cred(context, pcred);
163     krb5_free_cred_enc_part(context, &encpart);
164 
165     return retval;
166 }
167 
168 /*----------------------- krb5_rd_cred -----------------------*/
169 
170 #define in_clock_skew(date) (labs((date)-currenttime) < context->clockskew)
171 
172 /*
173  * This functions takes as input an KRB_CRED message, validates it, and
174  * outputs the nonce and an array of the forwarded credentials.
175  */
176 krb5_error_code KRB5_CALLCONV
177 krb5_rd_cred(krb5_context context, krb5_auth_context auth_context, krb5_data *pcreddata, krb5_creds ***pppcreds, krb5_replay_data *outdata)
178 {
179     krb5_error_code       retval;
180     krb5_keyblock       * keyblock;
181     krb5_replay_data      replaydata;
182 
183     /* Get keyblock */
184     if ((keyblock = auth_context->recv_subkey) == NULL)
185 	keyblock = auth_context->keyblock;
186 
187     if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
188       (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
189       (outdata == NULL))
190         /* Need a better error */
191         return KRB5_RC_REQUIRED;
192 
193     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
194       (auth_context->rcache == NULL))
195         return KRB5_RC_REQUIRED;
196 
197 
198 /* If decrypting with the first keyblock we try fails, perhaps the
199  * credentials are stored in the session key so try decrypting with
200     * that.
201 */
202     if ((retval = krb5_rd_cred_basic(context, pcreddata, keyblock,
203 				     &replaydata, pppcreds))) {
204 	if ((retval = krb5_rd_cred_basic(context, pcreddata,
205 					 auth_context->keyblock,
206 					 &replaydata, pppcreds))) {
207 	    return retval;
208     }
209     }
210 
211     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
212         krb5_donot_replay replay;
213         krb5_timestamp currenttime;
214 
215         if ((retval = krb5_timeofday(context, &currenttime)))
216             goto error;
217 
218         if (!in_clock_skew(replaydata.timestamp)) {
219             retval =  KRB5KRB_AP_ERR_SKEW;
220             goto error;
221         }
222 
223         if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr,
224 					   "_forw", &replay.client)))
225             goto error;
226 
227         replay.server = "";             /* XXX */
228         replay.cusec = replaydata.usec;
229         replay.ctime = replaydata.timestamp;
230         if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
231             krb5_xfree(replay.client);
232             goto error;
233         }
234         krb5_xfree(replay.client);
235     }
236 
237     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
238         if (auth_context->remote_seq_number != replaydata.seq) {
239             retval =  KRB5KRB_AP_ERR_BADORDER;
240             goto error;
241         }
242         auth_context->remote_seq_number++;
243     }
244 
245     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
246       (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
247         outdata->timestamp = replaydata.timestamp;
248         outdata->usec = replaydata.usec;
249         outdata->seq = replaydata.seq;
250     }
251 
252 error:;
253     if (retval) {
254     	krb5_free_tgt_creds(context, *pppcreds);
255 	*pppcreds = NULL;
256     }
257     return retval;
258 }
259 
260