root/source3/client/cifs.upcall.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. k5_data_equal
  2. get_tgt_time
  3. krb5cc_filter
  4. find_krb5_cc
  5. handle_krb5_mech
  6. decode_key_description
  7. cifs_resolver
  8. convert_inet6_addr
  9. ip_to_fqdn
  10. usage
  11. main

   1 /*
   2 * CIFS user-space helper.
   3 * Copyright (C) Igor Mammedov (niallain@gmail.com) 2007
   4 * Copyright (C) Jeff Layton (jlayton@redhat.com) 2009
   5 *
   6 * Used by /sbin/request-key for handling
   7 * cifs upcall for kerberos authorization of access to share and
   8 * cifs upcall for DFS srver name resolving (IPv4/IPv6 aware).
   9 * You should have keyutils installed and add something like the
  10 * following lines to /etc/request-key.conf file:
  11 
  12 create cifs.spnego * * /usr/local/sbin/cifs.upcall %k
  13 create dns_resolver * * /usr/local/sbin/cifs.upcall %k
  14 
  15 * This program is free software; you can redistribute it and/or modify
  16 * it under the terms of the GNU General Public License as published by
  17 * the Free Software Foundation; either version 2 of the License, or
  18 * (at your option) any later version.
  19 * This program is distributed in the hope that it will be useful,
  20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22 * GNU General Public License for more details.
  23 * You should have received a copy of the GNU General Public License
  24 * along with this program; if not, write to the Free Software
  25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  26 */
  27 
  28 #include "includes.h"
  29 #include <keyutils.h>
  30 #include <getopt.h>
  31 
  32 #include "cifs_spnego.h"
  33 
  34 #define CIFS_DEFAULT_KRB5_DIR           "/tmp"
  35 #define CIFS_DEFAULT_KRB5_PREFIX        "krb5cc_"
  36 
  37 #define MAX_CCNAME_LEN                  PATH_MAX + 5
  38 
  39 const char *CIFSSPNEGO_VERSION = "1.3";
  40 static const char *prog = "cifs.upcall";
  41 typedef enum _sectype {
  42         NONE = 0,
  43         KRB5,
  44         MS_KRB5
  45 } sectype_t;
  46 
  47 static inline int
  48 k5_data_equal(krb5_data d1, krb5_data d2, unsigned int length)
     /* [<][>][^][v][top][bottom][index][help] */
  49 {
  50         if (!length)
  51                 length = d1.length;
  52 
  53         return (d1.length == length &&
  54                 d1.length == d2.length &&
  55                 memcmp(d1.data, d2.data, length) == 0);
  56 
  57 }
  58 
  59 /* does the ccache have a valid TGT? */
  60 static time_t
  61 get_tgt_time(const char *ccname) {
     /* [<][>][^][v][top][bottom][index][help] */
  62         krb5_context context;
  63         krb5_ccache ccache;
  64         krb5_cc_cursor cur;
  65         krb5_creds creds;
  66         krb5_principal principal;
  67         krb5_data tgt = { .data =       "krbtgt",
  68                           .length =     6 };
  69         time_t credtime = 0;
  70 
  71         if (krb5_init_context(&context)) {
  72                 syslog(LOG_DEBUG, "%s: unable to init krb5 context", __func__);
  73                 return 0;
  74         }
  75 
  76         if (krb5_cc_resolve(context, ccname, &ccache)) {
  77                 syslog(LOG_DEBUG, "%s: unable to resolve krb5 cache", __func__);
  78                 goto err_cache;
  79         }
  80 
  81         if (krb5_cc_set_flags(context, ccache, 0)) {
  82                 syslog(LOG_DEBUG, "%s: unable to set flags", __func__);
  83                 goto err_cache;
  84         }
  85 
  86         if (krb5_cc_get_principal(context, ccache, &principal)) {
  87                 syslog(LOG_DEBUG, "%s: unable to get principal", __func__);
  88                 goto err_princ;
  89         }
  90 
  91         if (krb5_cc_start_seq_get(context, ccache, &cur)) {
  92                 syslog(LOG_DEBUG, "%s: unable to seq start", __func__);
  93                 goto err_ccstart;
  94         }
  95 
  96         while (!credtime && !krb5_cc_next_cred(context, ccache, &cur, &creds)) {
  97                 if (k5_data_equal(creds.server->realm, principal->realm, 0) &&
  98                     k5_data_equal(creds.server->data[0], tgt, tgt.length) &&
  99                     k5_data_equal(creds.server->data[1], principal->realm, 0) &&
 100                     creds.times.endtime > time(NULL))
 101                         credtime = creds.times.endtime;
 102                 krb5_free_cred_contents(context, &creds);
 103         }
 104         krb5_cc_end_seq_get(context, ccache, &cur);
 105 
 106 err_ccstart:
 107         krb5_free_principal(context, principal);
 108 err_princ:
 109         krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
 110         krb5_cc_close(context, ccache);
 111 err_cache:
 112         krb5_free_context(context);
 113         return credtime;
 114 }
 115 
 116 static int
 117 krb5cc_filter(const struct dirent *dirent)
     /* [<][>][^][v][top][bottom][index][help] */
 118 {
 119         if (strstr(dirent->d_name, CIFS_DEFAULT_KRB5_PREFIX))
 120                 return 1;
 121         else
 122                 return 0;
 123 }
 124 
 125 /* search for a credcache that looks like a likely candidate */
 126 static char *
 127 find_krb5_cc(const char *dirname, uid_t uid)
     /* [<][>][^][v][top][bottom][index][help] */
 128 {
 129         struct dirent **namelist;
 130         struct stat sbuf;
 131         char ccname[MAX_CCNAME_LEN], *credpath, *best_cache = NULL;
 132         int i, n;
 133         time_t cred_time, best_time = 0;
 134 
 135         n = scandir(dirname, &namelist, krb5cc_filter, NULL);
 136         if (n < 0) {
 137                 syslog(LOG_DEBUG, "%s: scandir error on directory '%s': %s",
 138                                   __func__, dirname, strerror(errno));
 139                 return NULL;
 140         }
 141 
 142         for (i = 0; i < n; i++) {
 143                 snprintf(ccname, sizeof(ccname), "FILE:%s/%s", dirname,
 144                          namelist[i]->d_name);
 145                 credpath = ccname + 5;
 146                 syslog(LOG_DEBUG, "%s: considering %s", __func__, credpath);
 147 
 148                 if (lstat(credpath, &sbuf)) {
 149                         syslog(LOG_DEBUG, "%s: stat error on '%s': %s",
 150                                           __func__, credpath, strerror(errno));
 151                         free(namelist[i]);
 152                         continue;
 153                 }
 154                 if (sbuf.st_uid != uid) {
 155                         syslog(LOG_DEBUG, "%s: %s is owned by %u, not %u",
 156                                         __func__, credpath, sbuf.st_uid, uid);
 157                         free(namelist[i]);
 158                         continue;
 159                 }
 160                 if (!S_ISREG(sbuf.st_mode)) {
 161                         syslog(LOG_DEBUG, "%s: %s is not a regular file",
 162                                         __func__, credpath);
 163                         free(namelist[i]);
 164                         continue;
 165                 }
 166                 if (!(cred_time = get_tgt_time(ccname))) {
 167                         syslog(LOG_DEBUG, "%s: %s is not a valid credcache.",
 168                                         __func__, ccname);
 169                         free(namelist[i]);
 170                         continue;
 171                 }
 172 
 173                 if (cred_time <= best_time) {
 174                         syslog(LOG_DEBUG, "%s: %s expires sooner than current "
 175                                           "best.", __func__, ccname);
 176                         free(namelist[i]);
 177                         continue;
 178                 }
 179 
 180                 syslog(LOG_DEBUG, "%s: %s is valid ccache", __func__, ccname);
 181                 free(best_cache);
 182                 best_cache = SMB_STRNDUP(ccname, MAX_CCNAME_LEN);
 183                 best_time = cred_time;
 184                 free(namelist[i]);
 185         }
 186         free(namelist);
 187 
 188         return best_cache;
 189 }
 190 
 191 /*
 192  * Prepares AP-REQ data for mechToken and gets session key
 193  * Uses credentials from cache. It will not ask for password
 194  * you should receive credentials for yuor name manually using
 195  * kinit or whatever you wish.
 196  *
 197  * in:
 198  *      oid -           string with OID/ Could be OID_KERBEROS5
 199  *                      or OID_KERBEROS5_OLD
 200  *      principal -     Service name.
 201  *                      Could be "cifs/FQDN" for KRB5 OID
 202  *                      or for MS_KRB5 OID style server principal
 203  *                      like "pdc$@YOUR.REALM.NAME"
 204  *
 205  * out:
 206  *      secblob -       pointer for spnego wrapped AP-REQ data to be stored
 207  *      sess_key-       pointer for SessionKey data to be stored
 208  *
 209  * ret: 0 - success, others - failure
 210  */
 211 static int
 212 handle_krb5_mech(const char *oid, const char *principal, DATA_BLOB *secblob,
     /* [<][>][^][v][top][bottom][index][help] */
 213                  DATA_BLOB *sess_key, const char *ccname)
 214 {
 215         int retval;
 216         DATA_BLOB tkt, tkt_wrapped;
 217 
 218         syslog(LOG_DEBUG, "%s: getting service ticket for %s", __func__,
 219                           principal);
 220 
 221         /* get a kerberos ticket for the service and extract the session key */
 222         retval = cli_krb5_get_ticket(principal, 0, &tkt, sess_key, 0, ccname,
 223                                      NULL);
 224 
 225         if (retval) {
 226                 syslog(LOG_DEBUG, "%s: failed to obtain service ticket (%d)",
 227                                   __func__, retval);
 228                 return retval;
 229         }
 230 
 231         syslog(LOG_DEBUG, "%s: obtained service ticket", __func__);
 232 
 233         /* wrap that up in a nice GSS-API wrapping */
 234         tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
 235 
 236         /* and wrap that in a shiny SPNEGO wrapper */
 237         *secblob = gen_negTokenInit(oid, tkt_wrapped);
 238 
 239         data_blob_free(&tkt_wrapped);
 240         data_blob_free(&tkt);
 241         return retval;
 242 }
 243 
 244 #define DKD_HAVE_HOSTNAME       0x1
 245 #define DKD_HAVE_VERSION        0x2
 246 #define DKD_HAVE_SEC            0x4
 247 #define DKD_HAVE_IP             0x8
 248 #define DKD_HAVE_UID            0x10
 249 #define DKD_HAVE_PID            0x20
 250 #define DKD_MUSTHAVE_SET (DKD_HAVE_HOSTNAME|DKD_HAVE_VERSION|DKD_HAVE_SEC)
 251 
 252 struct decoded_args {
 253         int             ver;
 254         char            *hostname;
 255         char            *ip;
 256         uid_t           uid;
 257         pid_t           pid;
 258         sectype_t       sec;
 259 };
 260 
 261 static unsigned int
 262 decode_key_description(const char *desc, struct decoded_args *arg)
     /* [<][>][^][v][top][bottom][index][help] */
 263 {
 264         int len;
 265         int retval = 0;
 266         char *pos;
 267         const char *tkn = desc;
 268 
 269         do {
 270                 pos = index(tkn, ';');
 271                 if (strncmp(tkn, "host=", 5) == 0) {
 272 
 273                         if (pos == NULL)
 274                                 len = strlen(tkn);
 275                         else
 276                                 len = pos - tkn;
 277 
 278                         len -= 4;
 279                         SAFE_FREE(arg->hostname);
 280                         arg->hostname = SMB_XMALLOC_ARRAY(char, len);
 281                         strlcpy(arg->hostname, tkn + 5, len);
 282                         retval |= DKD_HAVE_HOSTNAME;
 283                 } else if (!strncmp(tkn, "ip4=", 4) ||
 284                            !strncmp(tkn, "ip6=", 4)) {
 285                         if (pos == NULL)
 286                                 len = strlen(tkn);
 287                         else
 288                                 len = pos - tkn;
 289 
 290                         len -= 3;
 291                         SAFE_FREE(arg->ip);
 292                         arg->ip = SMB_XMALLOC_ARRAY(char, len);
 293                         strlcpy(arg->ip, tkn + 4, len);
 294                         retval |= DKD_HAVE_IP;
 295                 } else if (strncmp(tkn, "pid=", 4) == 0) {
 296                         errno = 0;
 297                         arg->pid = strtol(tkn + 4, NULL, 0);
 298                         if (errno != 0) {
 299                                 syslog(LOG_ERR, "Invalid pid format: %s",
 300                                        strerror(errno));
 301                                 return 1;
 302                         } else {
 303                                 retval |= DKD_HAVE_PID;
 304                         }
 305                 } else if (strncmp(tkn, "sec=", 4) == 0) {
 306                         if (strncmp(tkn + 4, "krb5", 4) == 0) {
 307                                 retval |= DKD_HAVE_SEC;
 308                                 arg->sec = KRB5;
 309                         } else if (strncmp(tkn + 4, "mskrb5", 6) == 0) {
 310                                 retval |= DKD_HAVE_SEC;
 311                                 arg->sec = MS_KRB5;
 312                         }
 313                 } else if (strncmp(tkn, "uid=", 4) == 0) {
 314                         errno = 0;
 315                         arg->uid = strtol(tkn + 4, NULL, 16);
 316                         if (errno != 0) {
 317                                 syslog(LOG_ERR, "Invalid uid format: %s",
 318                                        strerror(errno));
 319                                 return 1;
 320                         } else {
 321                                 retval |= DKD_HAVE_UID;
 322                         }
 323                 } else if (strncmp(tkn, "ver=", 4) == 0) {      /* if version */
 324                         errno = 0;
 325                         arg->ver = strtol(tkn + 4, NULL, 16);
 326                         if (errno != 0) {
 327                                 syslog(LOG_ERR, "Invalid version format: %s",
 328                                        strerror(errno));
 329                                 return 1;
 330                         } else {
 331                                 retval |= DKD_HAVE_VERSION;
 332                         }
 333                 }
 334                 if (pos == NULL)
 335                         break;
 336                 tkn = pos + 1;
 337         } while (tkn);
 338         return retval;
 339 }
 340 
 341 static int
 342 cifs_resolver(const key_serial_t key, const char *key_descr)
     /* [<][>][^][v][top][bottom][index][help] */
 343 {
 344         int c;
 345         struct addrinfo *addr;
 346         char ip[INET6_ADDRSTRLEN];
 347         void *p;
 348         const char *keyend = key_descr;
 349         /* skip next 4 ';' delimiters to get to description */
 350         for (c = 1; c <= 4; c++) {
 351                 keyend = index(keyend+1, ';');
 352                 if (!keyend) {
 353                         syslog(LOG_ERR, "invalid key description: %s",
 354                                         key_descr);
 355                         return 1;
 356                 }
 357         }
 358         keyend++;
 359 
 360         /* resolve name to ip */
 361         c = getaddrinfo(keyend, NULL, NULL, &addr);
 362         if (c) {
 363                 syslog(LOG_ERR, "unable to resolve hostname: %s [%s]",
 364                                 keyend, gai_strerror(c));
 365                 return 1;
 366         }
 367 
 368         /* conver ip to string form */
 369         if (addr->ai_family == AF_INET)
 370                 p = &(((struct sockaddr_in *)addr->ai_addr)->sin_addr);
 371         else
 372                 p = &(((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr);
 373 
 374         if (!inet_ntop(addr->ai_family, p, ip, sizeof(ip))) {
 375                 syslog(LOG_ERR, "%s: inet_ntop: %s", __func__, strerror(errno));
 376                 freeaddrinfo(addr);
 377                 return 1;
 378         }
 379 
 380         /* setup key */
 381         c = keyctl_instantiate(key, ip, strlen(ip)+1, 0);
 382         if (c == -1) {
 383                 syslog(LOG_ERR, "%s: keyctl_instantiate: %s", __func__,
 384                                 strerror(errno));
 385                 freeaddrinfo(addr);
 386                 return 1;
 387         }
 388 
 389         freeaddrinfo(addr);
 390         return 0;
 391 }
 392 
 393 /*
 394  * Older kernels sent IPv6 addresses without colons. Well, at least
 395  * they're fixed-length strings. Convert these addresses to have colon
 396  * delimiters to make getaddrinfo happy.
 397  */
 398 static void
 399 convert_inet6_addr(const char *from, char *to)
     /* [<][>][^][v][top][bottom][index][help] */
 400 {
 401         int i = 1;
 402 
 403         while (*from) {
 404                 *to++ = *from++;
 405                 if (!(i++ % 4) && *from)
 406                         *to++ = ':';
 407         }
 408         *to = 0;
 409 }
 410 
 411 static int
 412 ip_to_fqdn(const char *addrstr, char *host, size_t hostlen)
     /* [<][>][^][v][top][bottom][index][help] */
 413 {
 414         int rc;
 415         struct addrinfo hints = { .ai_flags = AI_NUMERICHOST };
 416         struct addrinfo *res;
 417         const char *ipaddr = addrstr;
 418         char converted[INET6_ADDRSTRLEN + 1];
 419 
 420         if ((strlen(ipaddr) > INET_ADDRSTRLEN) && !strchr(ipaddr, ':')) {
 421                 convert_inet6_addr(ipaddr, converted);
 422                 ipaddr = converted;
 423         }
 424 
 425         rc = getaddrinfo(ipaddr, NULL, &hints, &res);
 426         if (rc) {
 427                 syslog(LOG_DEBUG, "%s: failed to resolve %s to "
 428                         "ipaddr: %s", __func__, ipaddr,
 429                 rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc));
 430                 return rc;
 431         }
 432 
 433         rc = getnameinfo(res->ai_addr, res->ai_addrlen, host, hostlen,
 434                          NULL, 0, NI_NAMEREQD);
 435         freeaddrinfo(res);
 436         if (rc) {
 437                 syslog(LOG_DEBUG, "%s: failed to resolve %s to fqdn: %s",
 438                         __func__, ipaddr,
 439                         rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc));
 440                 return rc;
 441         }
 442 
 443         syslog(LOG_DEBUG, "%s: resolved %s to %s", __func__, ipaddr, host);
 444         return 0;
 445 }
 446 
 447 static void
 448 usage(void)
     /* [<][>][^][v][top][bottom][index][help] */
 449 {
 450         syslog(LOG_INFO, "Usage: %s [-t] [-v] key_serial", prog);
 451         fprintf(stderr, "Usage: %s [-t] [-v] key_serial\n", prog);
 452 }
 453 
 454 const struct option long_options[] = {
 455         { "trust-dns",  0, NULL, 't' },
 456         { "version",    0, NULL, 'v' },
 457         { NULL,         0, NULL, 0 }
 458 };
 459 
 460 int main(const int argc, char *const argv[])
     /* [<][>][^][v][top][bottom][index][help] */
 461 {
 462         struct cifs_spnego_msg *keydata = NULL;
 463         DATA_BLOB secblob = data_blob_null;
 464         DATA_BLOB sess_key = data_blob_null;
 465         key_serial_t key = 0;
 466         size_t datalen;
 467         unsigned int have;
 468         long rc = 1;
 469         int c, try_dns = 0;
 470         char *buf, *princ = NULL, *ccname = NULL;
 471         char hostbuf[NI_MAXHOST], *host;
 472         struct decoded_args arg = { };
 473         const char *oid;
 474 
 475         hostbuf[0] = '\0';
 476 
 477         openlog(prog, 0, LOG_DAEMON);
 478 
 479         while ((c = getopt_long(argc, argv, "ctv", long_options, NULL)) != -1) {
 480                 switch (c) {
 481                 case 'c':
 482                         /* legacy option -- skip it */
 483                         break;
 484                 case 't':
 485                         try_dns++;
 486                         break;
 487                 case 'v':
 488                         printf("version: %s\n", CIFSSPNEGO_VERSION);
 489                         goto out;
 490                 default:
 491                         syslog(LOG_ERR, "unknown option: %c", c);
 492                         goto out;
 493                 }
 494         }
 495 
 496         /* is there a key? */
 497         if (argc <= optind) {
 498                 usage();
 499                 goto out;
 500         }
 501 
 502         /* get key and keyring values */
 503         errno = 0;
 504         key = strtol(argv[optind], NULL, 10);
 505         if (errno != 0) {
 506                 key = 0;
 507                 syslog(LOG_ERR, "Invalid key format: %s", strerror(errno));
 508                 goto out;
 509         }
 510 
 511         rc = keyctl_describe_alloc(key, &buf);
 512         if (rc == -1) {
 513                 syslog(LOG_ERR, "keyctl_describe_alloc failed: %s",
 514                        strerror(errno));
 515                 rc = 1;
 516                 goto out;
 517         }
 518 
 519         syslog(LOG_DEBUG, "key description: %s", buf);
 520 
 521         if ((strncmp(buf, "cifs.resolver", sizeof("cifs.resolver")-1) == 0) ||
 522             (strncmp(buf, "dns_resolver", sizeof("dns_resolver")-1) == 0)) {
 523                 rc = cifs_resolver(key, buf);
 524                 goto out;
 525         }
 526 
 527         have = decode_key_description(buf, &arg);
 528         SAFE_FREE(buf);
 529         if ((have & DKD_MUSTHAVE_SET) != DKD_MUSTHAVE_SET) {
 530                 syslog(LOG_ERR, "unable to get necessary params from key "
 531                                 "description (0x%x)", have);
 532                 rc = 1;
 533                 goto out;
 534         }
 535 
 536         if (arg.ver > CIFS_SPNEGO_UPCALL_VERSION) {
 537                 syslog(LOG_ERR, "incompatible kernel upcall version: 0x%x",
 538                                 arg.ver);
 539                 rc = 1;
 540                 goto out;
 541         }
 542 
 543         if (have & DKD_HAVE_UID) {
 544                 rc = setuid(arg.uid);
 545                 if (rc == -1) {
 546                         syslog(LOG_ERR, "setuid: %s", strerror(errno));
 547                         goto out;
 548                 }
 549 
 550                 ccname = find_krb5_cc(CIFS_DEFAULT_KRB5_DIR, arg.uid);
 551         }
 552 
 553         host = arg.hostname;
 554 
 555         // do mech specific authorization
 556         switch (arg.sec) {
 557         case MS_KRB5:
 558         case KRB5:
 559 retry_new_hostname:
 560                 /* for "cifs/" service name + terminating 0 */
 561                 datalen = strlen(host) + 5 + 1;
 562                 princ = SMB_XMALLOC_ARRAY(char, datalen);
 563                 if (!princ) {
 564                         rc = -ENOMEM;
 565                         break;
 566                 }
 567 
 568                 if (arg.sec == MS_KRB5)
 569                         oid = OID_KERBEROS5_OLD;
 570                 else
 571                         oid = OID_KERBEROS5;
 572 
 573                 /*
 574                  * try getting a cifs/ principal first and then fall back to
 575                  * getting a host/ principal if that doesn't work.
 576                  */
 577                 strlcpy(princ, "cifs/", datalen);
 578                 strlcpy(princ + 5, host, datalen - 5);
 579                 rc = handle_krb5_mech(oid, princ, &secblob, &sess_key, ccname);
 580                 if (!rc)
 581                         break;
 582 
 583                 memcpy(princ, "host/", 5);
 584                 rc = handle_krb5_mech(oid, princ, &secblob, &sess_key, ccname);
 585                 if (!rc)
 586                         break;
 587 
 588                 if (!try_dns || !(have & DKD_HAVE_IP))
 589                         break;
 590 
 591                 rc = ip_to_fqdn(arg.ip, hostbuf, sizeof(hostbuf));
 592                 if (rc)
 593                         break;
 594 
 595                 SAFE_FREE(princ);
 596                 try_dns = 0;
 597                 host = hostbuf;
 598                 goto retry_new_hostname;
 599         default:
 600                 syslog(LOG_ERR, "sectype: %d is not implemented", arg.sec);
 601                 rc = 1;
 602                 break;
 603         }
 604 
 605         SAFE_FREE(princ);
 606 
 607         if (rc)
 608                 goto out;
 609 
 610         /* pack SecurityBLob and SessionKey into downcall packet */
 611         datalen =
 612             sizeof(struct cifs_spnego_msg) + secblob.length + sess_key.length;
 613         keydata = (struct cifs_spnego_msg*)SMB_XMALLOC_ARRAY(char, datalen);
 614         if (!keydata) {
 615                 rc = 1;
 616                 goto out;
 617         }
 618         keydata->version = arg.ver;
 619         keydata->flags = 0;
 620         keydata->sesskey_len = sess_key.length;
 621         keydata->secblob_len = secblob.length;
 622         memcpy(&(keydata->data), sess_key.data, sess_key.length);
 623         memcpy(&(keydata->data) + keydata->sesskey_len,
 624                secblob.data, secblob.length);
 625 
 626         /* setup key */
 627         rc = keyctl_instantiate(key, keydata, datalen, 0);
 628         if (rc == -1) {
 629                 syslog(LOG_ERR, "keyctl_instantiate: %s", strerror(errno));
 630                 goto out;
 631         }
 632 
 633         /* BB: maybe we need use timeout for key: for example no more then
 634          * ticket lifietime? */
 635         /* keyctl_set_timeout( key, 60); */
 636 out:
 637         /*
 638          * on error, negatively instantiate the key ourselves so that we can
 639          * make sure the kernel doesn't hang it off of a searchable keyring
 640          * and interfere with the next attempt to instantiate the key.
 641          */
 642         if (rc != 0  && key == 0)
 643                 keyctl_negate(key, 1, KEY_REQKEY_DEFL_DEFAULT);
 644         data_blob_free(&secblob);
 645         data_blob_free(&sess_key);
 646         SAFE_FREE(ccname);
 647         SAFE_FREE(arg.hostname);
 648         SAFE_FREE(arg.ip);
 649         SAFE_FREE(keydata);
 650         return rc;
 651 }

/* [<][>][^][v][top][bottom][index][help] */