1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/mk_cred.c - definition of krb5_mk_ncred(), krb5_mk_1cred() */
3 /*
4 * Copyright (C) 2019 by the Massachusetts Institute of Technology.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "k5-int.h"
34 #include "int-proto.h"
35 #include "auth_con.h"
36
37 /* Encrypt the enc_part of krb5_cred. key may be NULL to use the unencrypted
38 * KRB-CRED form (RFC 6448). */
39 static krb5_error_code
encrypt_credencpart(krb5_context context,krb5_cred_enc_part * encpart,krb5_key key,krb5_enc_data * encdata_out)40 encrypt_credencpart(krb5_context context, krb5_cred_enc_part *encpart,
41 krb5_key key, krb5_enc_data *encdata_out)
42 {
43 krb5_error_code ret;
44 krb5_data *der_enccred;
45
46 /* Start by encoding to-be-encrypted part of the message. */
47 ret = encode_krb5_enc_cred_part(encpart, &der_enccred);
48 if (ret)
49 return ret;
50
51 if (key == NULL) {
52 /* Just copy the encoded data to the ciphertext area. */
53 encdata_out->enctype = ENCTYPE_NULL;
54 encdata_out->ciphertext = *der_enccred;
55 free(der_enccred);
56 return 0;
57 }
58
59 ret = k5_encrypt_keyhelper(context, key, KRB5_KEYUSAGE_KRB_CRED_ENCPART,
60 der_enccred, encdata_out);
61
62 zapfreedata(der_enccred);
63 return ret;
64 }
65
66 /*
67 * Marshal a KRB-CRED message into der_out, encrypted with key (or unencrypted
68 * if key is NULL). Store the ciphertext in enc_out. Use the timestamp and
69 * sequence number from rdata and the addresses from local_addr and remote_addr
70 * (either of which may be NULL). der_out and enc_out should be freed by the
71 * caller when finished.
72 */
73 static krb5_error_code
create_krbcred(krb5_context context,krb5_creds ** creds,krb5_key key,const krb5_replay_data * rdata,krb5_address * local_addr,krb5_address * remote_addr,krb5_data ** der_out,krb5_enc_data * enc_out)74 create_krbcred(krb5_context context, krb5_creds **creds, krb5_key key,
75 const krb5_replay_data *rdata, krb5_address *local_addr,
76 krb5_address *remote_addr, krb5_data **der_out,
77 krb5_enc_data *enc_out)
78 {
79 krb5_error_code ret;
80 krb5_cred_enc_part credenc;
81 krb5_cred cred;
82 krb5_ticket **tickets = NULL;
83 krb5_cred_info **ticket_info = NULL, *tinfos = NULL;
84 krb5_enc_data enc;
85 size_t i, ncreds;
86
87 *der_out = NULL;
88 memset(enc_out, 0, sizeof(*enc_out));
89 memset(&enc, 0, sizeof(enc));
90
91 for (ncreds = 0; creds[ncreds] != NULL; ncreds++);
92
93 tickets = k5calloc(ncreds + 1, sizeof(*tickets), &ret);
94 if (tickets == NULL)
95 goto cleanup;
96
97 ticket_info = k5calloc(ncreds + 1, sizeof(*ticket_info), &ret);
98 if (ticket_info == NULL)
99 goto cleanup;
100
101 tinfos = k5calloc(ncreds, sizeof(*tinfos), &ret);
102 if (tinfos == NULL)
103 goto cleanup;
104
105 /* For each credential in the list, decode the ticket and create a cred
106 * info structure using alias pointers. */
107 for (i = 0; i < ncreds; i++) {
108 ret = decode_krb5_ticket(&creds[i]->ticket, &tickets[i]);
109 if (ret)
110 goto cleanup;
111
112 tinfos[i].magic = KV5M_CRED_INFO;
113 tinfos[i].times = creds[i]->times;
114 tinfos[i].flags = creds[i]->ticket_flags;
115 tinfos[i].session = &creds[i]->keyblock;
116 tinfos[i].client = creds[i]->client;
117 tinfos[i].server = creds[i]->server;
118 tinfos[i].caddrs = creds[i]->addresses;
119 ticket_info[i] = &tinfos[i];
120 }
121
122 /* Encrypt the credential encrypted part. */
123 credenc.magic = KV5M_CRED_ENC_PART;
124 credenc.s_address = local_addr;
125 credenc.r_address = remote_addr;
126 credenc.nonce = rdata->seq;
127 credenc.usec = rdata->usec;
128 credenc.timestamp = rdata->timestamp;
129 credenc.ticket_info = ticket_info;
130 ret = encrypt_credencpart(context, &credenc, key, &enc);
131 if (ret)
132 goto cleanup;
133
134 /* Encode the KRB-CRED message. */
135 cred.magic = KV5M_CRED;
136 cred.tickets = tickets;
137 cred.enc_part = enc;
138 ret = encode_krb5_cred(&cred, der_out);
139 if (ret)
140 goto cleanup;
141
142 *enc_out = enc;
143 memset(&enc, 0, sizeof(enc));
144
145 cleanup:
146 krb5_free_tickets(context, tickets);
147 krb5_free_data_contents(context, &enc.ciphertext);
148 free(tinfos);
149 free(ticket_info);
150 return ret;
151 }
152
153 krb5_error_code KRB5_CALLCONV
krb5_mk_ncred(krb5_context context,krb5_auth_context authcon,krb5_creds ** creds,krb5_data ** der_out,krb5_replay_data * rdata_out)154 krb5_mk_ncred(krb5_context context, krb5_auth_context authcon,
155 krb5_creds **creds, krb5_data **der_out,
156 krb5_replay_data *rdata_out)
157 {
158 krb5_error_code ret;
159 krb5_key key;
160 krb5_replay_data rdata;
161 krb5_data *der_krbcred = NULL;
162 krb5_enc_data enc;
163 krb5_address *local_addr, *remote_addr, lstorage, rstorage;
164
165 *der_out = NULL;
166 memset(&enc, 0, sizeof(enc));
167 memset(&lstorage, 0, sizeof(lstorage));
168 memset(&rstorage, 0, sizeof(rstorage));
169
170 if (creds == NULL)
171 return KRB5KRB_AP_ERR_BADADDR;
172
173 ret = k5_privsafe_gen_rdata(context, authcon, &rdata, rdata_out);
174 if (ret)
175 goto cleanup;
176 /* Historically we always set the timestamp, so keep doing that. */
177 if (rdata.timestamp == 0) {
178 ret = krb5_us_timeofday(context, &rdata.timestamp, &rdata.usec);
179 if (ret)
180 goto cleanup;
181 }
182
183 ret = k5_privsafe_gen_addrs(context, authcon, &lstorage, &rstorage,
184 &local_addr, &remote_addr);
185 if (ret)
186 goto cleanup;
187
188 key = (authcon->send_subkey != NULL) ? authcon->send_subkey : authcon->key;
189 ret = create_krbcred(context, creds, key, &rdata, local_addr, remote_addr,
190 &der_krbcred, &enc);
191 if (ret)
192 goto cleanup;
193
194 if (key != NULL) {
195 ret = k5_privsafe_check_replay(context, authcon, NULL, &enc, NULL);
196 if (ret)
197 goto cleanup;
198 }
199
200 *der_out = der_krbcred;
201 der_krbcred = NULL;
202 if ((authcon->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
203 (authcon->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
204 authcon->local_seq_number++;
205
206 cleanup:
207 krb5_free_data_contents(context, &enc.ciphertext);
208 free(lstorage.contents);
209 free(rstorage.contents);
210 zapfreedata(der_krbcred);
211 return ret;
212 }
213
214 krb5_error_code KRB5_CALLCONV
krb5_mk_1cred(krb5_context context,krb5_auth_context authcon,krb5_creds * creds,krb5_data ** der_out,krb5_replay_data * rdata_out)215 krb5_mk_1cred(krb5_context context, krb5_auth_context authcon,
216 krb5_creds *creds, krb5_data **der_out,
217 krb5_replay_data *rdata_out)
218 {
219 krb5_error_code retval;
220 krb5_creds **list;
221
222 list = calloc(2, sizeof(*list));
223 if (list == NULL)
224 return ENOMEM;
225
226 list[0] = creds;
227 list[1] = NULL;
228 retval = krb5_mk_ncred(context, authcon, list, der_out, rdata_out);
229 free(list);
230 return retval;
231 }
232