Changeset 370 for branches/samba-3.3.x/source/client/cifs.upcall.c
- Timestamp:
- Jan 15, 2010, 8:23:30 AM (16 years ago)
- File:
-
- 1 edited
-
branches/samba-3.3.x/source/client/cifs.upcall.c (modified) (18 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/samba-3.3.x/source/client/cifs.upcall.c
r206 r370 2 2 * CIFS user-space helper. 3 3 * Copyright (C) Igor Mammedov ([email protected]) 2007 4 4 5 * 5 6 * Used by /sbin/request-key for handling … … 26 27 27 28 #include "includes.h" 29 28 30 #include <keyutils.h> 31 29 32 30 33 #include "cifs_spnego.h" 31 34 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 40 const char *CIFSSPNEGO_VERSION = "1.3"; 33 41 static const char *prog = "cifs.upcall"; 34 typedef enum _sec Type {42 typedef enum _secype { 35 43 NONE = 0, 36 44 KRB5, 37 45 MS_KRB5 38 } secType_t; 46 } sectype_t; 47 48 /* does the ccache have a valid TGT? */ 49 static time_t 50 get_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 } 103 err_endseq: 104 krb5_cc_end_seq_get(context, ccache, &cur); 105 err_ccstart: 106 krb5_free_principal(context, principal); 107 err_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); 112 err_cache: 113 krb5_free_context(context); 114 return credtime; 115 } 116 117 static int 118 krb5cc_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 */ 127 static char * 128 find_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 } 39 191 40 192 /* … … 57 209 * 58 210 * ret: 0 - success, others - failure 59 */211 */ 60 212 static int 61 handle_krb5_mech(const char *oid, const char *principal, 62 DATA_BLOB * secblob, DATA_BLOB * sess_key)213 handle_krb5_mech(const char *oid, const char *principal, 214 ) 63 215 { 64 216 int retval; 65 217 DATA_BLOB tkt, tkt_wrapped; 66 218 219 220 221 67 222 /* 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); 72 229 return retval; 230 231 232 73 233 74 234 /* wrap that up in a nice GSS-API wrapping */ … … 83 243 } 84 244 85 #define DKD_HAVE_HOSTNAME 186 #define DKD_HAVE_VERSION 287 #define DKD_HAVE_SEC 488 #define DKD_HAVE_IP V4889 #define DKD_HAVE_ IPV6 1690 #define DKD_HAVE_ UID 32245 #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_ 91 251 #define DKD_MUSTHAVE_SET (DKD_HAVE_HOSTNAME|DKD_HAVE_VERSION|DKD_HAVE_SEC) 92 252 93 static int 94 decode_key_description(const char *desc, int *ver, secType_t * sec, 95 char **hostname, uid_t * uid) 96 { 253 struct 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 262 static unsigned int 263 decode_key_description(const char *desc, struct decoded_args *arg) 264 { 265 int len; 97 266 int retval = 0; 98 267 char *pos; … … 102 271 pos = index(tkn, ';'); 103 272 if (strncmp(tkn, "host=", 5) == 0) { 104 int len; 105 106 if (pos == NULL) { 273 274 if (pos == NULL) 107 275 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 108 303 } else { 109 len = pos - tkn;304 ; 110 305 } 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? */120 306 } else if (strncmp(tkn, "sec=", 4) == 0) { 121 307 if (strncmp(tkn + 4, "krb5", 4) == 0) { 122 308 retval |= DKD_HAVE_SEC; 123 *sec = KRB5;309 sec = KRB5; 124 310 } else if (strncmp(tkn + 4, "mskrb5", 6) == 0) { 125 311 retval |= DKD_HAVE_SEC; 126 *sec = MS_KRB5;312 sec = MS_KRB5; 127 313 } 128 314 } else if (strncmp(tkn, "uid=", 4) == 0) { 129 315 errno = 0; 130 *uid = strtol(tkn + 4, NULL, 16);316 uid = strtol(tkn + 4, NULL, 16); 131 317 if (errno != 0) { 132 syslog(LOG_ WARNING, "Invalid uid format: %s",318 syslog(LOG_, "Invalid uid format: %s", 133 319 strerror(errno)); 134 320 return 1; … … 138 324 } else if (strncmp(tkn, "ver=", 4) == 0) { /* if version */ 139 325 errno = 0; 140 *ver = strtol(tkn + 4, NULL, 16);326 ver = strtol(tkn + 4, NULL, 16); 141 327 if (errno != 0) { 142 syslog(LOG_WARNING, 143 "Invalid version format: %s", 328 syslog(LOG_ERR, "Invalid version format: %s", 144 329 strerror(errno)); 145 330 return 1; … … 167 352 keyend = index(keyend+1, ';'); 168 353 if (!keyend) { 169 syslog(LOG_ WARNING, "invalid key description: %s",354 syslog(LOG_, "invalid key description: %s", 170 355 key_descr); 171 356 return 1; … … 177 362 c = getaddrinfo(keyend, NULL, NULL, &addr); 178 363 if (c) { 179 syslog(LOG_ WARNING, "unable to resolve hostname: %s [%s]",364 syslog(LOG_, "unable to resolve hostname: %s [%s]", 180 365 keyend, gai_strerror(c)); 181 366 return 1; … … 183 368 184 369 /* conver ip to string form */ 185 if (addr->ai_family == AF_INET) {370 if (addr->ai_family == AF_INET) 186 371 p = &(((struct sockaddr_in *)addr->ai_addr)->sin_addr); 187 } else {372 188 373 p = &(((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr); 189 } 374 190 375 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)); 193 377 freeaddrinfo(addr); 194 378 return 1; … … 198 382 c = keyctl_instantiate(key, ip, strlen(ip)+1, 0); 199 383 if (c == -1) { 200 syslog(LOG_ WARNING, "%s: keyctl_instantiate: %s",201 __FUNCTION__,strerror(errno));384 syslog(LOG_, 385 strerror(errno)); 202 386 freeaddrinfo(addr); 203 387 return 1; … … 208 392 } 209 393 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 210 448 static void 211 449 usage(void) 212 450 { 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 455 const struct option long_options[] = { 456 { "trust-dns", 0, NULL, 't' }, 457 { "version", 0, NULL, 'v' }, 458 { NULL, 0, NULL, 0 } 459 }; 216 460 217 461 int main(const int argc, char *const argv[]) … … 220 464 DATA_BLOB secblob = data_blob_null; 221 465 DATA_BLOB sess_key = data_blob_null; 222 secType_t sectype = NONE;223 466 key_serial_t key = 0; 224 467 size_t datalen; 468 225 469 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 ; 230 474 const char *oid; 231 475 476 477 232 478 openlog(prog, 0, LOG_DAEMON); 233 479 234 while ((c = getopt (argc, argv, "cv")) != -1) {480 while ((c = getopt)) != -1) { 235 481 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': 241 489 printf("version: %s\n", CIFSSPNEGO_VERSION); 242 490 goto out; 243 } 244 default:{ 245 syslog(LOG_WARNING, "unknown option: %c", c); 491 default: 492 syslog(LOG_ERR, "unknown option: %c", c); 246 493 goto out; 247 }248 494 } 249 495 } … … 260 506 if (errno != 0) { 261 507 key = 0; 262 syslog(LOG_ WARNING, "Invalid key format: %s", strerror(errno));508 syslog(LOG_, "Invalid key format: %s", strerror(errno)); 263 509 goto out; 264 510 } … … 266 512 rc = keyctl_describe_alloc(key, &buf); 267 513 if (rc == -1) { 268 syslog(LOG_ WARNING, "keyctl_describe_alloc failed: %s",514 syslog(LOG_, "keyctl_describe_alloc failed: %s", 269 515 strerror(errno)); 270 516 rc = 1; 271 517 goto out; 272 518 } 519 520 273 521 274 522 if ((strncmp(buf, "cifs.resolver", sizeof("cifs.resolver")-1) == 0) || … … 278 526 } 279 527 280 rc = decode_key_description(buf, &kernel_upcall_version, §ype,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 ); 285 533 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); 295 540 rc = 1; 296 541 goto out; 297 542 } 298 543 299 if ( rc& DKD_HAVE_UID) {300 rc = setuid( uid);544 if ( & DKD_HAVE_UID) { 545 rc = setuid(uid); 301 546 if (rc == -1) { 302 syslog(LOG_ WARNING, "setuid: %s", strerror(errno));547 syslog(LOG_, "setuid: %s", strerror(errno)); 303 548 goto out; 304 549 } 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; 309 555 310 556 // do mech specific authorization 311 switch ( sectype) {557 switch () { 312 558 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: 560 retry_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; 351 610 352 611 /* pack SecurityBLob and SessionKey into downcall packet */ … … 358 617 goto out; 359 618 } 360 keydata->version = kernel_upcall_version;619 keydata->version = ; 361 620 keydata->flags = 0; 362 621 keydata->sesskey_len = sess_key.length; … … 369 628 rc = keyctl_instantiate(key, keydata, datalen, 0); 370 629 if (rc == -1) { 371 syslog(LOG_ WARNING, "keyctl_instantiate: %s", strerror(errno));630 syslog(LOG_, "keyctl_instantiate: %s", strerror(errno)); 372 631 goto out; 373 632 } … … 386 645 data_blob_free(&secblob); 387 646 data_blob_free(&sess_key); 388 SAFE_FREE(hostname); 647 SAFE_FREE(ccname); 648 SAFE_FREE(arg.hostname); 649 SAFE_FREE(arg.ip); 389 650 SAFE_FREE(keydata); 390 651 return rc;
Note:
See TracChangeset
for help on using the changeset viewer.
