change bzero to memset
[krb5.git] / src / lib / krb5 / krb / get_in_tkt.c
1 /*
2  * $Source$
3  * $Author$
4  *
5  * Copyright 1990 by the Massachusetts Institute of Technology.
6  *
7  * For copying and distribution information, please see the file
8  * <krb5/copyright.h>.
9  *
10  * krb5_get_in_tkt()
11  */
12
13 #if !defined(lint) && !defined(SABER)
14 static char rcsid_get_in_tkt_c[] =
15 "$Id$";
16 #endif  /* !lint & !SABER */
17
18 #include <krb5/copyright.h>
19 #include <krb5/krb5.h>
20 #include <krb5/asn1.h>
21 #include <krb5/libos-proto.h>
22 #include <krb5/ext-proto.h>
23
24 /*
25  All-purpose initial ticket routine, usually called via
26  krb5_get_in_tkt_with_password or krb5_get_in_tkt_with_skey.
27
28  Attempts to get an initial ticket for creds->client to use server
29  creds->server, (realm is taken from creds->client), with options
30  options, requesting encryption type etype, and using
31  creds->times.starttime,  creds->times.endtime,  creds->times.renew_till
32  as from, till, and rtime.  creds->times.renew_till is ignored unless
33  the RENEWABLE option is requested.
34
35  key_proc is called to fill in the key to be used for decryption.
36  keyseed is passed on to key_proc.
37
38  decrypt_proc is called to perform the decryption of the response (the
39  encrypted part is in dec_rep->enc_part; the decrypted part should be
40  allocated and filled into dec_rep->enc_part2
41  arg is passed on to decrypt_proc.
42
43  If addrs is non-NULL, it is used for the addresses requested.  If it is
44  null, the system standard addresses are used.
45
46  A succesful call will place the ticket in the credentials cache ccache
47  and fill in creds with the ticket information used/returned..
48
49  returns system errors, encryption errors
50
51  */
52
53
54 extern krb5_deltat krb5_clockskew;
55 #define in_clock_skew(date) (abs((date)-request.ctime) < krb5_clockskew)
56
57 /* some typedef's for the function args to make things look a bit cleaner */
58
59 typedef krb5_error_code (*git_key_proc) PROTOTYPE((const krb5_keytype,
60                                                    krb5_keyblock **,
61                                                    krb5_const_pointer ));
62 typedef krb5_error_code (*git_decrypt_proc) PROTOTYPE((const krb5_keyblock *,
63                                                        krb5_const_pointer,
64                                                        krb5_kdc_rep * ));
65 krb5_error_code
66 krb5_get_in_tkt(DECLARG(const krb5_flags, options),
67                 DECLARG(krb5_address * const *, addrs),
68                 DECLARG(const krb5_enctype, etype),
69                 DECLARG(const krb5_keytype, keytype),
70                 DECLARG(git_key_proc, key_proc),
71                 DECLARG(krb5_const_pointer, keyseed),
72                 DECLARG(git_decrypt_proc, decrypt_proc),
73                 DECLARG(krb5_const_pointer, decryptarg),
74                 DECLARG(krb5_creds *, creds),
75                 DECLARG(krb5_ccache, ccache))
76 OLDDECLARG(const krb5_flags, options)
77 OLDDECLARG(krb5_address * const *, addrs)
78 OLDDECLARG(const krb5_enctype, etype)
79 OLDDECLARG(const krb5_keytype, keytype)
80 OLDDECLARG(git_key_proc, key_proc)
81 OLDDECLARG(krb5_const_pointer, keyseed)
82 OLDDECLARG(git_decrypt_proc, decrypt_proc)
83 OLDDECLARG(krb5_const_pointer, decryptarg)
84 OLDDECLARG(krb5_creds *, creds)
85 OLDDECLARG(krb5_ccache, ccache)
86 {
87     krb5_kdc_req request;
88     krb5_kdc_rep *as_reply;
89     krb5_error *err_reply;
90     krb5_error_code retval;
91     krb5_data *packet;
92     krb5_data reply;
93     krb5_keyblock *decrypt_key;
94
95     request.msg_type = KRB5_AS_REQ;
96
97     /* AS_REQ has no pre-authentication. */
98     request.padata_type = 0;
99     request.padata.data = 0;
100     request.padata.length = 0;
101
102     request.kdc_options = options;
103     request.client = creds->client;
104     request.server = creds->server;
105
106     request.from = creds->times.starttime;
107     request.till = creds->times.endtime;
108     request.rtime = creds->times.renew_till;
109     if (retval = krb5_timeofday(&request.ctime))
110         return(retval);
111     /* XXX we know they are the same size... */
112     request.nonce = (krb5_int32) request.ctime;
113     request.etype = etype;
114     request.addresses = (krb5_address **) addrs;
115     request.second_ticket = 0;
116     request.authorization_data = 0;
117
118     /* encode & send to KDC */
119     if (retval = encode_krb5_as_req(&request, &packet))
120         return(retval);
121     retval = krb5_sendto_kdc(packet, krb5_princ_realm(creds->client), &reply);
122     krb5_free_data(packet);
123     if (retval)
124         return(retval);
125
126     /* now decode the reply...could be error or as_rep */
127
128     if (!krb5_is_as_rep(&reply) && !krb5_is_krb_error(&reply))
129             return KRB5KRB_AP_ERR_MSG_TYPE;
130     if (retval = decode_krb5_as_rep(&reply, &as_reply)) {
131         if (decode_krb5_error(&reply, &err_reply))
132             return retval;              /* some other reply--??? */
133         /* it was an error */
134
135         if ((err_reply->ctime != request.ctime) ||
136             !krb5_principal_compare(err_reply->server, request.server) ||
137             !krb5_principal_compare(err_reply->client, request.client))
138             retval = KRB5_KDCREP_MODIFIED;
139         else
140             retval = err_reply->error + ERROR_TABLE_BASE_krb5;
141
142         /* XXX somehow make error msg text available to application? */
143
144         krb5_free_error(err_reply);
145         return retval;
146     }
147
148     /* it was a kdc_rep--decrypt & check */
149
150     /* generate the key */
151     if (retval = (*key_proc)(keytype, &decrypt_key, keyseed)) {
152         krb5_free_kdc_rep(as_reply);
153         return retval;
154     }
155     
156     retval = (*decrypt_proc)(decrypt_key, decryptarg, as_reply);
157     memset((char *)decrypt_key->contents, 0, decrypt_key->length);
158     krb5_free_keyblock(decrypt_key);
159     if (retval) {
160         krb5_free_kdc_rep(as_reply);
161         return retval;
162     }
163
164     /* check the contents for sanity: */
165     if (!krb5_principal_compare(as_reply->client, request.client)
166         || !krb5_principal_compare(as_reply->enc_part2->server, request.server)
167         || !krb5_principal_compare(as_reply->ticket->server, request.server)
168         || (request.nonce != as_reply->enc_part2->nonce)
169         /* XXX check for extraneous flags */
170         /* XXX || (!krb5_addresses_compare(addrs, as_reply->enc_part2->caddrs)) */
171         || ((request.from == 0) &&
172             !in_clock_skew(as_reply->enc_part2->times.starttime))
173         || ((request.from != 0) &&
174             (request.from != as_reply->enc_part2->times.starttime))
175         || ((request.till != 0) &&
176             (as_reply->enc_part2->times.endtime > request.till))
177         || ((request.kdc_options & KDC_OPT_RENEWABLE) &&
178             (request.rtime != 0) &&
179             (as_reply->enc_part2->times.renew_till > request.rtime))
180         || ((request.kdc_options & KDC_OPT_RENEWABLE_OK) &&
181             (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
182             (request.till != 0) &&
183             (as_reply->enc_part2->times.renew_till > request.till))
184         ) {
185         memset((char *)as_reply->enc_part2->session->contents, 0,
186               as_reply->enc_part2->session->length);
187         krb5_free_kdc_rep(as_reply);
188         return KRB5_KDCREP_MODIFIED;
189     }
190
191     /* XXX issue warning if as_reply->enc_part2->key_exp is nearby */
192         
193     /* fill in the credentials */
194     if (retval = krb5_copy_keyblock(as_reply->enc_part2->session,
195                                     &creds->keyblock)) {
196         memset((char *)as_reply->enc_part2->session->contents, 0,
197               as_reply->enc_part2->session->length);
198         krb5_free_kdc_rep(as_reply);
199         return retval;
200     }
201 #define cleanup_key() {memset((char *)creds->keyblock.contents, 0,\
202                              creds->keyblock.length); \
203                        free((char *)creds->keyblock.contents); \
204                        creds->keyblock.contents = 0; \
205                        creds->keyblock.length = 0;}
206
207     creds->times = as_reply->enc_part2->times;
208     creds->is_skey = FALSE;             /* this is an AS_REQ, so cannot
209                                            be encrypted in skey */
210     creds->ticket_flags = as_reply->enc_part2->flags;
211     if (retval = krb5_copy_addresses(as_reply->enc_part2->caddrs,
212                                      &creds->addresses)) {
213         cleanup_key();
214         return retval;
215     }
216     creds->second_ticket.length = 0;
217     creds->second_ticket.data = 0;
218
219     retval = encode_krb5_ticket(as_reply->ticket, &packet);
220     krb5_free_kdc_rep(as_reply);
221     if (retval) {
222         krb5_free_address(creds->addresses);
223         cleanup_key();
224         return retval;
225     }   
226     creds->ticket = *packet;
227     free((char *) packet);
228
229     /* store it in the ccache! */
230     if (retval = krb5_cc_store_cred(ccache, creds)) {
231         /* clean up the pieces */
232         free((char *)creds->ticket.data);
233         krb5_free_address(creds->addresses);
234         cleanup_key();
235         return retval;
236     }
237     return 0;
238 }
239