rd_req_dec.c (krb5_rd_req_decoded): Move code which validated 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 "k5-int.h"
29 #include "auth_con.h"
30
31 /*
32  Formats a KRB_AP_REQ message into outbuf, with more complete options than
33  krb_mk_req.
34
35  outbuf, ap_req_options, checksum, and ccache are used in the
36  same fashion as for krb5_mk_req.
37
38  creds is used to supply the credentials (ticket and session key) needed
39  to form the request.
40
41  if creds->ticket has no data (length == 0), then a ticket is obtained
42  from either the cache or the TGS, passing creds to krb5_get_credentials().
43  kdc_options specifies the options requested for the ticket to be used.
44  If a ticket with appropriate flags is not found in the cache, then these
45  options are passed on in a request to an appropriate KDC.
46
47  ap_req_options specifies the KRB_AP_REQ options desired.
48
49  if ap_req_options specifies AP_OPTS_USE_SESSION_KEY, then creds->ticket
50  must contain the appropriate ENC-TKT-IN-SKEY ticket.
51
52  checksum specifies the checksum to be used in the authenticator.
53
54  The outbuf buffer storage is allocated, and should be freed by the
55  caller when finished.
56
57  On an error return, the credentials pointed to by creds might have been
58  augmented with additional fields from the obtained credentials; the entire
59  credentials should be released by calling krb5_free_creds().
60
61  returns system errors
62 */
63
64 static krb5_error_code 
65 krb5_generate_authenticator PROTOTYPE((krb5_context,
66                                        krb5_authenticator *, krb5_principal,
67                                        const krb5_checksum *, krb5_keyblock *,
68                                        krb5_int32, krb5_authdata ** ));
69
70 krb5_error_code INTERFACE
71 krb5_mk_req_extended(context, auth_context, ap_req_options, in_data, in_creds,
72                      outbuf)
73     krb5_context          context;
74     krb5_auth_context   * auth_context;
75     const krb5_flags      ap_req_options;
76     krb5_data           * in_data;
77     krb5_creds          * in_creds;
78     krb5_data           * outbuf;
79 {
80     krb5_error_code       retval;
81     krb5_checksum         checksum;
82     krb5_checksum         *checksump = 0;
83     krb5_auth_context     new_auth_context;
84
85     krb5_ap_req request;
86     krb5_data *scratch = 0;
87     krb5_encrypt_block eblock;
88     krb5_data *toutbuf;
89
90     request.ap_options = ap_req_options & AP_OPTS_WIRE_MASK;
91     request.authenticator.ciphertext.data = 0;
92     request.ticket = 0;
93     
94     if (!in_creds->ticket.length) 
95         return(KRB5_NO_TKT_SUPPLIED);
96
97     /* we need a native ticket */
98     if ((retval = decode_krb5_ticket(&(in_creds)->ticket, &request.ticket)))
99         return(retval);
100     
101     /* verify a valid enctype is available */
102     if (!valid_enctype(request.ticket->enc_part.enctype)) {
103         retval = KRB5_PROG_ETYPE_NOSUPP;
104         goto cleanup;
105     }
106
107     /* verify that the ticket is not expired */
108     if ((retval = krb5_validate_times(context, &in_creds->times)) != 0)
109         goto cleanup;
110
111     /* generate auth_context if needed */
112     if (*auth_context == NULL) {
113         if ((retval = krb5_auth_con_init(context, &new_auth_context)))
114             goto cleanup;
115         *auth_context = new_auth_context;
116     }
117
118     /* set auth context keyblock */
119     if ((retval = krb5_copy_keyblock(context, &in_creds->keyblock, 
120                                      &((*auth_context)->keyblock))))
121         goto cleanup;
122
123     /* generate seq number if needed */
124     if ((((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
125      || ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
126       && ((*auth_context)->local_seq_number == 0)) 
127         if ((retval = krb5_generate_seq_number(context, &in_creds->keyblock,
128                                      &(*auth_context)->local_seq_number)))
129             goto cleanup;
130         
131
132     /* generate subkey if needed */
133     if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->local_subkey))
134         if ((retval = krb5_generate_subkey(context, &(in_creds)->keyblock, 
135                                            &(*auth_context)->local_subkey)))
136             goto cleanup;
137
138
139     if (in_data) {
140       if ((*auth_context)->cksumtype == 0x8003) {
141         /* XXX Special hack for GSSAPI */
142         checksum.checksum_type = 0x8003;
143         checksum.length = in_data->length;
144         checksum.contents = (krb5_octet *) in_data->data;
145       } else  {
146         /* Generate checksum, XXX What should the seed be? */
147         if ((checksum.contents = (krb5_octet *)malloc(krb5_checksum_size(context,
148                                  (*auth_context)->cksumtype))) == NULL) {
149           retval = ENOMEM;
150           goto cleanup;
151         }
152         if ((retval = krb5_calculate_checksum(context, 
153                                               (*auth_context)->cksumtype, 
154                                               in_data->data, in_data->length,
155                                               (*auth_context)->keyblock->contents,
156                                               (*auth_context)->keyblock->length,
157                                               &checksum)))
158           goto cleanup_cksum;
159       }
160       checksump = &checksum;
161     }
162
163     /* Generate authenticator */
164     if (((*auth_context)->authentp = (krb5_authenticator *)malloc(sizeof(
165                                         krb5_authenticator))) == NULL) {
166         retval = ENOMEM;
167         goto cleanup_cksum;
168     }
169
170     if ((retval = krb5_generate_authenticator(context,
171                                               (*auth_context)->authentp,
172                                               (in_creds)->client, checksump,
173                                               (*auth_context)->local_subkey,
174                                               (*auth_context)->local_seq_number,
175                                               (in_creds)->authdata)))
176         goto cleanup_cksum;
177         
178     /* encode the authenticator */
179     if ((retval = encode_krb5_authenticator((*auth_context)->authentp,
180                                             &scratch)))
181         goto cleanup_cksum;
182     
183     /* Null out these fields, to prevent pointer sharing problems;
184      * they were supplied by the caller
185      */
186     (*auth_context)->authentp->client = NULL;
187     (*auth_context)->authentp->checksum = NULL;
188     (*auth_context)->authentp->authorization_data = NULL;
189
190     /* put together an eblock for this encryption */
191
192     krb5_use_enctype(context, &eblock, in_creds->keyblock.enctype);
193     request.authenticator.enctype = in_creds->keyblock.enctype;
194     request.authenticator.kvno = 0;
195     request.authenticator.ciphertext.length =
196         krb5_encrypt_size(scratch->length, eblock.crypto_entry);
197     /* add padding area, and zero it */
198     if (!(scratch->data = realloc(scratch->data,
199                                   request.authenticator.ciphertext.length))) {
200         /* may destroy scratch->data */
201         retval = ENOMEM;
202         goto cleanup_cksum;
203     }
204     memset(scratch->data + scratch->length, 0,
205           request.authenticator.ciphertext.length - scratch->length);
206     if (!(request.authenticator.ciphertext.data =
207           malloc(request.authenticator.ciphertext.length))) {
208         retval = ENOMEM;
209         goto cleanup_cksum;
210     }
211
212     /* do any necessary key pre-processing */
213     if ((retval = krb5_process_key(context, &eblock, &(in_creds)->keyblock)))
214         goto cleanup;
215
216     /* call the encryption routine */
217     if ((retval = krb5_encrypt(context, (krb5_pointer) scratch->data,
218                                (krb5_pointer) request.authenticator.ciphertext.data,
219                                scratch->length, &eblock, 0))) {
220         krb5_finish_key(context, &eblock);
221         goto cleanup_cksum;
222     }
223
224     if ((retval = krb5_finish_key(context, &eblock)))
225         goto cleanup_cksum;
226     
227     if ((retval = encode_krb5_ap_req(&request, &toutbuf)))
228         goto cleanup_cksum;
229 #ifdef HAVE_C_STRUCTURE_ASSIGNMENT
230     *outbuf = *toutbuf;
231 #else
232     memcpy(outbuf, toutbuf, sizeof(krb5_data));
233 #endif
234
235     krb5_xfree(toutbuf);
236
237 cleanup_cksum:
238     if (checksump && checksump->checksum_type != 0x8003)
239       free(checksump->contents);
240
241 cleanup:
242     if (request.ticket)
243         krb5_free_ticket(context, request.ticket);
244     if (request.authenticator.ciphertext.data) {
245         (void) memset(request.authenticator.ciphertext.data, 0,
246                       request.authenticator.ciphertext.length);
247         free(request.authenticator.ciphertext.data);
248     }
249     if (scratch) {
250         memset(scratch->data, 0, scratch->length);
251         krb5_xfree(scratch->data);
252         krb5_xfree(scratch);
253     }
254     return retval;
255 }
256
257 static krb5_error_code
258 krb5_generate_authenticator(context, authent, client, cksum, key, seq_number, authorization)
259     krb5_context context;
260     krb5_authenticator *authent;
261     krb5_principal client;
262     const krb5_checksum *cksum;
263     krb5_keyblock *key;
264     krb5_int32 seq_number;
265     krb5_authdata **authorization;
266 {
267     krb5_error_code retval;
268     
269     authent->client = client;
270     authent->checksum = (krb5_checksum *)cksum;
271     if (key) {
272         retval = krb5_copy_keyblock(context, key, &authent->subkey);
273         if (retval)
274             return retval;
275     } else
276         authent->subkey = 0;
277     authent->seq_number = seq_number;
278     authent->authorization_data = authorization;
279
280     return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec));
281 }