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