encode_kdc.c (krb5_encode_kdc_rep): Now requires that the
[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_authenticator *, krb5_principal,
71                                        const krb5_checksum *, krb5_keyblock *,
72                                        krb5_int32, krb5_authdata ** ));
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     if (creds->keyblock.etype == ETYPE_UNKNOWN)
111         etype = krb5_keytype_array[creds->keyblock.keytype]->system->proto_enctype;
112     else
113         etype = creds->keyblock.etype;
114
115     if (!valid_etype(etype))
116         return KRB5_PROG_ETYPE_NOSUPP;
117
118     request.ap_options = ap_req_options;
119     /* we need a native ticket */
120     if (retval = decode_krb5_ticket(&creds->ticket, &request.ticket))
121         return(retval);
122
123 #define cleanup_ticket() krb5_free_ticket(request.ticket)
124     if (newkey) {
125         if (retval = krb5_generate_subkey(&creds->keyblock, newkey)) {
126             cleanup_ticket();
127             return retval;
128         }
129     }
130 #define cleanup_key() {if (newkey) krb5_free_keyblock(*newkey);}
131     if (retval = krb5_generate_authenticator(&authent, creds->client, checksum,
132                                              newkey ? *newkey : 0,
133                                              sequence, creds->authdata)) {
134         cleanup_key();
135         cleanup_ticket();
136         return retval;
137     }
138     
139     /* encode the authenticator */
140     retval = encode_krb5_authenticator(&authent, &scratch);
141     if (retval) {
142         cleanup_key();
143         cleanup_ticket();
144         return(retval);
145     }
146     
147     /* Null out these fields, to prevent pointer sharing problems;
148      * they were supplied by the caller
149      */
150     authent.client = NULL;
151     authent.checksum = NULL;
152     authent.authorization_data = NULL;
153     if (authentp)
154             *authentp = authent;
155     else
156             krb5_free_authenticator_contents(&authent);
157
158 #define cleanup_scratch() { (void) memset(scratch->data, 0, scratch->length); \
159 krb5_free_data(scratch); }
160
161     /* put together an eblock for this encryption */
162
163     krb5_use_cstype(&eblock, etype);
164     request.authenticator.etype = etype;
165     request.authenticator.kvno = 0; /* XXX user set? */
166     request.authenticator.ciphertext.length =
167         krb5_encrypt_size(scratch->length, eblock.crypto_entry);
168     /* add padding area, and zero it */
169     if (!(scratch->data = realloc(scratch->data,
170                                   request.authenticator.ciphertext.length))) {
171         /* may destroy scratch->data */
172         krb5_xfree(scratch);
173         retval = ENOMEM;
174         goto clean_ticket;
175     }
176     memset(scratch->data + scratch->length, 0,
177           request.authenticator.ciphertext.length - scratch->length);
178     if (!(request.authenticator.ciphertext.data =
179           malloc(request.authenticator.ciphertext.length))) {
180         retval = ENOMEM;
181         goto clean_scratch;
182     }
183
184 #define cleanup_encpart() {\
185 (void) memset(request.authenticator.ciphertext.data, 0,\
186              request.authenticator.ciphertext.length); \
187 free(request.authenticator.ciphertext.data); \
188 request.authenticator.ciphertext.length = 0; \
189 request.authenticator.ciphertext.data = 0;}
190
191     /* do any necessary key pre-processing */
192     if (retval = krb5_process_key(&eblock, &creds->keyblock)) {
193         goto clean_encpart;
194     }
195
196 #define cleanup_prockey() {(void) krb5_finish_key(&eblock);}
197
198     /* call the encryption routine */
199     if (retval = krb5_encrypt((krb5_pointer) scratch->data,
200                               (krb5_pointer) request.authenticator.ciphertext.data,
201                               scratch->length, &eblock, 0)) {
202         goto clean_prockey;
203     }
204
205     /* authenticator now assembled-- do some cleanup */
206     cleanup_scratch();
207
208     if (retval = krb5_finish_key(&eblock)) {
209         cleanup_encpart();
210         return retval;
211     }
212
213     if (!(retval = encode_krb5_ap_req(&request, &toutbuf))) {
214         *outbuf = *toutbuf;
215         krb5_xfree(toutbuf);
216     }
217     cleanup_ticket();
218     cleanup_encpart();
219     return retval;
220
221  clean_prockey:
222     cleanup_prockey();
223  clean_encpart:
224     cleanup_encpart();
225  clean_scratch:
226     cleanup_scratch();
227  clean_ticket:
228     cleanup_key();
229     cleanup_ticket();
230
231     return retval;
232 }
233
234 static krb5_error_code
235 krb5_generate_authenticator(authent, client, cksum, key, seq_number, authorization)
236 krb5_authenticator *authent;
237 krb5_principal client;
238 const krb5_checksum *cksum;
239 krb5_keyblock *key;
240 krb5_int32 seq_number;
241 krb5_authdata **authorization;
242 {
243     krb5_error_code retval;
244     
245     authent->client = client;
246     authent->checksum = (krb5_checksum *)cksum;
247     if (key) {
248         retval = krb5_copy_keyblock(key, &authent->subkey);
249         if (retval)
250             return retval;
251     } else
252         authent->subkey = 0;
253     authent->subkey = key;
254     authent->seq_number = seq_number;
255     authent->authorization_data = authorization;
256
257     return(krb5_us_timeofday(&authent->ctime, &authent->cusec));
258 }