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*/
19static krb5_error_code
20decrypt_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
47cleanup:
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
60static krb5_error_code
61krb5_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
155cleanup:
156    if (retval)
157	krb5_free_tgt_creds(context, *pppcreds);
158
159cleanup_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 */
173krb5_error_code KRB5_CALLCONV
174krb5_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
244error:;
245    if (retval) {
246    	krb5_free_tgt_creds(context, *pppcreds);
247	*pppcreds = NULL;
248    }
249    return retval;
250}
251
252