Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gss_krb5_wrap.c
Go to the documentation of this file.
1 /*
2  * COPYRIGHT (c) 2008
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 <linux/types.h>
32 #include <linux/jiffies.h>
33 #include <linux/sunrpc/gss_krb5.h>
34 #include <linux/random.h>
35 #include <linux/pagemap.h>
36 #include <linux/crypto.h>
37 
38 #ifdef RPC_DEBUG
39 # define RPCDBG_FACILITY RPCDBG_AUTH
40 #endif
41 
42 static inline int
43 gss_krb5_padding(int blocksize, int length)
44 {
45  return blocksize - (length % blocksize);
46 }
47 
48 static inline void
49 gss_krb5_add_padding(struct xdr_buf *buf, int offset, int blocksize)
50 {
51  int padding = gss_krb5_padding(blocksize, buf->len - offset);
52  char *p;
53  struct kvec *iov;
54 
55  if (buf->page_len || buf->tail[0].iov_len)
56  iov = &buf->tail[0];
57  else
58  iov = &buf->head[0];
59  p = iov->iov_base + iov->iov_len;
60  iov->iov_len += padding;
61  buf->len += padding;
62  memset(p, padding, padding);
63 }
64 
65 static inline int
66 gss_krb5_remove_padding(struct xdr_buf *buf, int blocksize)
67 {
68  u8 *ptr;
69  u8 pad;
70  size_t len = buf->len;
71 
72  if (len <= buf->head[0].iov_len) {
73  pad = *(u8 *)(buf->head[0].iov_base + len - 1);
74  if (pad > buf->head[0].iov_len)
75  return -EINVAL;
76  buf->head[0].iov_len -= pad;
77  goto out;
78  } else
79  len -= buf->head[0].iov_len;
80  if (len <= buf->page_len) {
81  unsigned int last = (buf->page_base + len - 1)
83  unsigned int offset = (buf->page_base + len - 1)
84  & (PAGE_CACHE_SIZE - 1);
85  ptr = kmap_atomic(buf->pages[last]);
86  pad = *(ptr + offset);
87  kunmap_atomic(ptr);
88  goto out;
89  } else
90  len -= buf->page_len;
91  BUG_ON(len > buf->tail[0].iov_len);
92  pad = *(u8 *)(buf->tail[0].iov_base + len - 1);
93 out:
94  /* XXX: NOTE: we do not adjust the page lengths--they represent
95  * a range of data in the real filesystem page cache, and we need
96  * to know that range so the xdr code can properly place read data.
97  * However adjusting the head length, as we do above, is harmless.
98  * In the case of a request that fits into a single page, the server
99  * also uses length and head length together to determine the original
100  * start of the request to copy the request for deferal; so it's
101  * easier on the server if we adjust head and tail length in tandem.
102  * It's not really a problem that we don't fool with the page and
103  * tail lengths, though--at worst badly formed xdr might lead the
104  * server to attempt to parse the padding.
105  * XXX: Document all these weird requirements for gss mechanism
106  * wrap/unwrap functions. */
107  if (pad > blocksize)
108  return -EINVAL;
109  if (buf->len > pad)
110  buf->len -= pad;
111  else
112  return -EINVAL;
113  return 0;
114 }
115 
116 void
117 gss_krb5_make_confounder(char *p, u32 conflen)
118 {
119  static u64 i = 0;
120  u64 *q = (u64 *)p;
121 
122  /* rfc1964 claims this should be "random". But all that's really
123  * necessary is that it be unique. And not even that is necessary in
124  * our case since our "gssapi" implementation exists only to support
125  * rpcsec_gss, so we know that the only buffers we will ever encrypt
126  * already begin with a unique sequence number. Just to hedge my bets
127  * I'll make a half-hearted attempt at something unique, but ensuring
128  * uniqueness would mean worrying about atomicity and rollover, and I
129  * don't care enough. */
130 
131  /* initialize to random value */
132  if (i == 0) {
133  i = random32();
134  i = (i << 32) | random32();
135  }
136 
137  switch (conflen) {
138  case 16:
139  *q++ = i++;
140  /* fall through */
141  case 8:
142  *q++ = i++;
143  break;
144  default:
145  BUG();
146  }
147 }
148 
149 /* Assumptions: the head and tail of inbuf are ours to play with.
150  * The pages, however, may be real pages in the page cache and we replace
151  * them with scratch pages from **pages before writing to them. */
152 /* XXX: obviously the above should be documentation of wrap interface,
153  * and shouldn't be in this kerberos-specific file. */
154 
155 /* XXX factor out common code with seal/unseal. */
156 
157 static u32
158 gss_wrap_kerberos_v1(struct krb5_ctx *kctx, int offset,
159  struct xdr_buf *buf, struct page **pages)
160 {
161  char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
162  struct xdr_netobj md5cksum = {.len = sizeof(cksumdata),
163  .data = cksumdata};
164  int blocksize = 0, plainlen;
165  unsigned char *ptr, *msg_start;
166  s32 now;
167  int headlen;
168  struct page **tmp_pages;
169  u32 seq_send;
170  u8 *cksumkey;
171  u32 conflen = kctx->gk5e->conflen;
172 
173  dprintk("RPC: %s\n", __func__);
174 
175  now = get_seconds();
176 
177  blocksize = crypto_blkcipher_blocksize(kctx->enc);
178  gss_krb5_add_padding(buf, offset, blocksize);
179  BUG_ON((buf->len - offset) % blocksize);
180  plainlen = conflen + buf->len - offset;
181 
182  headlen = g_token_size(&kctx->mech_used,
183  GSS_KRB5_TOK_HDR_LEN + kctx->gk5e->cksumlength + plainlen) -
184  (buf->len - offset);
185 
186  ptr = buf->head[0].iov_base + offset;
187  /* shift data to make room for header. */
188  xdr_extend_head(buf, offset, headlen);
189 
190  /* XXX Would be cleverer to encrypt while copying. */
191  BUG_ON((buf->len - offset - headlen) % blocksize);
192 
195  kctx->gk5e->cksumlength + plainlen, &ptr);
196 
197 
198  /* ptr now at header described in rfc 1964, section 1.2.1: */
199  ptr[0] = (unsigned char) ((KG_TOK_WRAP_MSG >> 8) & 0xff);
200  ptr[1] = (unsigned char) (KG_TOK_WRAP_MSG & 0xff);
201 
202  msg_start = ptr + GSS_KRB5_TOK_HDR_LEN + kctx->gk5e->cksumlength;
203 
204  *(__be16 *)(ptr + 2) = cpu_to_le16(kctx->gk5e->signalg);
205  memset(ptr + 4, 0xff, 4);
206  *(__be16 *)(ptr + 4) = cpu_to_le16(kctx->gk5e->sealalg);
207 
208  gss_krb5_make_confounder(msg_start, conflen);
209 
210  if (kctx->gk5e->keyed_cksum)
211  cksumkey = kctx->cksum;
212  else
213  cksumkey = NULL;
214 
215  /* XXXJBF: UGH!: */
216  tmp_pages = buf->pages;
217  buf->pages = pages;
218  if (make_checksum(kctx, ptr, 8, buf, offset + headlen - conflen,
219  cksumkey, KG_USAGE_SEAL, &md5cksum))
220  return GSS_S_FAILURE;
221  buf->pages = tmp_pages;
222 
223  memcpy(ptr + GSS_KRB5_TOK_HDR_LEN, md5cksum.data, md5cksum.len);
224 
225  spin_lock(&krb5_seq_lock);
226  seq_send = kctx->seq_send++;
227  spin_unlock(&krb5_seq_lock);
228 
229  /* XXX would probably be more efficient to compute checksum
230  * and encrypt at the same time: */
231  if ((krb5_make_seq_num(kctx, kctx->seq, kctx->initiate ? 0 : 0xff,
232  seq_send, ptr + GSS_KRB5_TOK_HDR_LEN, ptr + 8)))
233  return GSS_S_FAILURE;
234 
235  if (kctx->enctype == ENCTYPE_ARCFOUR_HMAC) {
236  struct crypto_blkcipher *cipher;
237  int err;
238  cipher = crypto_alloc_blkcipher(kctx->gk5e->encrypt_name, 0,
240  if (IS_ERR(cipher))
241  return GSS_S_FAILURE;
242 
243  krb5_rc4_setup_enc_key(kctx, cipher, seq_send);
244 
245  err = gss_encrypt_xdr_buf(cipher, buf,
246  offset + headlen - conflen, pages);
247  crypto_free_blkcipher(cipher);
248  if (err)
249  return GSS_S_FAILURE;
250  } else {
251  if (gss_encrypt_xdr_buf(kctx->enc, buf,
252  offset + headlen - conflen, pages))
253  return GSS_S_FAILURE;
254  }
255 
256  return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
257 }
258 
259 static u32
260 gss_unwrap_kerberos_v1(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
261 {
262  int signalg;
263  int sealalg;
264  char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
265  struct xdr_netobj md5cksum = {.len = sizeof(cksumdata),
266  .data = cksumdata};
267  s32 now;
268  int direction;
269  s32 seqnum;
270  unsigned char *ptr;
271  int bodysize;
272  void *data_start, *orig_start;
273  int data_len;
274  int blocksize;
275  u32 conflen = kctx->gk5e->conflen;
276  int crypt_offset;
277  u8 *cksumkey;
278 
279  dprintk("RPC: gss_unwrap_kerberos\n");
280 
281  ptr = (u8 *)buf->head[0].iov_base + offset;
282  if (g_verify_token_header(&kctx->mech_used, &bodysize, &ptr,
283  buf->len - offset))
284  return GSS_S_DEFECTIVE_TOKEN;
285 
286  if ((ptr[0] != ((KG_TOK_WRAP_MSG >> 8) & 0xff)) ||
287  (ptr[1] != (KG_TOK_WRAP_MSG & 0xff)))
288  return GSS_S_DEFECTIVE_TOKEN;
289 
290  /* XXX sanity-check bodysize?? */
291 
292  /* get the sign and seal algorithms */
293 
294  signalg = ptr[2] + (ptr[3] << 8);
295  if (signalg != kctx->gk5e->signalg)
296  return GSS_S_DEFECTIVE_TOKEN;
297 
298  sealalg = ptr[4] + (ptr[5] << 8);
299  if (sealalg != kctx->gk5e->sealalg)
300  return GSS_S_DEFECTIVE_TOKEN;
301 
302  if ((ptr[6] != 0xff) || (ptr[7] != 0xff))
303  return GSS_S_DEFECTIVE_TOKEN;
304 
305  /*
306  * Data starts after token header and checksum. ptr points
307  * to the beginning of the token header
308  */
309  crypt_offset = ptr + (GSS_KRB5_TOK_HDR_LEN + kctx->gk5e->cksumlength) -
310  (unsigned char *)buf->head[0].iov_base;
311 
312  /*
313  * Need plaintext seqnum to derive encryption key for arcfour-hmac
314  */
316  ptr + 8, &direction, &seqnum))
317  return GSS_S_BAD_SIG;
318 
319  if ((kctx->initiate && direction != 0xff) ||
320  (!kctx->initiate && direction != 0))
321  return GSS_S_BAD_SIG;
322 
323  if (kctx->enctype == ENCTYPE_ARCFOUR_HMAC) {
324  struct crypto_blkcipher *cipher;
325  int err;
326 
327  cipher = crypto_alloc_blkcipher(kctx->gk5e->encrypt_name, 0,
329  if (IS_ERR(cipher))
330  return GSS_S_FAILURE;
331 
332  krb5_rc4_setup_enc_key(kctx, cipher, seqnum);
333 
334  err = gss_decrypt_xdr_buf(cipher, buf, crypt_offset);
335  crypto_free_blkcipher(cipher);
336  if (err)
337  return GSS_S_DEFECTIVE_TOKEN;
338  } else {
339  if (gss_decrypt_xdr_buf(kctx->enc, buf, crypt_offset))
340  return GSS_S_DEFECTIVE_TOKEN;
341  }
342 
343  if (kctx->gk5e->keyed_cksum)
344  cksumkey = kctx->cksum;
345  else
346  cksumkey = NULL;
347 
348  if (make_checksum(kctx, ptr, 8, buf, crypt_offset,
349  cksumkey, KG_USAGE_SEAL, &md5cksum))
350  return GSS_S_FAILURE;
351 
352  if (memcmp(md5cksum.data, ptr + GSS_KRB5_TOK_HDR_LEN,
353  kctx->gk5e->cksumlength))
354  return GSS_S_BAD_SIG;
355 
356  /* it got through unscathed. Make sure the context is unexpired */
357 
358  now = get_seconds();
359 
360  if (now > kctx->endtime)
361  return GSS_S_CONTEXT_EXPIRED;
362 
363  /* do sequencing checks */
364 
365  /* Copy the data back to the right position. XXX: Would probably be
366  * better to copy and encrypt at the same time. */
367 
368  blocksize = crypto_blkcipher_blocksize(kctx->enc);
369  data_start = ptr + (GSS_KRB5_TOK_HDR_LEN + kctx->gk5e->cksumlength) +
370  conflen;
371  orig_start = buf->head[0].iov_base + offset;
372  data_len = (buf->head[0].iov_base + buf->head[0].iov_len) - data_start;
373  memmove(orig_start, data_start, data_len);
374  buf->head[0].iov_len -= (data_start - orig_start);
375  buf->len -= (data_start - orig_start);
376 
377  if (gss_krb5_remove_padding(buf, blocksize))
378  return GSS_S_DEFECTIVE_TOKEN;
379 
380  return GSS_S_COMPLETE;
381 }
382 
383 /*
384  * We can shift data by up to LOCAL_BUF_LEN bytes in a pass. If we need
385  * to do more than that, we shift repeatedly. Kevin Coffman reports
386  * seeing 28 bytes as the value used by Microsoft clients and servers
387  * with AES, so this constant is chosen to allow handling 28 in one pass
388  * without using too much stack space.
389  *
390  * If that proves to a problem perhaps we could use a more clever
391  * algorithm.
392  */
393 #define LOCAL_BUF_LEN 32u
394 
395 static void rotate_buf_a_little(struct xdr_buf *buf, unsigned int shift)
396 {
397  char head[LOCAL_BUF_LEN];
398  char tmp[LOCAL_BUF_LEN];
399  unsigned int this_len, i;
400 
401  BUG_ON(shift > LOCAL_BUF_LEN);
402 
403  read_bytes_from_xdr_buf(buf, 0, head, shift);
404  for (i = 0; i + shift < buf->len; i += LOCAL_BUF_LEN) {
405  this_len = min(LOCAL_BUF_LEN, buf->len - (i + shift));
406  read_bytes_from_xdr_buf(buf, i+shift, tmp, this_len);
407  write_bytes_to_xdr_buf(buf, i, tmp, this_len);
408  }
409  write_bytes_to_xdr_buf(buf, buf->len - shift, head, shift);
410 }
411 
412 static void _rotate_left(struct xdr_buf *buf, unsigned int shift)
413 {
414  int shifted = 0;
415  int this_shift;
416 
417  shift %= buf->len;
418  while (shifted < shift) {
419  this_shift = min(shift - shifted, LOCAL_BUF_LEN);
420  rotate_buf_a_little(buf, this_shift);
421  shifted += this_shift;
422  }
423 }
424 
425 static void rotate_left(u32 base, struct xdr_buf *buf, unsigned int shift)
426 {
427  struct xdr_buf subbuf;
428 
429  xdr_buf_subsegment(buf, &subbuf, base, buf->len - base);
430  _rotate_left(&subbuf, shift);
431 }
432 
433 static u32
434 gss_wrap_kerberos_v2(struct krb5_ctx *kctx, u32 offset,
435  struct xdr_buf *buf, struct page **pages)
436 {
437  int blocksize;
438  u8 *ptr, *plainhdr;
439  s32 now;
440  u8 flags = 0x00;
441  __be16 *be16ptr, ec = 0;
442  __be64 *be64ptr;
443  u32 err;
444 
445  dprintk("RPC: %s\n", __func__);
446 
447  if (kctx->gk5e->encrypt_v2 == NULL)
448  return GSS_S_FAILURE;
449 
450  /* make room for gss token header */
451  if (xdr_extend_head(buf, offset, GSS_KRB5_TOK_HDR_LEN))
452  return GSS_S_FAILURE;
453 
454  /* construct gss token header */
455  ptr = plainhdr = buf->head[0].iov_base + offset;
456  *ptr++ = (unsigned char) ((KG2_TOK_WRAP>>8) & 0xff);
457  *ptr++ = (unsigned char) (KG2_TOK_WRAP & 0xff);
458 
459  if ((kctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
461  if ((kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY) != 0)
463  /* We always do confidentiality in wrap tokens */
464  flags |= KG2_TOKEN_FLAG_SEALED;
465 
466  *ptr++ = flags;
467  *ptr++ = 0xff;
468  be16ptr = (__be16 *)ptr;
469 
470  blocksize = crypto_blkcipher_blocksize(kctx->acceptor_enc);
471  *be16ptr++ = cpu_to_be16(ec);
472  /* "inner" token header always uses 0 for RRC */
473  *be16ptr++ = cpu_to_be16(0);
474 
475  be64ptr = (__be64 *)be16ptr;
476  spin_lock(&krb5_seq_lock);
477  *be64ptr = cpu_to_be64(kctx->seq_send64++);
478  spin_unlock(&krb5_seq_lock);
479 
480  err = (*kctx->gk5e->encrypt_v2)(kctx, offset, buf, ec, pages);
481  if (err)
482  return err;
483 
484  now = get_seconds();
485  return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
486 }
487 
488 static u32
489 gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
490 {
491  s32 now;
492  u64 seqnum;
493  u8 *ptr;
494  u8 flags = 0x00;
495  u16 ec, rrc;
496  int err;
497  u32 headskip, tailskip;
498  u8 decrypted_hdr[GSS_KRB5_TOK_HDR_LEN];
499  unsigned int movelen;
500 
501 
502  dprintk("RPC: %s\n", __func__);
503 
504  if (kctx->gk5e->decrypt_v2 == NULL)
505  return GSS_S_FAILURE;
506 
507  ptr = buf->head[0].iov_base + offset;
508 
509  if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_WRAP)
510  return GSS_S_DEFECTIVE_TOKEN;
511 
512  flags = ptr[2];
513  if ((!kctx->initiate && (flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)) ||
514  (kctx->initiate && !(flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)))
515  return GSS_S_BAD_SIG;
516 
517  if ((flags & KG2_TOKEN_FLAG_SEALED) == 0) {
518  dprintk("%s: token missing expected sealed flag\n", __func__);
519  return GSS_S_DEFECTIVE_TOKEN;
520  }
521 
522  if (ptr[3] != 0xff)
523  return GSS_S_DEFECTIVE_TOKEN;
524 
525  ec = be16_to_cpup((__be16 *)(ptr + 4));
526  rrc = be16_to_cpup((__be16 *)(ptr + 6));
527 
528  seqnum = be64_to_cpup((__be64 *)(ptr + 8));
529 
530  if (rrc != 0)
531  rotate_left(offset + 16, buf, rrc);
532 
533  err = (*kctx->gk5e->decrypt_v2)(kctx, offset, buf,
534  &headskip, &tailskip);
535  if (err)
536  return GSS_S_FAILURE;
537 
538  /*
539  * Retrieve the decrypted gss token header and verify
540  * it against the original
541  */
542  err = read_bytes_from_xdr_buf(buf,
543  buf->len - GSS_KRB5_TOK_HDR_LEN - tailskip,
544  decrypted_hdr, GSS_KRB5_TOK_HDR_LEN);
545  if (err) {
546  dprintk("%s: error %u getting decrypted_hdr\n", __func__, err);
547  return GSS_S_FAILURE;
548  }
549  if (memcmp(ptr, decrypted_hdr, 6)
550  || memcmp(ptr + 8, decrypted_hdr + 8, 8)) {
551  dprintk("%s: token hdr, plaintext hdr mismatch!\n", __func__);
552  return GSS_S_FAILURE;
553  }
554 
555  /* do sequencing checks */
556 
557  /* it got through unscathed. Make sure the context is unexpired */
558  now = get_seconds();
559  if (now > kctx->endtime)
560  return GSS_S_CONTEXT_EXPIRED;
561 
562  /*
563  * Move the head data back to the right position in xdr_buf.
564  * We ignore any "ec" data since it might be in the head or
565  * the tail, and we really don't need to deal with it.
566  * Note that buf->head[0].iov_len may indicate the available
567  * head buffer space rather than that actually occupied.
568  */
569  movelen = min_t(unsigned int, buf->head[0].iov_len, buf->len);
570  movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip;
571  BUG_ON(offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen >
572  buf->head[0].iov_len);
573  memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen);
574  buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
575  buf->len -= GSS_KRB5_TOK_HDR_LEN + headskip;
576 
577  return GSS_S_COMPLETE;
578 }
579 
580 u32
581 gss_wrap_kerberos(struct gss_ctx *gctx, int offset,
582  struct xdr_buf *buf, struct page **pages)
583 {
584  struct krb5_ctx *kctx = gctx->internal_ctx_id;
585 
586  switch (kctx->enctype) {
587  default:
588  BUG();
589  case ENCTYPE_DES_CBC_RAW:
591  case ENCTYPE_ARCFOUR_HMAC:
592  return gss_wrap_kerberos_v1(kctx, offset, buf, pages);
595  return gss_wrap_kerberos_v2(kctx, offset, buf, pages);
596  }
597 }
598 
599 u32
600 gss_unwrap_kerberos(struct gss_ctx *gctx, int offset, struct xdr_buf *buf)
601 {
602  struct krb5_ctx *kctx = gctx->internal_ctx_id;
603 
604  switch (kctx->enctype) {
605  default:
606  BUG();
607  case ENCTYPE_DES_CBC_RAW:
609  case ENCTYPE_ARCFOUR_HMAC:
610  return gss_unwrap_kerberos_v1(kctx, offset, buf);
613  return gss_unwrap_kerberos_v2(kctx, offset, buf);
614  }
615 }
616