Whamcloud - gitweb
branch: HEAD
[fs/lustre-release.git] / lustre / utils / gss / context_lucid.c
1 /*
2  * COPYRIGHT (c) 2006
3  * The Regents of the University of Michigan
4  * ALL RIGHTS RESERVED
5  *
6  * Permission is granted to use, copy, create derivative works
7  * and redistribute this software and such derivative works
8  * for any purpose, so long as the name of The University of
9  * Michigan is not used in any advertising or publicity
10  * pertaining to the use of distribution of this software
11  * without specific, written prior authorization.  If the
12  * above copyright notice or any other identification of the
13  * University of Michigan is included in any copy of any
14  * portion of this software, then the disclaimer below must
15  * also be included.
16  *
17  * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
18  * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
19  * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
20  * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
21  * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
22  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
23  * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
24  * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
25  * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
26  * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
27  * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGES.
29  */
30
31 #include "config.h"
32
33 #ifdef HAVE_LUCID_CONTEXT_SUPPORT
34
35 /*
36  * Newer versions of MIT and Heimdal have lucid context support.
37  * We can use common code if it is supported.
38  */
39
40 #include <stdio.h>
41 #include <syslog.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <stdint.h>
45 #include <krb5.h>
46 #include <gssapi/gssapi.h>
47 #ifndef OM_uint64
48 typedef uint64_t OM_uint64;
49 #endif
50 #include <gssapi/gssapi_krb5.h>
51
52 #ifdef _NEW_BUILD_
53 # include "lgss_utils.h"
54 #else
55 # include "gss_util.h"
56 # include "gss_oids.h"
57 # include "err_util.h"
58 #endif
59 #include "write_bytes.h"
60 #include "context.h"
61
62 static int
63 write_lucid_keyblock(char **p, char *end, gss_krb5_lucid_key_t *key)
64 {
65         gss_buffer_desc tmp;
66
67         if (WRITE_BYTES(p, end, key->type)) return -1;
68         tmp.length = key->length;
69         tmp.value = key->data;
70         if (write_buffer(p, end, &tmp)) return -1;
71         return 0;
72 }
73
74 static int
75 prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx,
76         gss_buffer_desc *buf)
77 {
78         char *p, *end;
79         static int constant_zero = 0;
80         unsigned char fakeseed[16];
81         uint32_t word_send_seq;
82         gss_krb5_lucid_key_t enc_key;
83         int i;
84         char *skd, *dkd;
85         gss_buffer_desc fakeoid;
86
87         /*
88          * The new Kerberos interface to get the gss context
89          * does not include the seed or seed_init fields
90          * because we never really use them.  But for now,
91          * send down a fake buffer so we can use the same
92          * interface to the kernel.
93          */
94         memset(&enc_key, 0, sizeof(enc_key));
95         memset(&fakeoid, 0, sizeof(fakeoid));
96
97         if (!(buf->value = calloc(1, MAX_CTX_LEN)))
98                 goto out_err;
99         p = buf->value;
100         end = buf->value + MAX_CTX_LEN;
101
102         if (WRITE_BYTES(&p, end, lctx->initiate)) goto out_err;
103
104         /* seed_init and seed not used by kernel anyway */
105         if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
106         if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
107
108         if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.sign_alg)) goto out_err;
109         if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.seal_alg)) goto out_err;
110         if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
111         word_send_seq = lctx->send_seq; /* XXX send_seq is 64-bit */
112         if (WRITE_BYTES(&p, end, word_send_seq)) goto out_err;
113         if (write_oid(&p, end, &krb5oid)) goto out_err;
114
115 #ifdef HAVE_HEIMDAL
116         /*
117          * The kernel gss code expects des-cbc-raw for all flavors of des.
118          * The keytype from MIT has this type, but Heimdal does not.
119          * Force the Heimdal keytype to 4 (des-cbc-raw).
120          * Note that the rfc1964 version only supports DES enctypes.
121          */
122         if (lctx->rfc1964_kd.ctx_key.type != 4) {
123                 printerr(2, "%s: overriding heimdal keytype (%d => %d)\n",
124                          __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, 4);
125                 lctx->rfc1964_kd.ctx_key.type = 4;
126         }
127 #endif
128         printerr(2, "%s: serializing keys with enctype %d and length %d\n",
129                  __FUNCTION__, lctx->rfc1964_kd.ctx_key.type,
130                  lctx->rfc1964_kd.ctx_key.length);
131
132         /* derive the encryption key and copy it into buffer */
133         enc_key.type = lctx->rfc1964_kd.ctx_key.type;
134         enc_key.length = lctx->rfc1964_kd.ctx_key.length;
135         if ((enc_key.data = calloc(1, enc_key.length)) == NULL)
136                 goto out_err;
137         skd = (char *) lctx->rfc1964_kd.ctx_key.data;
138         dkd = (char *) enc_key.data;
139         for (i = 0; i < enc_key.length; i++)
140                 dkd[i] = skd[i] ^ 0xf0;
141         if (write_lucid_keyblock(&p, end, &enc_key)) {
142                 free(enc_key.data);
143                 goto out_err;
144         }
145         free(enc_key.data);
146
147         if (write_lucid_keyblock(&p, end, &lctx->rfc1964_kd.ctx_key))
148                 goto out_err;
149
150         buf->length = p - (char *)buf->value;
151         return 0;
152 out_err:
153         printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
154         if (buf->value) free(buf->value);
155         buf->length = 0;
156         if (enc_key.data) free(enc_key.data);
157         return -1;
158 }
159
160 /* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
161 /* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
162
163 /* for 3DES */
164 #define KG_USAGE_SEAL 22
165 #define KG_USAGE_SIGN 23
166 #define KG_USAGE_SEQ  24
167
168 /* for rfc???? */
169 #define KG_USAGE_ACCEPTOR_SEAL  22
170 #define KG_USAGE_ACCEPTOR_SIGN  23
171 #define KG_USAGE_INITIATOR_SEAL 24
172 #define KG_USAGE_INITIATOR_SIGN 25
173
174 /* Lifted from mit src/lib/gssapi/krb5/gssapiP_krb5.h */
175 enum seal_alg {
176   SEAL_ALG_NONE            = 0xffff,
177   SEAL_ALG_DES             = 0x0000,
178   SEAL_ALG_1               = 0x0001, /* not published */
179   SEAL_ALG_MICROSOFT_RC4   = 0x0010, /* microsoft w2k;  */
180   SEAL_ALG_DES3KD          = 0x0002
181 };
182
183 #define KEY_USAGE_SEED_ENCRYPTION       0xAA
184 #define KEY_USAGE_SEED_INTEGRITY        0x55
185 #define KEY_USAGE_SEED_CHECKSUM         0x99
186 #define K5CLENGTH 5
187
188 /* Flags for version 2 context flags */
189 #define KRB5_CTX_FLAG_INITIATOR         0x00000001
190 #define KRB5_CTX_FLAG_CFX               0x00000002
191 #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY   0x00000004
192
193 /* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
194 /* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
195 /*
196  * We don't have "legal" access to these MIT-only
197  * structures located in libk5crypto
198  */
199 extern void krb5int_enc_arcfour;
200 extern void krb5int_enc_des3;
201 extern void krb5int_enc_aes128;
202 extern void krb5int_enc_aes256;
203 extern int krb5_derive_key();
204
205 static void
206 key_lucid_to_krb5(const gss_krb5_lucid_key_t *lin, krb5_keyblock *kout)
207 {
208         memset(kout, '\0', sizeof(kout));
209 #ifdef HAVE_KRB5
210         kout->enctype = lin->type;
211         kout->length = lin->length;
212         kout->contents = lin->data;
213 #else
214         kout->keytype = lin->type;
215         kout->keyvalue.length = lin->length;
216         kout->keyvalue.data = lin->data;
217 #endif
218 }
219
220 static void
221 key_krb5_to_lucid(const krb5_keyblock *kin, gss_krb5_lucid_key_t *lout)
222 {
223         memset(lout, '\0', sizeof(lout));
224 #ifdef HAVE_KRB5
225         lout->type = kin->enctype;
226         lout->length = kin->length;
227         lout->data = kin->contents;
228 #else
229         lout->type = kin->keytype;
230         lout->length = kin->keyvalue.length;
231         memcpy(lout->data, kin->keyvalue.data, kin->keyvalue.length);
232 #endif
233 }
234
235 /* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
236 /* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
237 /* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
238 /* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
239 /*
240  * Function to derive a new key from a given key and given constant data.
241  */
242 static krb5_error_code
243 derive_key_lucid(const gss_krb5_lucid_key_t *in, gss_krb5_lucid_key_t *out,
244                  int usage, char extra)
245 {
246         krb5_error_code code;
247         unsigned char constant_data[K5CLENGTH];
248         krb5_data datain;
249         int keylength;
250         void *enc;
251         krb5_keyblock kin, kout;  /* must send krb5_keyblock, not lucid! */
252 #ifdef HAVE_HEIMDAL
253         krb5_context kcontext;
254         krb5_keyblock *outkey;
255 #endif
256
257         /*
258          * XXX Hack alert.  We don't have "legal" access to these
259          * values and structures located in libk5crypto
260          */
261         switch (in->type) {
262         case ENCTYPE_DES3_CBC_SHA1:
263 #ifdef HAVE_KRB5
264         case ENCTYPE_DES3_CBC_RAW:
265 #endif
266                 keylength = 24;
267 #ifdef HAVE_KRB5
268                 enc = &krb5int_enc_des3;
269 #endif
270                 break;
271         case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
272                 keylength = 16;
273 #ifdef HAVE_KRB5
274                 enc = &krb5int_enc_aes128;
275 #endif
276                 break;
277         case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
278                 keylength = 32;
279 #ifdef HAVE_KRB5
280                 enc = &krb5int_enc_aes256;
281 #endif
282                 break;
283         default:
284                 code = KRB5_BAD_ENCTYPE;
285                 goto out;
286         }
287
288         /* allocate memory for output key */
289         if ((out->data = malloc(keylength)) == NULL) {
290                 code = ENOMEM;
291                 goto out;
292         }
293         out->length = keylength;
294         out->type = in->type;
295
296         /* Convert to correct format for call to krb5_derive_key */
297         key_lucid_to_krb5(in, &kin);
298         key_lucid_to_krb5(out, &kout);
299
300         datain.data = (char *) constant_data;
301         datain.length = K5CLENGTH;
302
303         ((char *)(datain.data))[0] = (usage>>24)&0xff;
304         ((char *)(datain.data))[1] = (usage>>16)&0xff;
305         ((char *)(datain.data))[2] = (usage>>8)&0xff;
306         ((char *)(datain.data))[3] = usage&0xff;
307
308         ((char *)(datain.data))[4] = (char) extra;
309
310 #ifdef HAVE_KRB5
311         code = krb5_derive_key(enc, &kin, &kout, &datain);
312 #else
313         if ((code = krb5_init_context(&kcontext))) {
314         }
315         code = krb5_derive_key(kcontext, &kin, in->type, constant_data, K5CLENGTH, &outkey);
316 #endif
317         if (code) {
318                 free(out->data);
319                 out->data = NULL;
320                 goto out;
321         }
322 #ifdef HAVE_KRB5
323         key_krb5_to_lucid(&kout, out);
324 #else
325         key_krb5_to_lucid(outkey, out);
326         krb5_free_keyblock(kcontext, outkey);
327         krb5_free_context(kcontext);
328 #endif
329
330   out:
331         if (code)
332                 printerr(0, "ERROR: %s: returning error %d (%s)\n",
333                          __FUNCTION__, code, error_message(code));
334         return (code);
335 }
336
337
338 /*
339  * Prepare a new-style buffer, as defined in rfc4121 (a.k.a. cfx),
340  * to send to the kernel for newer encryption types -- or for DES3.
341  *
342  * The new format is:
343  *
344  *      u32 initiate;                   ( whether we are the initiator or not )
345  *      s32 endtime;
346  *      u32 flags;
347  *      #define KRB5_CTX_FLAG_INITIATOR         0x00000001
348  *      #define KRB5_CTX_FLAG_CFX               0x00000002
349  *      #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY   0x00000004
350  *      u64 seq_send;
351  *      u32  enctype;                   ( encrption type of keys )
352  *      u32  size_of_each_key;          ( size of each key in bytes )
353  *      u32  number_of_keys;            ( N -- should always be 3 for now )
354  *      keydata-1;                      ( Ke )
355  *      keydata-2;                      ( Ki )
356  *      keydata-3;                      ( Kc )
357  *
358  */
359 static int
360 prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx,
361                             gss_buffer_desc *buf)
362 {
363         static int constant_two = 2;
364         char *p, *end;
365         uint32_t v2_flags = 0;
366         gss_krb5_lucid_key_t enc_key;
367         gss_krb5_lucid_key_t derived_key;
368         gss_buffer_desc fakeoid;
369         uint32_t enctype;
370         uint32_t keysize;
371         uint32_t numkeys;
372
373         memset(&enc_key, 0, sizeof(enc_key));
374         memset(&fakeoid, 0, sizeof(fakeoid));
375
376         if (!(buf->value = calloc(1, MAX_CTX_LEN)))
377                 goto out_err;
378         p = buf->value;
379         end = buf->value + MAX_CTX_LEN;
380
381         /* Version 2 */
382         if (WRITE_BYTES(&p, end, constant_two)) goto out_err;
383         if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
384
385         if (lctx->initiate)
386                 v2_flags |= KRB5_CTX_FLAG_INITIATOR;
387         if (lctx->protocol != 0)
388                 v2_flags |= KRB5_CTX_FLAG_CFX;
389         if (lctx->protocol != 0 && lctx->cfx_kd.have_acceptor_subkey == 1)
390                 v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY;
391
392         if (WRITE_BYTES(&p, end, v2_flags)) goto out_err;
393
394         if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err;
395
396         /* Protocol 0 here implies DES3 or RC4 */
397         printerr(3, "protocol %d\n", lctx->protocol);
398         if (lctx->protocol == 0) {
399                 enctype = lctx->rfc1964_kd.ctx_key.type;
400 #ifdef HAVE_HEIMDAL
401                 /*
402                  * The kernel gss code expects ENCTYPE_DES3_CBC_RAW (6) for
403                  * 3des keys, but Heimdal key has ENCTYPE_DES3_CBC_SHA1 (16).
404                  * Force the Heimdal enctype to 6.
405                  */
406                 if (enctype == ENCTYPE_DES3_CBC_SHA1) {
407                         printerr(2, "%s: overriding heimdal keytype (%d => %d)\n",
408                                  __FUNCTION__, enctype, 6);
409
410                         enctype = 6;
411                 }
412 #endif
413                 keysize = lctx->rfc1964_kd.ctx_key.length;
414                 numkeys = 3;    /* XXX is always gonna be three? */
415         } else {
416                 if (lctx->cfx_kd.have_acceptor_subkey) {
417                         enctype = lctx->cfx_kd.acceptor_subkey.type;
418                         keysize = lctx->cfx_kd.acceptor_subkey.length;
419                 } else {
420                         enctype = lctx->cfx_kd.ctx_key.type;
421                         keysize = lctx->cfx_kd.ctx_key.length;
422                 }
423                 numkeys = 3;
424         }
425         printerr(3, "serializing %d keys with enctype %d and size %d\n",
426                  numkeys, enctype, keysize);
427         if (WRITE_BYTES(&p, end, enctype)) goto out_err;
428         if (WRITE_BYTES(&p, end, keysize)) goto out_err;
429         if (WRITE_BYTES(&p, end, numkeys)) goto out_err;
430
431         if (lctx->protocol == 0) {
432                 /* derive and send down: Ke, Ki, and Kc */
433                 /* Ke */
434                 if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data,
435                                 lctx->rfc1964_kd.ctx_key.length))
436                         goto out_err;
437
438                 /* Ki */
439                 if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data,
440                                 lctx->rfc1964_kd.ctx_key.length))
441                         goto out_err;
442
443                 /* Kc */
444                 /*
445                  * RC4 is special, it dosen't need key derivation. Actually
446                  * the Ke is based on plain text. Here we just let all three
447                  * key identical, kernel will handle everything. --ericm
448                  */
449                 if (lctx->rfc1964_kd.ctx_key.type == ENCTYPE_ARCFOUR_HMAC) {
450                         if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data,
451                                         lctx->rfc1964_kd.ctx_key.length))
452                                 goto out_err;
453                 } else {
454                         if (derive_key_lucid(&lctx->rfc1964_kd.ctx_key,
455                                         &derived_key,
456                                         KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM))
457                                 goto out_err;
458                         if (write_bytes(&p, end, derived_key.data,
459                                         derived_key.length))
460                                 goto out_err;
461                         free(derived_key.data);
462                 }
463         } else {
464                 gss_krb5_lucid_key_t *keyptr;
465                 uint32_t sign_usage, seal_usage;
466
467                 if (lctx->cfx_kd.have_acceptor_subkey)
468                         keyptr = &lctx->cfx_kd.acceptor_subkey;
469                 else
470                         keyptr = &lctx->cfx_kd.ctx_key;
471
472 #if 0
473                 if (lctx->initiate == 1) {
474                         sign_usage = KG_USAGE_INITIATOR_SIGN;
475                         seal_usage = KG_USAGE_INITIATOR_SEAL;
476                 } else {
477                         sign_usage = KG_USAGE_ACCEPTOR_SIGN;
478                         seal_usage = KG_USAGE_ACCEPTOR_SEAL;
479                 }
480 #else
481                 /* FIXME
482                  * These are from rfc4142, but I don't understand: if we supply
483                  * different 'usage' value for client & server, then the peers
484                  * will have different derived keys. How could this work?
485                  *
486                  * Here we simply use old SIGN/SEAL values until we find the
487                  * answer.  --ericm
488                  * FIXME
489                  */
490                 sign_usage = KG_USAGE_SIGN;
491                 seal_usage = KG_USAGE_SEAL;
492 #endif
493
494                 /* derive and send down: Ke, Ki, and Kc */
495
496                 /* Ke */
497                 if (derive_key_lucid(keyptr, &derived_key,
498                                seal_usage, KEY_USAGE_SEED_ENCRYPTION))
499                         goto out_err;
500                 if (write_bytes(&p, end, derived_key.data,
501                                 derived_key.length))
502                         goto out_err;
503                 free(derived_key.data);
504
505                 /* Ki */
506                 if (derive_key_lucid(keyptr, &derived_key,
507                                seal_usage, KEY_USAGE_SEED_INTEGRITY))
508                         goto out_err;
509                 if (write_bytes(&p, end, derived_key.data,
510                                 derived_key.length))
511                         goto out_err;
512                 free(derived_key.data);
513
514                 /* Kc */
515                 if (derive_key_lucid(keyptr, &derived_key,
516                                sign_usage, KEY_USAGE_SEED_CHECKSUM))
517                         goto out_err;
518                 if (write_bytes(&p, end, derived_key.data,
519                                 derived_key.length))
520                         goto out_err;
521                 free(derived_key.data);
522         }
523
524         buf->length = p - (char *)buf->value;
525         return 0;
526
527 out_err:
528         printerr(0, "ERROR: %s: failed serializing krb5 context for kernel\n",
529                  __FUNCTION__);
530         if (buf->value) {
531                 free(buf->value);
532                 buf->value = NULL;
533         }
534         buf->length = 0;
535         if (enc_key.data) {
536                 free(enc_key.data);
537                 enc_key.data = NULL;
538         }
539         return -1;
540 }
541 int
542 serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
543 {
544         OM_uint32 maj_stat, min_stat;
545         void *return_ctx = 0;
546         OM_uint32 vers;
547         gss_krb5_lucid_context_v1_t *lctx = 0;
548         int retcode = 0;
549
550         printerr(3, "lucid version!\n");
551         maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx,
552                                                 1, &return_ctx);
553         if (maj_stat != GSS_S_COMPLETE) {
554                 pgsserr("gss_export_lucid_sec_context",
555                         maj_stat, min_stat, &krb5oid);
556                 goto out_err;
557         }
558
559         /* Check the version returned, we only support v1 right now */
560         vers = ((gss_krb5_lucid_context_version_t *)return_ctx)->version;
561         switch (vers) {
562         case 1:
563                 lctx = (gss_krb5_lucid_context_v1_t *) return_ctx;
564                 break;
565         default:
566                 printerr(0, "ERROR: unsupported lucid sec context version %d\n",
567                         vers);
568                 goto out_err;
569                 break;
570         }
571
572         /*
573          * Now lctx points to a lucid context that we can send down to kernel
574          *
575          * Note: we send down different information to the kernel depending
576          * on the protocol version and the enctyption type.
577          * For protocol version 0 with all enctypes besides DES3, we use
578          * the original format.  For protocol version != 0 or DES3, we
579          * send down the new style information.
580          */
581
582         if (lctx->protocol == 0 && lctx->rfc1964_kd.ctx_key.type <= 4)
583                 retcode = prepare_krb5_rfc1964_buffer(lctx, buf);
584         else
585                 retcode = prepare_krb5_rfc4121_buffer(lctx, buf);
586
587         maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx);
588         if (maj_stat != GSS_S_COMPLETE) {
589                 pgsserr("gss_export_lucid_sec_context",
590                         maj_stat, min_stat, &krb5oid);
591                 printerr(0, "WARN: failed to free lucid sec context\n");
592         }
593
594         if (retcode) {
595                 printerr(1, "%s: prepare_krb5_*_buffer failed (retcode = %d)\n",
596                          __FUNCTION__, retcode);
597                 goto out_err;
598         }
599
600         return 0;
601
602 out_err:
603         printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
604         return -1;
605 }
606
607
608
609 #endif /* HAVE_LUCID_CONTEXT_SUPPORT */