Add option to specify directory to search for credentials cache files
[nfs-utils.git] / utils / gssd / context_heimdal.c
1 /*
2   Copyright (c) 2004 The Regents of the University of Michigan.
3   All rights reserved.
4
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions
7   are met:
8
9   1. Redistributions of source code must retain the above copyright
10      notice, this list of conditions and the following disclaimer.
11   2. Redistributions in binary form must reproduce the above copyright
12      notice, this list of conditions and the following disclaimer in the
13      documentation and/or other materials provided with the distribution.
14   3. Neither the name of the University nor the names of its
15      contributors may be used to endorse or promote products derived
16      from this software without specific prior written permission.
17
18   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
19   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32
33 #ifdef HAVE_HEIMDAL
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <syslog.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <krb5.h>
41 #include <gssapi.h>     /* Must use the heimdal copy! */
42 #ifdef HAVE_COM_ERR_H
43 #include <com_err.h>
44 #endif
45 #include "err_util.h"
46 #include "gss_oids.h"
47 #include "write_bytes.h"
48
49 #define MAX_CTX_LEN 4096
50
51 int write_heimdal_keyblock(char **p, char *end, krb5_keyblock *key)
52 {
53         gss_buffer_desc tmp;
54         int code = -1;
55
56         if (WRITE_BYTES(p, end, key->keytype)) goto out_err;
57         tmp.length = key->keyvalue.length;
58         tmp.value = key->keyvalue.data;
59         if (write_buffer(p, end, &tmp)) goto out_err;
60         code = 0;
61     out_err:
62         return(code);
63 }
64
65 int write_heimdal_enc_key(char **p, char *end, gss_ctx_id_t ctx)
66 {
67         krb5_keyblock enc_key, *key;
68         krb5_context context;
69         krb5_error_code ret;
70         int i;
71         char *skd, *dkd;
72         int code = -1;
73
74         if ((ret = krb5_init_context(&context))) {
75                 printerr(0, "ERROR: initializing krb5_context: %s\n",
76                         error_message(ret));
77                 goto out_err;
78         }
79
80         if ((ret = krb5_auth_con_getlocalsubkey(context,
81                                                 ctx->auth_context, &key))){
82                 printerr(0, "ERROR: getting auth_context key: %s\n",
83                         error_message(ret));
84                 goto out_err_free_context;
85         }
86
87         memset(&enc_key, 0, sizeof(enc_key));
88         enc_key.keytype = key->keytype;
89         /* XXX current kernel code only handles des-cbc-raw  (4) */
90         if (enc_key.keytype != 4) {
91                 printerr(1, "WARN: write_heimdal_enc_key: "
92                             "overriding heimdal keytype (%d => %d)\n",
93                          enc_key.keytype, 4);
94                 enc_key.keytype = 4;
95         }
96         enc_key.keyvalue.length = key->keyvalue.length;
97         if ((enc_key.keyvalue.data =
98                                 calloc(1, enc_key.keyvalue.length)) == NULL) {
99
100                 printerr(0, "ERROR: allocating memory for enc key: %s\n",
101                         error_message(ENOMEM));
102                 goto out_err_free_key;
103         }
104         skd = (char *) key->keyvalue.data;
105         dkd = (char *) enc_key.keyvalue.data;
106         for (i = 0; i < enc_key.keyvalue.length; i++)
107                 dkd[i] = skd[i] ^ 0xf0;
108         if (write_heimdal_keyblock(p, end, &enc_key)) {
109                 goto out_err_free_enckey;
110         }
111
112         code = 0;
113
114     out_err_free_enckey:
115         krb5_free_keyblock_contents(context, &enc_key);
116     out_err_free_key:
117         krb5_free_keyblock(context, key);
118     out_err_free_context:
119         krb5_free_context(context);
120     out_err:
121         printerr(2, "write_heimdal_enc_key: %s\n", code ? "FAILED" : "SUCCESS");
122         return(code);
123 }
124
125 int write_heimdal_seq_key(char **p, char *end, gss_ctx_id_t ctx)
126 {
127         krb5_keyblock *key;
128         krb5_context context;
129         krb5_error_code ret;
130         int code = -1;
131
132         if ((ret = krb5_init_context(&context))) {
133                 printerr(0, "ERROR: initializing krb5_context: %s\n",
134                         error_message(ret));
135                 goto out_err;
136         }
137
138         if ((ret = krb5_auth_con_getlocalsubkey(context,
139                                                 ctx->auth_context, &key))){
140                 printerr(0, "ERROR: getting auth_context key: %s\n",
141                         error_message(ret));
142                 goto out_err_free_context;
143         }
144
145         /* XXX current kernel code only handles des-cbc-raw  (4) */
146         if (key->keytype != 4) {
147                 printerr(1, "WARN: write_heimdal_seq_key: "
148                             "overriding heimdal keytype (%d => %d)\n",
149                          key->keytype, 4);
150                 key->keytype = 4;
151         }
152
153         if (write_heimdal_keyblock(p, end, key)) {
154                 goto out_err_free_key;
155         }
156
157         code = 0;
158
159     out_err_free_key:
160         krb5_free_keyblock(context, key);
161     out_err_free_context:
162         krb5_free_context(context);
163     out_err:
164         printerr(2, "write_heimdal_seq_key: %s\n", code ? "FAILED" : "SUCCESS");
165         return(code);
166 }
167
168 /*
169  * The following is the kernel structure that we are filling in:
170  *
171  * struct krb5_ctx {
172  *         int                     initiate;
173  *         int                     seed_init;
174  *         unsigned char           seed[16];
175  *         int                     signalg;
176  *         int                     sealalg;
177  *         struct crypto_tfm       *enc;
178  *         struct crypto_tfm       *seq;
179  *         s32                     endtime;
180  *         u32                     seq_send;
181  *         struct xdr_netobj       mech_used;
182  * };
183  *
184  * However, note that we do not send the data fields in the
185  * order they appear in the structure.  The order they are
186  * sent down in is:
187  *
188  *      initiate
189  *      seed_init
190  *      seed
191  *      signalg
192  *      sealalg
193  *      endtime
194  *      seq_send
195  *      mech_used
196  *      enc key
197  *      seq key
198  *
199  */
200
201 int
202 serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
203 {
204
205         char *p, *end;
206         static int constant_one = 1;
207         static int constant_zero = 0;
208         unsigned char fakeseed[16];
209         uint32_t algorithm;
210
211         if (!(buf->value = calloc(1, MAX_CTX_LEN)))
212                 goto out_err;
213         p = buf->value;
214         end = buf->value + MAX_CTX_LEN;
215
216
217         /* initiate:  1 => initiating 0 => accepting */
218         if (ctx->more_flags & LOCAL) {
219                 if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
220         }
221         else {
222                 if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
223         }
224
225         /* seed_init: not used by kernel code */
226         if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
227
228         /* seed: not used by kernel code */
229         memset(&fakeseed, 0, sizeof(fakeseed));
230         if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
231
232         /* signalg */
233         algorithm = 0; /* SGN_ALG_DES_MAC_MD5   XXX */
234         if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
235
236         /* sealalg */
237         algorithm = 0; /* SEAL_ALG_DES          XXX */
238         if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
239
240         /* endtime */
241         if (WRITE_BYTES(&p, end, ctx->lifetime)) goto out_err;
242
243         /* seq_send */
244         if (WRITE_BYTES(&p, end, ctx->auth_context->local_seqnumber))
245                 goto out_err;
246         /* mech_used */
247         if (write_buffer(&p, end, (gss_buffer_desc*)&krb5oid)) goto out_err;
248
249         /* enc: derive the encryption key and copy it into buffer */
250         if (write_heimdal_enc_key(&p, end, ctx)) goto out_err;
251
252         /* seq: get the sequence number key and copy it into buffer */
253         if (write_heimdal_seq_key(&p, end, ctx)) goto out_err;
254
255         buf->length = p - (char *)buf->value;
256         printerr(2, "serialize_krb5_ctx: returning buffer "
257                     "with %d bytes\n", buf->length);
258
259         return 0;
260 out_err:
261         printerr(0, "ERROR: failed exporting Heimdal krb5 ctx to kernel\n");
262         if (buf->value) free(buf->value);
263         buf->length = 0;
264         return -1;
265 }
266
267 #endif  /* HAVE_HEIMDAL */