pull up 3des implementation from the marc-3des branch
[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_DLLIMP krb5_error_code KRB5_CALLCONV
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   FAR * auth_context;
75     const krb5_flags      ap_req_options;
76     krb5_data           FAR * in_data;
77     krb5_creds          FAR * in_creds;
78     krb5_data           FAR * 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_data *toutbuf;
88
89     request.ap_options = ap_req_options & AP_OPTS_WIRE_MASK;
90     request.authenticator.ciphertext.data = 0;
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 that the ticket is not expired */
101     if ((retval = krb5_validate_times(context, &in_creds->times)) != 0)
102         goto cleanup;
103
104     /* generate auth_context if needed */
105     if (*auth_context == NULL) {
106         if ((retval = krb5_auth_con_init(context, &new_auth_context)))
107             goto cleanup;
108         *auth_context = new_auth_context;
109     }
110
111     /* set auth context keyblock */
112     if ((retval = krb5_copy_keyblock(context, &in_creds->keyblock, 
113                                      &((*auth_context)->keyblock))))
114         goto cleanup;
115
116     /* generate seq number if needed */
117     if ((((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
118      || ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
119       && ((*auth_context)->local_seq_number == 0)) 
120         if ((retval = krb5_generate_seq_number(context, &in_creds->keyblock,
121                                      &(*auth_context)->local_seq_number)))
122             goto cleanup;
123         
124
125     /* generate subkey if needed */
126     if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->local_subkey))
127         if ((retval = krb5_generate_subkey(context, &(in_creds)->keyblock, 
128                                            &(*auth_context)->local_subkey)))
129             goto cleanup;
130
131     if (in_data) {
132         if ((*auth_context)->req_cksumtype == 0x8003) {
133             /* XXX Special hack for GSSAPI */
134             checksum.checksum_type = 0x8003;
135             checksum.length = in_data->length;
136             checksum.contents = (krb5_octet *) in_data->data;
137         } else {
138             if ((retval = krb5_c_make_checksum(context, 
139                                                (*auth_context)->req_cksumtype,
140                                                (*auth_context)->keyblock,
141                                                KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM,
142                                                in_data, &checksum)))
143                 goto cleanup_cksum;
144         }
145         checksump = &checksum;
146     }
147
148     /* Generate authenticator */
149     if (((*auth_context)->authentp = (krb5_authenticator *)malloc(sizeof(
150                                         krb5_authenticator))) == NULL) {
151         retval = ENOMEM;
152         goto cleanup_cksum;
153     }
154
155     if ((retval = krb5_generate_authenticator(context,
156                                               (*auth_context)->authentp,
157                                               (in_creds)->client, checksump,
158                                               (*auth_context)->local_subkey,
159                                               (*auth_context)->local_seq_number,
160                                               (in_creds)->authdata)))
161         goto cleanup_cksum;
162         
163     /* encode the authenticator */
164     if ((retval = encode_krb5_authenticator((*auth_context)->authentp,
165                                             &scratch)))
166         goto cleanup_cksum;
167     
168     /* Null out these fields, to prevent pointer sharing problems;
169      * they were supplied by the caller
170      */
171     (*auth_context)->authentp->client = NULL;
172     (*auth_context)->authentp->checksum = NULL;
173     (*auth_context)->authentp->authorization_data = NULL;
174
175     /* call the encryption routine */
176     if ((retval = krb5_encrypt_helper(context, &in_creds->keyblock,
177                                       KRB5_KEYUSAGE_AP_REQ_AUTH,
178                                       scratch, &request.authenticator)))
179         goto cleanup_cksum;
180
181     if ((retval = encode_krb5_ap_req(&request, &toutbuf)))
182         goto cleanup_cksum;
183 #ifdef HAVE_C_STRUCTURE_ASSIGNMENT
184     *outbuf = *toutbuf;
185 #else
186     memcpy(outbuf, toutbuf, sizeof(krb5_data));
187 #endif
188
189     krb5_xfree(toutbuf);
190
191 cleanup_cksum:
192     if (checksump && checksump->checksum_type != 0x8003)
193       free(checksump->contents);
194
195 cleanup:
196     if (request.ticket)
197         krb5_free_ticket(context, request.ticket);
198     if (request.authenticator.ciphertext.data) {
199         (void) memset(request.authenticator.ciphertext.data, 0,
200                       request.authenticator.ciphertext.length);
201         free(request.authenticator.ciphertext.data);
202     }
203     if (scratch) {
204         memset(scratch->data, 0, scratch->length);
205         krb5_xfree(scratch->data);
206         krb5_xfree(scratch);
207     }
208     return retval;
209 }
210
211 static krb5_error_code
212 krb5_generate_authenticator(context, authent, client, cksum, key, seq_number, authorization)
213     krb5_context context;
214     krb5_authenticator *authent;
215     krb5_principal client;
216     const krb5_checksum *cksum;
217     krb5_keyblock *key;
218     krb5_int32 seq_number;
219     krb5_authdata **authorization;
220 {
221     krb5_error_code retval;
222     
223     authent->client = client;
224     authent->checksum = (krb5_checksum *)cksum;
225     if (key) {
226         retval = krb5_copy_keyblock(context, key, &authent->subkey);
227         if (retval)
228             return retval;
229     } else
230         authent->subkey = 0;
231     authent->seq_number = seq_number;
232     authent->authorization_data = authorization;
233
234     return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec));
235 }