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