1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/mk_safe.c - definition of krb5_mk_safe() */
3 /*
4 * Copyright 1990,1991,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 /*
38 * Marshal a KRB-SAFE message into der_out, with a keyed checksum of type
39 * sumtype. Store the checksum in cksum_out. Use the timestamp and sequence
40 * number from rdata and the addresses from local_addr and remote_addr (the
41 * second of which may be NULL). der_out and cksum_out should be freed by the
42 * caller when finished.
43 */
44 static krb5_error_code
create_krbsafe(krb5_context context,const krb5_data * userdata,krb5_key key,const krb5_replay_data * rdata,krb5_address * local_addr,krb5_address * remote_addr,krb5_cksumtype sumtype,krb5_data * der_out,krb5_checksum * cksum_out)45 create_krbsafe(krb5_context context, const krb5_data *userdata, krb5_key key,
46 const krb5_replay_data *rdata, krb5_address *local_addr,
47 krb5_address *remote_addr, krb5_cksumtype sumtype,
48 krb5_data *der_out, krb5_checksum *cksum_out)
49 {
50 krb5_error_code ret;
51 krb5_safe safemsg;
52 krb5_octet zero_octet = 0;
53 krb5_checksum safe_checksum;
54 krb5_data *der_krbsafe;
55
56 if (sumtype && !krb5_c_valid_cksumtype(sumtype))
57 return KRB5_PROG_SUMTYPE_NOSUPP;
58 if (sumtype && !krb5_c_is_keyed_cksum(sumtype))
59 return KRB5KRB_AP_ERR_INAPP_CKSUM;
60
61 safemsg.user_data = *userdata;
62 safemsg.s_address = local_addr;
63 safemsg.r_address = remote_addr;
64 safemsg.timestamp = rdata->timestamp;
65 safemsg.usec = rdata->usec;
66 safemsg.seq_number = rdata->seq;
67
68 /* Encode the message with a zero-length zero-type checksum. */
69 safe_checksum.length = 0;
70 safe_checksum.checksum_type = 0;
71 safe_checksum.contents = &zero_octet;
72 safemsg.checksum = &safe_checksum;
73 ret = encode_krb5_safe(&safemsg, &der_krbsafe);
74 if (ret)
75 return ret;
76
77 /* Checksum the encoding. */
78 ret = krb5_k_make_checksum(context, sumtype, key,
79 KRB5_KEYUSAGE_KRB_SAFE_CKSUM, der_krbsafe,
80 &safe_checksum);
81 zapfreedata(der_krbsafe);
82 if (ret)
83 return ret;
84
85 /* Encode the message again with the real checksum. */
86 safemsg.checksum = &safe_checksum;
87 ret = encode_krb5_safe(&safemsg, &der_krbsafe);
88 if (ret) {
89 krb5_free_checksum_contents(context, &safe_checksum);
90 return ret;
91 }
92
93 *der_out = *der_krbsafe;
94 free(der_krbsafe);
95 *cksum_out = safe_checksum;
96 return 0;
97 }
98
99 /* Return the checksum type for the KRB-SAFE message, or 0 to use the enctype's
100 * mandatory checksum. */
101 static krb5_cksumtype
safe_cksumtype(krb5_context context,krb5_auth_context auth_context,krb5_enctype enctype)102 safe_cksumtype(krb5_context context, krb5_auth_context auth_context,
103 krb5_enctype enctype)
104 {
105 krb5_error_code ret;
106 unsigned int nsumtypes, i;
107 krb5_cksumtype *sumtypes;
108
109 /* Use the auth context's safe_cksumtype if it is valid for the enctype.
110 * Otherwise return 0 for the mandatory checksum. */
111 ret = krb5_c_keyed_checksum_types(context, enctype, &nsumtypes, &sumtypes);
112 if (ret != 0)
113 return 0;
114 for (i = 0; i < nsumtypes; i++) {
115 if (auth_context->safe_cksumtype == sumtypes[i])
116 break;
117 }
118 krb5_free_cksumtypes(context, sumtypes);
119 return (i == nsumtypes) ? 0 : auth_context->safe_cksumtype;
120 }
121
122 krb5_error_code KRB5_CALLCONV
krb5_mk_safe(krb5_context context,krb5_auth_context authcon,const krb5_data * userdata,krb5_data * der_out,krb5_replay_data * rdata_out)123 krb5_mk_safe(krb5_context context, krb5_auth_context authcon,
124 const krb5_data *userdata, krb5_data *der_out,
125 krb5_replay_data *rdata_out)
126 {
127 krb5_error_code ret;
128 krb5_key key;
129 krb5_replay_data rdata;
130 krb5_data der_krbsafe = empty_data();
131 krb5_checksum cksum;
132 krb5_address *local_addr, *remote_addr, lstorage, rstorage;
133 krb5_cksumtype sumtype;
134
135 *der_out = empty_data();
136 memset(&cksum, 0, sizeof(cksum));
137 memset(&lstorage, 0, sizeof(lstorage));
138 memset(&rstorage, 0, sizeof(rstorage));
139 if (authcon->local_addr == NULL)
140 return KRB5_LOCAL_ADDR_REQUIRED;
141
142 ret = k5_privsafe_gen_rdata(context, authcon, &rdata, rdata_out);
143 if (ret)
144 goto cleanup;
145
146 ret = k5_privsafe_gen_addrs(context, authcon, &lstorage, &rstorage,
147 &local_addr, &remote_addr);
148 if (ret)
149 goto cleanup;
150
151 key = (authcon->send_subkey != NULL) ? authcon->send_subkey : authcon->key;
152 sumtype = safe_cksumtype(context, authcon, key->keyblock.enctype);
153 ret = create_krbsafe(context, userdata, key, &rdata, local_addr,
154 remote_addr, sumtype, &der_krbsafe, &cksum);
155 if (ret)
156 goto cleanup;
157
158 ret = k5_privsafe_check_replay(context, authcon, NULL, NULL, &cksum);
159 if (ret)
160 goto cleanup;
161
162 *der_out = der_krbsafe;
163 der_krbsafe = empty_data();
164 if ((authcon->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
165 (authcon->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
166 authcon->local_seq_number++;
167
168 cleanup:
169 krb5_free_data_contents(context, &der_krbsafe);
170 krb5_free_checksum_contents(context, &cksum);
171 free(lstorage.contents);
172 free(rstorage.contents);
173 return ret;
174 }
175