Oops in previous memory freeing fixes; was a bit too agressive freeing things
[krb5.git] / src / lib / krb5 / krb / mk_req_ext.c
1 /*
2  * lib/krb5/krb/mk_req_ext.c
3  *
4  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  * 
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  M.I.T. makes no representations about the suitability of
20  * this software for any purpose.  It is provided "as is" without express
21  * or implied warranty.
22  * 
23  *
24  * krb5_mk_req_extended()
25  */
26
27
28 #include <krb5/krb5.h>
29 #include <krb5/asn1.h>
30
31 #include <krb5/libos.h>
32 #include <krb5/los-proto.h>
33
34 #include <krb5/ext-proto.h>
35
36 /*
37  Formats a KRB_AP_REQ message into outbuf, with more complete options than
38  krb_mk_req.
39
40  outbuf, ap_req_options, checksum, and ccache are used in the
41  same fashion as for krb5_mk_req.
42
43  creds is used to supply the credentials (ticket and session key) needed
44  to form the request.
45
46  if creds->ticket has no data (length == 0), then a ticket is obtained
47  from either the cache or the TGS, passing creds to krb5_get_credentials().
48  kdc_options specifies the options requested for the ticket to be used.
49  If a ticket with appropriate flags is not found in the cache, then these
50  options are passed on in a request to an appropriate KDC.
51
52  ap_req_options specifies the KRB_AP_REQ options desired.
53
54  if ap_req_options specifies AP_OPTS_USE_SESSION_KEY, then creds->ticket
55  must contain the appropriate ENC-TKT-IN-SKEY ticket.
56
57  checksum specifies the checksum to be used in the authenticator.
58
59  The outbuf buffer storage is allocated, and should be freed by the
60  caller when finished.
61
62  On an error return, the credentials pointed to by creds might have been
63  augmented with additional fields from the obtained credentials; the entire
64  credentials should be released by calling krb5_free_creds().
65
66  returns system errors
67 */
68
69 static krb5_error_code 
70 krb5_generate_authenticator PROTOTYPE((krb5_context,
71                                        krb5_authenticator *, krb5_principal,
72                                        const krb5_checksum *, krb5_keyblock *,
73                                        krb5_int32, krb5_authdata ** ));
74
75 krb5_error_code
76 krb5_mk_req_extended(context, ap_req_options, checksum, kdc_options,
77                      sequence, newkey, ccache, creds, authentp, outbuf)
78     krb5_context context;
79     const krb5_flags ap_req_options;
80     const krb5_checksum *checksum;
81     const krb5_flags kdc_options;
82     krb5_int32 sequence;
83     krb5_keyblock **newkey;
84     krb5_ccache ccache;
85     krb5_creds *creds;
86     krb5_authenticator *authentp;
87     krb5_data *outbuf;
88 {
89     krb5_error_code retval;
90     krb5_ap_req request;
91     krb5_authenticator authent;
92     krb5_data *scratch;
93     krb5_enctype etype;
94     krb5_encrypt_block eblock;
95     krb5_data *toutbuf;
96     int cleanup_key = 0;
97
98     request.ticket = 0;
99     request.authenticator.ciphertext.data = 0;
100     if (newkey)
101         *newkey = 0;
102     scratch = 0;
103     
104     if ((ap_req_options & AP_OPTS_USE_SESSION_KEY) &&
105         !creds->ticket.length)
106         return(KRB5_NO_TKT_SUPPLIED);
107
108     if (!creds->ticket.length) {
109         /* go get creds */
110         if (retval = krb5_get_credentials(context, kdc_options,
111                                           ccache,
112                                           creds))
113             return(retval);
114     }
115
116     /* we need a native ticket */
117     if (retval = decode_krb5_ticket(&creds->ticket, &request.ticket))
118         return(retval);
119     
120     /* verify a valid etype is available */
121     etype = request.ticket->enc_part.etype;
122
123     if (!valid_etype(etype)) {
124         retval = KRB5_PROG_ETYPE_NOSUPP;
125         goto cleanup;
126     }
127
128     request.ap_options = ap_req_options;
129     if (newkey) {
130         if (retval = krb5_generate_subkey(context, &creds->keyblock, newkey))
131             goto cleanup;
132     }
133
134     if (retval = krb5_generate_authenticator(context, &authent, creds->client, checksum,
135                                              newkey ? *newkey : 0,
136                                              sequence, creds->authdata))
137         goto cleanup;
138         
139     /* encode the authenticator */
140     retval = encode_krb5_authenticator(&authent, &scratch);
141     if (retval)
142         goto cleanup;
143     
144     /* Null out these fields, to prevent pointer sharing problems;
145      * they were supplied by the caller
146      */
147     authent.client = NULL;
148     authent.checksum = NULL;
149     authent.authorization_data = NULL;
150     if (authentp)
151             *authentp = authent;
152     else
153             krb5_free_authenticator_contents(context, &authent);
154
155     /* put together an eblock for this encryption */
156
157     krb5_use_cstype(context, &eblock, etype);
158     request.authenticator.etype = etype;
159     request.authenticator.kvno = 0;
160     request.authenticator.ciphertext.length =
161         krb5_encrypt_size(scratch->length, eblock.crypto_entry);
162     /* add padding area, and zero it */
163     if (!(scratch->data = realloc(scratch->data,
164                                   request.authenticator.ciphertext.length))) {
165         /* may destroy scratch->data */
166         retval = ENOMEM;
167         goto cleanup;
168     }
169     memset(scratch->data + scratch->length, 0,
170           request.authenticator.ciphertext.length - scratch->length);
171     if (!(request.authenticator.ciphertext.data =
172           malloc(request.authenticator.ciphertext.length))) {
173         retval = ENOMEM;
174         goto cleanup;
175     }
176
177     /* do any necessary key pre-processing */
178     if (retval = krb5_process_key(context, &eblock, &creds->keyblock))
179         goto cleanup;
180
181     cleanup_key++;
182
183     /* call the encryption routine */
184     if (retval = krb5_encrypt(context, (krb5_pointer) scratch->data,
185                               (krb5_pointer) request.authenticator.ciphertext.data,
186                               scratch->length, &eblock, 0))
187         goto cleanup;
188
189     if (retval = krb5_finish_key(context, &eblock))
190         goto cleanup;
191     cleanup_key = 0;
192     
193     retval = encode_krb5_ap_req(&request, &toutbuf);
194     if (retval)
195         goto cleanup;
196     
197     *outbuf = *toutbuf;
198     krb5_xfree(toutbuf);
199
200 cleanup:
201     if (request.ticket)
202         krb5_free_ticket(context, request.ticket);
203     if (request.authenticator.ciphertext.data) {
204         (void) memset(request.authenticator.ciphertext.data, 0,
205                       request.authenticator.ciphertext.length);
206         free(request.authenticator.ciphertext.data);
207     }
208     if (retval && newkey && *newkey)
209         krb5_free_keyblock(context, *newkey);
210     if (scratch) {
211         memset(scratch->data, 0, scratch->length);
212         krb5_xfree(scratch->data);
213         krb5_xfree(scratch);
214     }
215     if (cleanup_key)
216         krb5_finish_key(context, &eblock);
217
218     return retval;
219 }
220
221 static krb5_error_code
222 krb5_generate_authenticator(context, authent, client, cksum, key, seq_number, authorization)
223     krb5_context context;
224     krb5_authenticator *authent;
225     krb5_principal client;
226     const krb5_checksum *cksum;
227     krb5_keyblock *key;
228     krb5_int32 seq_number;
229     krb5_authdata **authorization;
230 {
231     krb5_error_code retval;
232     
233     authent->client = client;
234     authent->checksum = (krb5_checksum *)cksum;
235     if (key) {
236         retval = krb5_copy_keyblock(context, key, &authent->subkey);
237         if (retval)
238             return retval;
239     } else
240         authent->subkey = 0;
241     authent->subkey = key;
242     authent->seq_number = seq_number;
243     authent->authorization_data = authorization;
244
245     return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec));
246 }