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