xref: /freebsd/crypto/krb5/src/lib/krb5/krb/mk_cred.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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