ddf064d3974d537714c3d0ccb4f7ac3f4d3560eb
[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;
73         int code = -1;
74
75         if ((ret = krb5_init_context(&context))) {
76                 printerr(0, "ERROR: initializing krb5_context: %s\n",
77                         gssd_k5_err_msg(NULL, ret));
78                 goto out_err;
79         }
80
81         if ((ret = krb5_auth_con_getlocalsubkey(context,
82                                                 ctx->auth_context, &key))){
83                 printerr(0, "ERROR: getting auth_context key: %s\n",
84                         gssd_k5_err_msg(context, ret));
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
101                 printerr(0, "ERROR: allocating memory for enc key: %s\n",
102                         gssd_k5_err_msg(context, ENOMEM));
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         printerr(2, "write_heimdal_enc_key: %s\n", code ? "FAILED" : "SUCCESS");
123         return(code);
124 }
125
126 int write_heimdal_seq_key(char **p, char *end, gss_ctx_id_t ctx)
127 {
128         krb5_keyblock *key;
129         krb5_context context;
130         krb5_error_code ret;
131         int code = -1;
132
133         if ((ret = krb5_init_context(&context))) {
134                 printerr(0, "ERROR: initializing krb5_context: %s\n",
135                         gssd_k5_err_msg(NULL, ret));
136                 goto out_err;
137         }
138
139         if ((ret = krb5_auth_con_getlocalsubkey(context,
140                                                 ctx->auth_context, &key))){
141                 printerr(0, "ERROR: getting auth_context key: %s\n",
142                         gssd_k5_err_msg(context, ret));
143                 goto out_err_free_context;
144         }
145
146         /* XXX current kernel code only handles des-cbc-raw  (4) */
147         if (key->keytype != 4) {
148                 printerr(1, "WARN: write_heimdal_seq_key: "
149                             "overriding heimdal keytype (%d => %d)\n",
150                          key->keytype, 4);
151                 key->keytype = 4;
152         }
153
154         if (write_heimdal_keyblock(p, end, key)) {
155                 goto out_err_free_key;
156         }
157
158         code = 0;
159
160     out_err_free_key:
161         krb5_free_keyblock(context, key);
162     out_err_free_context:
163         krb5_free_context(context);
164     out_err:
165         printerr(2, "write_heimdal_seq_key: %s\n", code ? "FAILED" : "SUCCESS");
166         return(code);
167 }
168
169 /*
170  * The following is the kernel structure that we are filling in:
171  *
172  * struct krb5_ctx {
173  *         int                     initiate;
174  *         int                     seed_init;
175  *         unsigned char           seed[16];
176  *         int                     signalg;
177  *         int                     sealalg;
178  *         struct crypto_tfm       *enc;
179  *         struct crypto_tfm       *seq;
180  *         s32                     endtime;
181  *         u32                     seq_send;
182  *         struct xdr_netobj       mech_used;
183  * };
184  *
185  * However, note that we do not send the data fields in the
186  * order they appear in the structure.  The order they are
187  * sent down in is:
188  *
189  *      initiate
190  *      seed_init
191  *      seed
192  *      signalg
193  *      sealalg
194  *      endtime
195  *      seq_send
196  *      mech_used
197  *      enc key
198  *      seq key
199  *
200  */
201
202 int
203 serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, int32_t *endtime)
204 {
205
206         char *p, *end;
207         static int constant_one = 1;
208         static int constant_zero = 0;
209         unsigned char fakeseed[16];
210         uint32_t algorithm;
211
212         if (!(buf->value = calloc(1, MAX_CTX_LEN)))
213                 goto out_err;
214         p = buf->value;
215         end = buf->value + MAX_CTX_LEN;
216
217
218         /* initiate:  1 => initiating 0 => accepting */
219         if (ctx->more_flags & LOCAL) {
220                 if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
221         }
222         else {
223                 if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
224         }
225
226         /* seed_init: not used by kernel code */
227         if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
228
229         /* seed: not used by kernel code */
230         memset(&fakeseed, 0, sizeof(fakeseed));
231         if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
232
233         /* signalg */
234         algorithm = 0; /* SGN_ALG_DES_MAC_MD5   XXX */
235         if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
236
237         /* sealalg */
238         algorithm = 0; /* SEAL_ALG_DES          XXX */
239         if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
240
241         /* endtime */
242         if (WRITE_BYTES(&p, end, ctx->lifetime)) goto out_err;
243
244         if (endtime)
245                 *endtime = ctx->lifetime;
246
247         /* seq_send */
248         if (WRITE_BYTES(&p, end, ctx->auth_context->local_seqnumber))
249                 goto out_err;
250         /* mech_used */
251         if (write_buffer(&p, end, (gss_buffer_desc*)&krb5oid)) goto out_err;
252
253         /* enc: derive the encryption key and copy it into buffer */
254         if (write_heimdal_enc_key(&p, end, ctx)) goto out_err;
255
256         /* seq: get the sequence number key and copy it into buffer */
257         if (write_heimdal_seq_key(&p, end, ctx)) goto out_err;
258
259         buf->length = p - (char *)buf->value;
260         printerr(2, "serialize_krb5_ctx: returning buffer "
261                     "with %d bytes\n", buf->length);
262
263         return 0;
264 out_err:
265         printerr(0, "ERROR: failed exporting Heimdal krb5 ctx to kernel\n");
266         if (buf->value) free(buf->value);
267         buf->length = 0;
268         return -1;
269 }
270
271 #endif  /* HAVE_HEIMDAL */
272 #endif  /* HAVE_LUCID_CONTEXT_SUPPORT */