6f3b8fd03f37e12a048337eed1d0d80e3bdb3224
[nfs-utils.git] / utils / gssd / context_heimdal.c
1 /*
2   Copyright (c) 2004-2006 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 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif  /* HAVE_CONFIG_H */
34
35 #ifndef HAVE_LUCID_CONTEXT_SUPPORT
36 #ifdef HAVE_HEIMDAL
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <syslog.h>
41 #include <string.h>
42 #include <errno.h>
43 #include <krb5.h>
44 #include <gssapi.h>     /* Must use the heimdal copy! */
45 #ifdef HAVE_COM_ERR_H
46 #include <com_err.h>
47 #endif
48 #include "err_util.h"
49 #include "gss_oids.h"
50 #include "write_bytes.h"
51
52 int write_heimdal_keyblock(char **p, char *end, krb5_keyblock *key)
53 {
54         gss_buffer_desc tmp;
55         int code = -1;
56
57         if (WRITE_BYTES(p, end, key->keytype)) goto out_err;
58         tmp.length = key->keyvalue.length;
59         tmp.value = key->keyvalue.data;
60         if (write_buffer(p, end, &tmp)) goto out_err;
61         code = 0;
62     out_err:
63         return(code);
64 }
65
66 int write_heimdal_enc_key(char **p, char *end, gss_ctx_id_t ctx)
67 {
68         krb5_keyblock enc_key, *key;
69         krb5_context context;
70         krb5_error_code ret;
71         int i;
72         char *skd, *dkd, *k5err = NULL;
73         int code = -1;
74
75         if ((ret = krb5_init_context(&context))) {
76                 k5err = gssd_k5_err_msg(NULL, ret);
77                 printerr(0, "ERROR: initializing krb5_context: %s\n", k5err);
78                 goto out_err;
79         }
80
81         if ((ret = krb5_auth_con_getlocalsubkey(context,
82                                                 ctx->auth_context, &key))){
83                 k5err = gssd_k5_err_msg(context, ret);
84                 printerr(0, "ERROR: getting auth_context key: %s\n", k5err);
85                 goto out_err_free_context;
86         }
87
88         memset(&enc_key, 0, sizeof(enc_key));
89         enc_key.keytype = key->keytype;
90         /* XXX current kernel code only handles des-cbc-raw  (4) */
91         if (enc_key.keytype != 4) {
92                 printerr(1, "WARN: write_heimdal_enc_key: "
93                             "overriding heimdal keytype (%d => %d)\n",
94                          enc_key.keytype, 4);
95                 enc_key.keytype = 4;
96         }
97         enc_key.keyvalue.length = key->keyvalue.length;
98         if ((enc_key.keyvalue.data =
99                                 calloc(1, enc_key.keyvalue.length)) == NULL) {
100                 k5err = gssd_k5_err_msg(context, ENOMEM);
101                 printerr(0, "ERROR: allocating memory for enc key: %s\n",
102                          k5err);
103                 goto out_err_free_key;
104         }
105         skd = (char *) key->keyvalue.data;
106         dkd = (char *) enc_key.keyvalue.data;
107         for (i = 0; i < enc_key.keyvalue.length; i++)
108                 dkd[i] = skd[i] ^ 0xf0;
109         if (write_heimdal_keyblock(p, end, &enc_key)) {
110                 goto out_err_free_enckey;
111         }
112
113         code = 0;
114
115     out_err_free_enckey:
116         krb5_free_keyblock_contents(context, &enc_key);
117     out_err_free_key:
118         krb5_free_keyblock(context, key);
119     out_err_free_context:
120         krb5_free_context(context);
121     out_err:
122         free(k5err);
123         printerr(2, "write_heimdal_enc_key: %s\n", code ? "FAILED" : "SUCCESS");
124         return(code);
125 }
126
127 int write_heimdal_seq_key(char **p, char *end, gss_ctx_id_t ctx)
128 {
129         krb5_keyblock *key;
130         krb5_context context;
131         krb5_error_code ret;
132         char *k5err = NULL;
133         int code = -1;
134
135         if ((ret = krb5_init_context(&context))) {
136                 k5err = gssd_k5_err_msg(NULL, ret);
137                 printerr(0, "ERROR: initializing krb5_context: %s\n", k5err);
138                 goto out_err;
139         }
140
141         if ((ret = krb5_auth_con_getlocalsubkey(context,
142                                                 ctx->auth_context, &key))){
143                 k5err = gssd_k5_err_msg(context, ret);
144                 printerr(0, "ERROR: getting auth_context key: %s\n", k5err);
145                 goto out_err_free_context;
146         }
147
148         /* XXX current kernel code only handles des-cbc-raw  (4) */
149         if (key->keytype != 4) {
150                 printerr(1, "WARN: write_heimdal_seq_key: "
151                             "overriding heimdal keytype (%d => %d)\n",
152                          key->keytype, 4);
153                 key->keytype = 4;
154         }
155
156         if (write_heimdal_keyblock(p, end, key)) {
157                 goto out_err_free_key;
158         }
159
160         code = 0;
161
162     out_err_free_key:
163         krb5_free_keyblock(context, key);
164     out_err_free_context:
165         krb5_free_context(context);
166     out_err:
167         free(k5err);
168         printerr(2, "write_heimdal_seq_key: %s\n", code ? "FAILED" : "SUCCESS");
169         return(code);
170 }
171
172 /*
173  * The following is the kernel structure that we are filling in:
174  *
175  * struct krb5_ctx {
176  *         int                     initiate;
177  *         int                     seed_init;
178  *         unsigned char           seed[16];
179  *         int                     signalg;
180  *         int                     sealalg;
181  *         struct crypto_tfm       *enc;
182  *         struct crypto_tfm       *seq;
183  *         s32                     endtime;
184  *         u32                     seq_send;
185  *         struct xdr_netobj       mech_used;
186  * };
187  *
188  * However, note that we do not send the data fields in the
189  * order they appear in the structure.  The order they are
190  * sent down in is:
191  *
192  *      initiate
193  *      seed_init
194  *      seed
195  *      signalg
196  *      sealalg
197  *      endtime
198  *      seq_send
199  *      mech_used
200  *      enc key
201  *      seq key
202  *
203  */
204
205 int
206 serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, int32_t *endtime)
207 {
208
209         char *p, *end;
210         static int constant_one = 1;
211         static int constant_zero = 0;
212         unsigned char fakeseed[16];
213         uint32_t algorithm;
214
215         if (!(buf->value = calloc(1, MAX_CTX_LEN)))
216                 goto out_err;
217         p = buf->value;
218         end = buf->value + MAX_CTX_LEN;
219
220
221         /* initiate:  1 => initiating 0 => accepting */
222         if (ctx->more_flags & LOCAL) {
223                 if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
224         }
225         else {
226                 if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
227         }
228
229         /* seed_init: not used by kernel code */
230         if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
231
232         /* seed: not used by kernel code */
233         memset(&fakeseed, 0, sizeof(fakeseed));
234         if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
235
236         /* signalg */
237         algorithm = 0; /* SGN_ALG_DES_MAC_MD5   XXX */
238         if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
239
240         /* sealalg */
241         algorithm = 0; /* SEAL_ALG_DES          XXX */
242         if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
243
244         /* endtime */
245         if (WRITE_BYTES(&p, end, ctx->lifetime)) goto out_err;
246
247         if (endtime)
248                 *endtime = ctx->lifetime;
249
250         /* seq_send */
251         if (WRITE_BYTES(&p, end, ctx->auth_context->local_seqnumber))
252                 goto out_err;
253         /* mech_used */
254         if (write_buffer(&p, end, (gss_buffer_desc*)&krb5oid)) goto out_err;
255
256         /* enc: derive the encryption key and copy it into buffer */
257         if (write_heimdal_enc_key(&p, end, ctx)) goto out_err;
258
259         /* seq: get the sequence number key and copy it into buffer */
260         if (write_heimdal_seq_key(&p, end, ctx)) goto out_err;
261
262         buf->length = p - (char *)buf->value;
263         printerr(2, "serialize_krb5_ctx: returning buffer "
264                     "with %d bytes\n", buf->length);
265
266         return 0;
267 out_err:
268         printerr(0, "ERROR: failed exporting Heimdal krb5 ctx to kernel\n");
269         if (buf->value) free(buf->value);
270         buf->length = 0;
271         return -1;
272 }
273
274 #endif  /* HAVE_HEIMDAL */
275 #endif  /* HAVE_LUCID_CONTEXT_SUPPORT */