* auth_con.h auth_con.c Added for krb5_auth_con definition and
[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_auth_context   * new_auth_context;
83
84     krb5_ap_req request;
85     krb5_data *scratch = 0;
86     krb5_encrypt_block eblock;
87     krb5_data *toutbuf;
88
89     request.authenticator.ciphertext.data = 0;
90     request.ap_options = ap_req_options;
91     request.ticket = 0;
92     
93     if (!in_creds->ticket.length) 
94         return(KRB5_NO_TKT_SUPPLIED);
95
96     /* we need a native ticket */
97     if (retval = decode_krb5_ticket(&(in_creds)->ticket, &request.ticket))
98         return(retval);
99     
100     /* verify a valid etype is available */
101     if (!valid_etype(request.ticket->enc_part.etype)) {
102         retval = KRB5_PROG_ETYPE_NOSUPP;
103         goto cleanup;
104     }
105
106     /* generate auth_context if needed */
107     if (*auth_context == NULL) {
108         if (retval = krb5_auth_con_init(context, &new_auth_context))
109             goto cleanup;
110         *auth_context = new_auth_context;
111     }
112
113     /* set auth context keyblock */
114     if (retval = krb5_copy_keyblock(context, &in_creds->keyblock, 
115                                     &((*auth_context)->keyblock))) 
116         goto cleanup;
117
118     /* generate seq number if needed */
119     if ((((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
120      || ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
121       && ((*auth_context)->local_seq_number == 0)) 
122         if (retval = krb5_generate_seq_number(context, &in_creds->keyblock,
123                                      &(*auth_context)->local_seq_number))
124             goto cleanup;
125         
126
127     /* generate subkey if needed */
128     if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->local_subkey))
129         if (retval = krb5_generate_subkey(context, &(in_creds)->keyblock, 
130                                           &(*auth_context)->local_subkey))
131             goto cleanup;
132
133
134     /* Generate checksum, XXX What should the seed be? */
135     if ((checksum.contents = (krb5_octet *)malloc(krb5_checksum_size(
136                                           context, CKSUMTYPE_CRC32))) == NULL) {
137         retval = ENOMEM;
138         goto cleanup;
139     }
140
141     if (in_data == NULL) {
142         if (retval = krb5_calculate_checksum(context, 
143                                              (*auth_context)->cksumtype, 
144                                              0, 0, 0, 0, &checksum))
145             goto cleanup_cksum;
146     } else 
147         if ((*auth_context)->cksumtype == 0x8003) {
148             /* XXX Special hack for GSSAPI */
149             checksum.checksum_type = 0x8003;
150             checksum.length = in_data->length;
151             checksum.contents = in_data->data;
152         } else 
153             if (retval = krb5_calculate_checksum(context, 
154                                                  (*auth_context)->cksumtype, 
155                                                  in_data->data, in_data->length,
156                                                  0, 0, &checksum))
157                 goto cleanup_cksum;
158
159     /* Generate authenticator */
160     if (((*auth_context)->authentp = (krb5_authenticator *)malloc(sizeof(
161                                         krb5_authenticator))) == NULL) {
162         retval = ENOMEM;
163         goto cleanup_cksum;
164     }
165
166     if (retval = krb5_generate_authenticator(context, (*auth_context)->authentp,
167                                              (in_creds)->client, &checksum,
168                                              (*auth_context)->local_subkey,
169                                              (*auth_context)->local_seq_number,
170                                              (in_creds)->authdata))
171         goto cleanup_cksum;
172         
173     /* encode the authenticator */
174     if (retval = encode_krb5_authenticator((*auth_context)->authentp, &scratch))
175         goto cleanup_cksum;
176     
177     /* Null out these fields, to prevent pointer sharing problems;
178      * they were supplied by the caller
179      */
180     (*auth_context)->authentp->client = NULL;
181     (*auth_context)->authentp->checksum = NULL;
182     (*auth_context)->authentp->authorization_data = NULL;
183
184     /* put together an eblock for this encryption */
185
186     krb5_use_cstype(context, &eblock, request.ticket->enc_part.etype);
187     request.authenticator.etype = request.ticket->enc_part.etype;
188     request.authenticator.kvno = 0;
189     request.authenticator.ciphertext.length =
190         krb5_encrypt_size(scratch->length, eblock.crypto_entry);
191     /* add padding area, and zero it */
192     if (!(scratch->data = realloc(scratch->data,
193                                   request.authenticator.ciphertext.length))) {
194         /* may destroy scratch->data */
195         retval = ENOMEM;
196         goto cleanup_cksum;
197     }
198     memset(scratch->data + scratch->length, 0,
199           request.authenticator.ciphertext.length - scratch->length);
200     if (!(request.authenticator.ciphertext.data =
201           malloc(request.authenticator.ciphertext.length))) {
202         retval = ENOMEM;
203         goto cleanup_cksum;
204     }
205
206     /* do any necessary key pre-processing */
207     if (retval = krb5_process_key(context, &eblock, &(in_creds)->keyblock))
208         goto cleanup;
209
210     /* call the encryption routine */
211     if (retval = krb5_encrypt(context, (krb5_pointer) scratch->data,
212                               (krb5_pointer) request.authenticator.ciphertext.data,
213                               scratch->length, &eblock, 0)) {
214         krb5_finish_key(context, &eblock);
215         goto cleanup_cksum;
216     }
217
218     if (retval = krb5_finish_key(context, &eblock))
219         goto cleanup_cksum;
220     
221     if (retval = encode_krb5_ap_req(&request, &toutbuf))
222         goto cleanup_cksum;
223     
224     *outbuf = *toutbuf;
225     krb5_xfree(toutbuf);
226
227 cleanup_cksum:
228     free(checksum.contents);
229
230 cleanup:
231     if (request.ticket)
232         krb5_free_ticket(context, request.ticket);
233     if (request.authenticator.ciphertext.data) {
234         (void) memset(request.authenticator.ciphertext.data, 0,
235                       request.authenticator.ciphertext.length);
236         free(request.authenticator.ciphertext.data);
237     }
238     if (scratch) {
239         memset(scratch->data, 0, scratch->length);
240         krb5_xfree(scratch->data);
241         krb5_xfree(scratch);
242     }
243     return retval;
244 }
245
246 static krb5_error_code
247 krb5_generate_authenticator(context, authent, client, cksum, key, seq_number, authorization)
248     krb5_context context;
249     krb5_authenticator *authent;
250     krb5_principal client;
251     const krb5_checksum *cksum;
252     krb5_keyblock *key;
253     krb5_int32 seq_number;
254     krb5_authdata **authorization;
255 {
256     krb5_error_code retval;
257     
258     authent->client = client;
259     authent->checksum = (krb5_checksum *)cksum;
260     if (key) {
261         retval = krb5_copy_keyblock(context, key, &authent->subkey);
262         if (retval)
263             return retval;
264     } else
265         authent->subkey = 0;
266     authent->subkey = key;
267     authent->seq_number = seq_number;
268     authent->authorization_data = authorization;
269
270     return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec));
271 }