Ignore:
Timestamp:
Jan 15, 2010, 8:23:30 AM (16 years ago)
Author:
Herwig Bauernfeind
Message:

Update Samba 3.3 to 3.3.10 (source)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/samba-3.3.x/source/client/cifs.upcall.c

    r206 r370  
    22* CIFS user-space helper.
    33* Copyright (C) Igor Mammedov ([email protected]) 2007
     4
    45*
    56* Used by /sbin/request-key for handling
     
    2627
    2728#include "includes.h"
     29
    2830#include <keyutils.h>
     31
    2932
    3033#include "cifs_spnego.h"
    3134
    32 const char *CIFSSPNEGO_VERSION = "1.2";
     35#define CIFS_DEFAULT_KRB5_DIR           "/tmp"
     36#define CIFS_DEFAULT_KRB5_PREFIX        "krb5cc_"
     37
     38#define MAX_CCNAME_LEN                  PATH_MAX + 5
     39
     40const char *CIFSSPNEGO_VERSION = "1.3";
    3341static const char *prog = "cifs.upcall";
    34 typedef enum _secType {
     42typedef enum _secype {
    3543        NONE = 0,
    3644        KRB5,
    3745        MS_KRB5
    38 } secType_t;
     46} sectype_t;
     47
     48/* does the ccache have a valid TGT? */
     49static time_t
     50get_tgt_time(const char *ccname) {
     51        krb5_context context;
     52        krb5_ccache ccache;
     53        krb5_cc_cursor cur;
     54        krb5_creds creds;
     55        krb5_principal principal;
     56        time_t credtime = 0;
     57        char *realm = NULL;
     58
     59        if (krb5_init_context(&context)) {
     60                syslog(LOG_DEBUG, "%s: unable to init krb5 context", __func__);
     61                return 0;
     62        }
     63
     64        if (krb5_cc_resolve(context, ccname, &ccache)) {
     65                syslog(LOG_DEBUG, "%s: unable to resolve krb5 cache", __func__);
     66                goto err_cache;
     67        }
     68
     69        if (krb5_cc_set_flags(context, ccache, 0)) {
     70                syslog(LOG_DEBUG, "%s: unable to set flags", __func__);
     71                goto err_cache;
     72        }
     73
     74        if (krb5_cc_get_principal(context, ccache, &principal)) {
     75                syslog(LOG_DEBUG, "%s: unable to get principal", __func__);
     76                goto err_princ;
     77        }
     78
     79        if (krb5_cc_start_seq_get(context, ccache, &cur)) {
     80                syslog(LOG_DEBUG, "%s: unable to seq start", __func__);
     81                goto err_ccstart;
     82        }
     83
     84        if ((realm = smb_krb5_principal_get_realm(context, principal)) == NULL) {
     85                syslog(LOG_DEBUG, "%s: unable to get realm", __func__);
     86                goto err_ccstart;
     87        }
     88
     89        while (!credtime && !krb5_cc_next_cred(context, ccache, &cur, &creds)) {
     90                char *name;
     91                if (smb_krb5_unparse_name(context, creds.server, &name)) {
     92                        syslog(LOG_DEBUG, "%s: unable to unparse name", __func__);
     93                        goto err_endseq;
     94                }
     95                if (krb5_realm_compare(context, creds.server, principal) &&
     96                    strnequal(name, KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE) &&
     97                    strnequal(name+KRB5_TGS_NAME_SIZE+1, realm, strlen(realm)) &&
     98                    creds.times.endtime > time(NULL))
     99                        credtime = creds.times.endtime;
     100                krb5_free_cred_contents(context, &creds);
     101                SAFE_FREE(name);
     102        }
     103err_endseq:
     104        krb5_cc_end_seq_get(context, ccache, &cur);
     105err_ccstart:
     106        krb5_free_principal(context, principal);
     107err_princ:
     108#if defined(KRB5_TC_OPENCLOSE)
     109        krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
     110#endif
     111        krb5_cc_close(context, ccache);
     112err_cache:
     113        krb5_free_context(context);
     114        return credtime;
     115}
     116
     117static int
     118krb5cc_filter(const struct dirent *dirent)
     119{
     120        if (strstr(dirent->d_name, CIFS_DEFAULT_KRB5_PREFIX))
     121                return 1;
     122        else
     123                return 0;
     124}
     125
     126/* search for a credcache that looks like a likely candidate */
     127static char *
     128find_krb5_cc(const char *dirname, uid_t uid)
     129{
     130        struct dirent **namelist;
     131        struct stat sbuf;
     132        char ccname[MAX_CCNAME_LEN], *credpath, *best_cache = NULL;
     133        int i, n;
     134        time_t cred_time, best_time = 0;
     135
     136        n = scandir(dirname, &namelist, krb5cc_filter, NULL);
     137        if (n < 0) {
     138                syslog(LOG_DEBUG, "%s: scandir error on directory '%s': %s",
     139                                  __func__, dirname, strerror(errno));
     140                return NULL;
     141        }
     142
     143        for (i = 0; i < n; i++) {
     144                snprintf(ccname, sizeof(ccname), "FILE:%s/%s", dirname,
     145                         namelist[i]->d_name);
     146                credpath = ccname + 5;
     147                syslog(LOG_DEBUG, "%s: considering %s", __func__, credpath);
     148
     149                if (lstat(credpath, &sbuf)) {
     150                        syslog(LOG_DEBUG, "%s: stat error on '%s': %s",
     151                                          __func__, credpath, strerror(errno));
     152                        free(namelist[i]);
     153                        continue;
     154                }
     155                if (sbuf.st_uid != uid) {
     156                        syslog(LOG_DEBUG, "%s: %s is owned by %u, not %u",
     157                                        __func__, credpath, sbuf.st_uid, uid);
     158                        free(namelist[i]);
     159                        continue;
     160                }
     161                if (!S_ISREG(sbuf.st_mode)) {
     162                        syslog(LOG_DEBUG, "%s: %s is not a regular file",
     163                                        __func__, credpath);
     164                        free(namelist[i]);
     165                        continue;
     166                }
     167                if (!(cred_time = get_tgt_time(ccname))) {
     168                        syslog(LOG_DEBUG, "%s: %s is not a valid credcache.",
     169                                        __func__, ccname);
     170                        free(namelist[i]);
     171                        continue;
     172                }
     173
     174                if (cred_time <= best_time) {
     175                        syslog(LOG_DEBUG, "%s: %s expires sooner than current "
     176                                          "best.", __func__, ccname);
     177                        free(namelist[i]);
     178                        continue;
     179                }
     180
     181                syslog(LOG_DEBUG, "%s: %s is valid ccache", __func__, ccname);
     182                free(best_cache);
     183                best_cache = SMB_STRNDUP(ccname, MAX_CCNAME_LEN);
     184                best_time = cred_time;
     185                free(namelist[i]);
     186        }
     187        free(namelist);
     188
     189        return best_cache;
     190}
    39191
    40192/*
     
    57209 *
    58210 * ret: 0 - success, others - failure
    59 */
     211*/
    60212static int
    61 handle_krb5_mech(const char *oid, const char *principal,
    62                      DATA_BLOB * secblob, DATA_BLOB * sess_key)
     213handle_krb5_mech(const char *oid, const char *principal,
     214                 )
    63215{
    64216        int retval;
    65217        DATA_BLOB tkt, tkt_wrapped;
    66218
     219
     220
     221
    67222        /* get a kerberos ticket for the service and extract the session key */
    68         retval = cli_krb5_get_ticket(principal, 0,
    69                                      &tkt, sess_key, 0, NULL, NULL);
    70 
    71         if (retval)
     223        retval = cli_krb5_get_ticket(principal, 0, &tkt, sess_key, 0, ccname,
     224                                     NULL);
     225
     226        if (retval) {
     227                syslog(LOG_DEBUG, "%s: failed to obtain service ticket (%d)",
     228                                  __func__, retval);
    72229                return retval;
     230
     231
     232
    73233
    74234        /* wrap that up in a nice GSS-API wrapping */
     
    83243}
    84244
    85 #define DKD_HAVE_HOSTNAME       1
    86 #define DKD_HAVE_VERSION        2
    87 #define DKD_HAVE_SEC            4
    88 #define DKD_HAVE_IPV4           8
    89 #define DKD_HAVE_IPV6           16
    90 #define DKD_HAVE_UID            32
     245#define DKD_HAVE_HOSTNAME       1
     246#define DKD_HAVE_VERSION        2
     247#define DKD_HAVE_SEC            4
     248#define DKD_HAVE_IP8
     249#define DKD_HAVE_
     250#define DKD_HAVE_
    91251#define DKD_MUSTHAVE_SET (DKD_HAVE_HOSTNAME|DKD_HAVE_VERSION|DKD_HAVE_SEC)
    92252
    93 static int
    94 decode_key_description(const char *desc, int *ver, secType_t * sec,
    95                            char **hostname, uid_t * uid)
    96 {
     253struct decoded_args {
     254        int             ver;
     255        char            *hostname;
     256        char            *ip;
     257        uid_t           uid;
     258        pid_t           pid;
     259        sectype_t       sec;
     260};
     261
     262static unsigned int
     263decode_key_description(const char *desc, struct decoded_args *arg)
     264{
     265        int len;
    97266        int retval = 0;
    98267        char *pos;
     
    102271                pos = index(tkn, ';');
    103272                if (strncmp(tkn, "host=", 5) == 0) {
    104                         int len;
    105 
    106                         if (pos == NULL) {
     273
     274                        if (pos == NULL)
    107275                                len = strlen(tkn);
     276
     277
     278
     279
     280
     281
     282
     283
     284
     285
     286
     287
     288
     289
     290
     291
     292
     293
     294
     295
     296
     297
     298
     299
     300
     301
     302
    108303                        } else {
    109                                 len = pos - tkn;
     304                                ;
    110305                        }
    111                         len -= 4;
    112                         SAFE_FREE(*hostname);
    113                         *hostname = SMB_XMALLOC_ARRAY(char, len);
    114                         strlcpy(*hostname, tkn + 5, len);
    115                         retval |= DKD_HAVE_HOSTNAME;
    116                 } else if (strncmp(tkn, "ipv4=", 5) == 0) {
    117                         /* BB: do we need it if we have hostname already? */
    118                 } else if (strncmp(tkn, "ipv6=", 5) == 0) {
    119                         /* BB: do we need it if we have hostname already? */
    120306                } else if (strncmp(tkn, "sec=", 4) == 0) {
    121307                        if (strncmp(tkn + 4, "krb5", 4) == 0) {
    122308                                retval |= DKD_HAVE_SEC;
    123                                 *sec = KRB5;
     309                                sec = KRB5;
    124310                        } else if (strncmp(tkn + 4, "mskrb5", 6) == 0) {
    125311                                retval |= DKD_HAVE_SEC;
    126                                 *sec = MS_KRB5;
     312                                sec = MS_KRB5;
    127313                        }
    128314                } else if (strncmp(tkn, "uid=", 4) == 0) {
    129315                        errno = 0;
    130                         *uid = strtol(tkn + 4, NULL, 16);
     316                        uid = strtol(tkn + 4, NULL, 16);
    131317                        if (errno != 0) {
    132                                 syslog(LOG_WARNING, "Invalid uid format: %s",
     318                                syslog(LOG_, "Invalid uid format: %s",
    133319                                       strerror(errno));
    134320                                return 1;
     
    138324                } else if (strncmp(tkn, "ver=", 4) == 0) {      /* if version */
    139325                        errno = 0;
    140                         *ver = strtol(tkn + 4, NULL, 16);
     326                        ver = strtol(tkn + 4, NULL, 16);
    141327                        if (errno != 0) {
    142                                 syslog(LOG_WARNING,
    143                                        "Invalid version format: %s",
     328                                syslog(LOG_ERR, "Invalid version format: %s",
    144329                                       strerror(errno));
    145330                                return 1;
     
    167352                keyend = index(keyend+1, ';');
    168353                if (!keyend) {
    169                         syslog(LOG_WARNING, "invalid key description: %s",
     354                        syslog(LOG_, "invalid key description: %s",
    170355                                        key_descr);
    171356                        return 1;
     
    177362        c = getaddrinfo(keyend, NULL, NULL, &addr);
    178363        if (c) {
    179                 syslog(LOG_WARNING, "unable to resolve hostname: %s [%s]",
     364                syslog(LOG_, "unable to resolve hostname: %s [%s]",
    180365                                keyend, gai_strerror(c));
    181366                return 1;
     
    183368
    184369        /* conver ip to string form */
    185         if (addr->ai_family == AF_INET) {
     370        if (addr->ai_family == AF_INET)
    186371                p = &(((struct sockaddr_in *)addr->ai_addr)->sin_addr);
    187         } else {
     372       
    188373                p = &(((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr);
    189         }
     374
    190375        if (!inet_ntop(addr->ai_family, p, ip, sizeof(ip))) {
    191                 syslog(LOG_WARNING, "%s: inet_ntop: %s",
    192                                 __FUNCTION__, strerror(errno));
     376                syslog(LOG_ERR, "%s: inet_ntop: %s", __func__, strerror(errno));
    193377                freeaddrinfo(addr);
    194378                return 1;
     
    198382        c = keyctl_instantiate(key, ip, strlen(ip)+1, 0);
    199383        if (c == -1) {
    200                 syslog(LOG_WARNING, "%s: keyctl_instantiate: %s",
    201                                 __FUNCTION__, strerror(errno));
     384                syslog(LOG_,
     385                                strerror(errno));
    202386                freeaddrinfo(addr);
    203387                return 1;
     
    208392}
    209393
     394
     395
     396
     397
     398
     399
     400
     401
     402
     403
     404
     405
     406
     407
     408
     409
     410
     411
     412
     413
     414
     415
     416
     417
     418
     419
     420
     421
     422
     423
     424
     425
     426
     427
     428
     429
     430
     431
     432
     433
     434
     435
     436
     437
     438
     439
     440
     441
     442
     443
     444
     445
     446
     447
    210448static void
    211449usage(void)
    212450{
    213         syslog(LOG_WARNING, "Usage: %s [-c] [-v] key_serial", prog);
    214         fprintf(stderr, "Usage: %s [-c] [-v] key_serial\n", prog);
    215 }
     451        syslog(LOG_INFO, "Usage: %s [-t] [-v] key_serial", prog);
     452        fprintf(stderr, "Usage: %s [-t] [-v] key_serial\n", prog);
     453}
     454
     455const struct option long_options[] = {
     456        { "trust-dns",  0, NULL, 't' },
     457        { "version",    0, NULL, 'v' },
     458        { NULL,         0, NULL, 0 }
     459};
    216460
    217461int main(const int argc, char *const argv[])
     
    220464        DATA_BLOB secblob = data_blob_null;
    221465        DATA_BLOB sess_key = data_blob_null;
    222         secType_t sectype = NONE;
    223466        key_serial_t key = 0;
    224467        size_t datalen;
     468
    225469        long rc = 1;
    226         uid_t uid = 0;
    227         int kernel_upcall_version = 0;
    228         int c, use_cifs_service_prefix = 0;
    229         char *buf, *hostname = NULL;
     470        = 0;
     471        ;
     472        ;
     473        ;
    230474        const char *oid;
    231475
     476
     477
    232478        openlog(prog, 0, LOG_DAEMON);
    233479
    234         while ((c = getopt(argc, argv, "cv")) != -1) {
     480        while ((c = getopt)) != -1) {
    235481                switch (c) {
    236                 case 'c':{
    237                         use_cifs_service_prefix = 1;
    238                         break;
    239                         }
    240                 case 'v':{
     482                case 'c':
     483                        /* legacy option -- skip it */
     484                        break;
     485                case 't':
     486                        try_dns++;
     487                        break;
     488                case 'v':
    241489                        printf("version: %s\n", CIFSSPNEGO_VERSION);
    242490                        goto out;
    243                         }
    244                 default:{
    245                         syslog(LOG_WARNING, "unknown option: %c", c);
     491                default:
     492                        syslog(LOG_ERR, "unknown option: %c", c);
    246493                        goto out;
    247                         }
    248494                }
    249495        }
     
    260506        if (errno != 0) {
    261507                key = 0;
    262                 syslog(LOG_WARNING, "Invalid key format: %s", strerror(errno));
     508                syslog(LOG_, "Invalid key format: %s", strerror(errno));
    263509                goto out;
    264510        }
     
    266512        rc = keyctl_describe_alloc(key, &buf);
    267513        if (rc == -1) {
    268                 syslog(LOG_WARNING, "keyctl_describe_alloc failed: %s",
     514                syslog(LOG_, "keyctl_describe_alloc failed: %s",
    269515                       strerror(errno));
    270516                rc = 1;
    271517                goto out;
    272518        }
     519
     520
    273521
    274522        if ((strncmp(buf, "cifs.resolver", sizeof("cifs.resolver")-1) == 0) ||
     
    278526        }
    279527
    280         rc = decode_key_description(buf, &kernel_upcall_version, &sectype,
    281                                     &hostname, &uid);
    282         if ((rc & DKD_MUSTHAVE_SET) != DKD_MUSTHAVE_SET) {
    283                 syslog(LOG_WARNING,
    284                        "unable to get from description necessary params");
     528       
     529        );
     530        if (( & DKD_MUSTHAVE_SET) != DKD_MUSTHAVE_SET) {
     531                syslog(LOG_
     532                );
    285533                rc = 1;
    286                 SAFE_FREE(buf);
    287                 goto out;
    288         }
    289         SAFE_FREE(buf);
    290 
    291         if (kernel_upcall_version > CIFS_SPNEGO_UPCALL_VERSION) {
    292                 syslog(LOG_WARNING,
    293                        "incompatible kernel upcall version: 0x%x",
    294                        kernel_upcall_version);
     534                goto out;
     535        }
     536
     537        if (arg.ver > CIFS_SPNEGO_UPCALL_VERSION) {
     538                syslog(LOG_ERR, "incompatible kernel upcall version: 0x%x",
     539                                arg.ver);
    295540                rc = 1;
    296541                goto out;
    297542        }
    298543
    299         if (rc & DKD_HAVE_UID) {
    300                 rc = setuid(uid);
     544        if ( & DKD_HAVE_UID) {
     545                rc = setuid(uid);
    301546                if (rc == -1) {
    302                         syslog(LOG_WARNING, "setuid: %s", strerror(errno));
     547                        syslog(LOG_, "setuid: %s", strerror(errno));
    303548                        goto out;
    304549                }
    305         }
    306 
    307         /* BB: someday upcall SPNEGO blob could be checked here to decide
    308          * what mech to use */
     550
     551                ccname = find_krb5_cc(CIFS_DEFAULT_KRB5_DIR, arg.uid);
     552        }
     553
     554        host = arg.hostname;
    309555
    310556        // do mech specific authorization
    311         switch (sectype) {
     557        switch () {
    312558        case MS_KRB5:
    313         case KRB5:{
    314                         char *princ;
    315                         size_t len;
    316 
    317                         /* for "cifs/" service name + terminating 0 */
    318                         len = strlen(hostname) + 5 + 1;
    319                         princ = SMB_XMALLOC_ARRAY(char, len);
    320                         if (!princ) {
    321                                 rc = 1;
    322                                 break;
    323                         }
    324                         if (use_cifs_service_prefix) {
    325                                 strlcpy(princ, "cifs/", len);
    326                         } else {
    327                                 strlcpy(princ, "host/", len);
    328                         }
    329                         strlcpy(princ + 5, hostname, len - 5);
    330 
    331                         if (sectype == MS_KRB5)
    332                                 oid = OID_KERBEROS5_OLD;
    333                         else
    334                                 oid = OID_KERBEROS5;
    335 
    336                         rc = handle_krb5_mech(oid, princ, &secblob, &sess_key);
    337                         SAFE_FREE(princ);
    338                         break;
    339                 }
    340         default:{
    341                         syslog(LOG_WARNING, "sectype: %d is not implemented",
    342                                sectype);
    343                         rc = 1;
    344                         break;
    345                 }
    346         }
    347 
    348         if (rc) {
    349                 goto out;
    350         }
     559        case KRB5:
     560retry_new_hostname:
     561                /* for "cifs/" service name + terminating 0 */
     562                datalen = strlen(host) + 5 + 1;
     563                princ = SMB_XMALLOC_ARRAY(char, datalen);
     564                if (!princ) {
     565                        rc = -ENOMEM;
     566                        break;
     567                }
     568
     569                if (arg.sec == MS_KRB5)
     570                        oid = OID_KERBEROS5_OLD;
     571                else
     572                        oid = OID_KERBEROS5;
     573
     574                /*
     575                 * try getting a cifs/ principal first and then fall back to
     576                 * getting a host/ principal if that doesn't work.
     577                 */
     578                strlcpy(princ, "cifs/", datalen);
     579                strlcpy(princ + 5, host, datalen - 5);
     580                rc = handle_krb5_mech(oid, princ, &secblob, &sess_key, ccname);
     581                if (!rc)
     582                        break;
     583
     584                memcpy(princ, "host/", 5);
     585                rc = handle_krb5_mech(oid, princ, &secblob, &sess_key, ccname);
     586                if (!rc)
     587                        break;
     588
     589                if (!try_dns || !(have & DKD_HAVE_IP))
     590                        break;
     591
     592                rc = ip_to_fqdn(arg.ip, hostbuf, sizeof(hostbuf));
     593                if (rc)
     594                        break;
     595
     596                SAFE_FREE(princ);
     597                try_dns = 0;
     598                host = hostbuf;
     599                goto retry_new_hostname;
     600        default:
     601                syslog(LOG_ERR, "sectype: %d is not implemented", arg.sec);
     602                rc = 1;
     603                break;
     604        }
     605
     606        SAFE_FREE(princ);
     607
     608        if (rc)
     609                goto out;
    351610
    352611        /* pack SecurityBLob and SessionKey into downcall packet */
     
    358617                goto out;
    359618        }
    360         keydata->version = kernel_upcall_version;
     619        keydata->version = ;
    361620        keydata->flags = 0;
    362621        keydata->sesskey_len = sess_key.length;
     
    369628        rc = keyctl_instantiate(key, keydata, datalen, 0);
    370629        if (rc == -1) {
    371                 syslog(LOG_WARNING, "keyctl_instantiate: %s", strerror(errno));
     630                syslog(LOG_, "keyctl_instantiate: %s", strerror(errno));
    372631                goto out;
    373632        }
     
    386645        data_blob_free(&secblob);
    387646        data_blob_free(&sess_key);
    388         SAFE_FREE(hostname);
     647        SAFE_FREE(ccname);
     648        SAFE_FREE(arg.hostname);
     649        SAFE_FREE(arg.ip);
    389650        SAFE_FREE(keydata);
    390651        return rc;
Note: See TracChangeset for help on using the changeset viewer.