1 /* 2 * Copyright (c) 2000, Boris Popov 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Boris Popov. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $Id: ctx.c,v 1.32.70.2 2005/06/02 00:55:40 lindak Exp $ 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 #include <sys/param.h> 38 #include <sys/ioctl.h> 39 #include <sys/time.h> 40 #include <sys/mount.h> 41 #include <sys/types.h> 42 #include <sys/byteorder.h> 43 44 #include <fcntl.h> 45 #include <ctype.h> 46 #include <errno.h> 47 #include <stdio.h> 48 #include <string.h> 49 #include <strings.h> 50 #include <stdlib.h> 51 #include <pwd.h> 52 #include <grp.h> 53 #include <unistd.h> 54 #include <libintl.h> 55 #include <assert.h> 56 #include <nss_dbdefs.h> 57 58 #include <kerberosv5/krb5.h> 59 #include <kerberosv5/com_err.h> 60 61 extern uid_t real_uid, eff_uid; 62 63 #define NB_NEEDRESOLVER 64 65 #include <netsmb/smb_lib.h> 66 #include <netsmb/netbios.h> 67 #include <netsmb/nb_lib.h> 68 #include <netsmb/smb_dev.h> 69 #include <cflib.h> 70 #include <charsets.h> 71 72 #include <spnego.h> 73 #include "derparse.h" 74 75 extern MECH_OID g_stcMechOIDList []; 76 77 #define POWEROF2(x) (((x) & ((x)-1)) == 0) 78 79 /* These two may be set by commands. */ 80 int smb_debug, smb_verbose; 81 82 /* 83 * This used to call the DCE/RPC code. 84 * We want more strict layering than this. 85 * The redirector should simply export a 86 * remote pipe API, comsumed by dce rpc. 87 * Make it a no-op for now. 88 */ 89 #if 0 90 #include <rpc_cleanup.h> 91 #else 92 static void 93 rpc_cleanup_smbctx(struct smb_ctx *ctx) 94 { 95 } 96 #endif 97 98 void 99 dump_ctx_flags(int flags) 100 { 101 printf(" Flags: "); 102 if (flags == 0) 103 printf("0"); 104 if (flags & SMBCF_NOPWD) 105 printf("NOPWD "); 106 if (flags & SMBCF_SRIGHTS) 107 printf("SRIGHTS "); 108 if (flags & SMBCF_LOCALE) 109 printf("LOCALE "); 110 if (flags & SMBCF_CMD_DOM) 111 printf("CMD_DOM "); 112 if (flags & SMBCF_CMD_USR) 113 printf("CMD_USR "); 114 if (flags & SMBCF_CMD_PW) 115 printf("CMD_PW "); 116 if (flags & SMBCF_RESOLVED) 117 printf("RESOLVED "); 118 if (flags & SMBCF_KCBAD) 119 printf("KCBAD "); 120 if (flags & SMBCF_KCFOUND) 121 printf("KCFOUND "); 122 if (flags & SMBCF_BROWSEOK) 123 printf("BROWSEOK "); 124 if (flags & SMBCF_AUTHREQ) 125 printf("AUTHREQ "); 126 if (flags & SMBCF_KCSAVE) 127 printf("KCSAVE "); 128 if (flags & SMBCF_XXX) 129 printf("XXX "); 130 if (flags & SMBCF_SSNACTIVE) 131 printf("SSNACTIVE "); 132 if (flags & SMBCF_KCDOMAIN) 133 printf("KCDOMAIN "); 134 printf("\n"); 135 } 136 137 void 138 dump_ctx_ssn(struct smbioc_ossn *ssn) 139 { 140 printf(" srvname=\"%s\", dom=\"%s\", user=\"%s\", password=%s\n", 141 ssn->ioc_srvname, ssn->ioc_workgroup, ssn->ioc_user, 142 ssn->ioc_password[0] ? "(non-null)" : "NULL"); 143 printf(" timeout=%d, retry=%d, owner=%d, group=%d\n", 144 ssn->ioc_timeout, ssn->ioc_retrycount, 145 ssn->ioc_owner, ssn->ioc_group); 146 } 147 148 void 149 dump_ctx_sh(struct smbioc_oshare *sh) 150 { 151 printf(" share_name=\"%s\", share_pw=\"%s\"\n", 152 sh->ioc_share, sh->ioc_password); 153 } 154 155 void 156 dump_ctx(char *where, struct smb_ctx *ctx) 157 { 158 printf("context %s:\n", where); 159 dump_ctx_flags(ctx->ct_flags); 160 161 printf(" localname=\"%s\"", ctx->ct_locname); 162 163 if (ctx->ct_fullserver) 164 printf(" fullserver=\"%s\"", ctx->ct_fullserver); 165 else 166 printf(" fullserver=NULL"); 167 168 if (ctx->ct_srvaddr) 169 printf(" srvaddr=\"%s\"\n", ctx->ct_srvaddr); 170 else 171 printf(" srvaddr=NULL\n"); 172 173 dump_ctx_ssn(&ctx->ct_ssn); 174 dump_ctx_sh(&ctx->ct_sh); 175 } 176 177 /* 178 * Initialize an smb_ctx struct. 179 * 180 * The sequence for getting all the members filled in 181 * has some tricky aspects. Here's how it works: 182 * 183 * The search order for options is as follows: 184 * command line options 185 * values parsed from UNC path (cmd) 186 * values from RC file (per-user) 187 * values from SMF (system-wide) 188 * built-in defaults 189 * 190 * Normally, one would simply get all the values starting with 191 * the bottom of the above list and working to the top, and 192 * overwriting values as you go. But we need an exception. 193 * 194 * In this function, we parse the UNC path and command line options, 195 * because we need (at least) the server name when we're getting the 196 * SMF and RC file values. However, values we get from the command 197 * should not be overwritten by SMF or RC file parsing, so we mark 198 * values from the command as "from CMD" and the RC file parser 199 * leaves in place any values so marked. See: SMBCF_CMD_* 200 * 201 * The semantics of these flags are: "This value came from the 202 * current command instance, not from sources that may apply to 203 * multiple commands." (Different from the old "FROMUSR" flag.) 204 * 205 * Note that smb_ctx_opt() is called later to handle the 206 * remaining options, which should be ignored here. 207 * The (magic) leading ":" in cf_getopt() makes it 208 * ignore options not in the options string. 209 */ 210 int 211 smb_ctx_init(struct smb_ctx *ctx, int argc, char *argv[], 212 int minlevel, int maxlevel, int sharetype) 213 { 214 int opt, error = 0; 215 const char *arg, *cp; 216 struct passwd pw; 217 char pwbuf[NSS_BUFLEN_PASSWD]; 218 int aflg = 0, uflg = 0; 219 220 bzero(ctx, sizeof (*ctx)); 221 if (sharetype == SMB_ST_DISK) 222 ctx->ct_flags |= SMBCF_BROWSEOK; 223 error = nb_ctx_create(&ctx->ct_nb); 224 if (error) 225 return (error); 226 227 ctx->ct_fd = -1; 228 ctx->ct_parsedlevel = SMBL_NONE; 229 ctx->ct_minlevel = minlevel; 230 ctx->ct_maxlevel = maxlevel; 231 232 ctx->ct_ssn.ioc_opt = SMBVOPT_CREATE | SMBVOPT_MINAUTH_NTLM; 233 ctx->ct_ssn.ioc_timeout = 15; 234 ctx->ct_ssn.ioc_retrycount = 4; 235 ctx->ct_ssn.ioc_owner = SMBM_ANY_OWNER; 236 ctx->ct_ssn.ioc_group = SMBM_ANY_GROUP; 237 ctx->ct_ssn.ioc_mode = SMBM_EXEC; 238 ctx->ct_ssn.ioc_rights = SMBM_DEFAULT; 239 240 ctx->ct_sh.ioc_opt = SMBVOPT_CREATE; 241 ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER; 242 ctx->ct_sh.ioc_group = SMBM_ANY_GROUP; 243 ctx->ct_sh.ioc_mode = SMBM_EXEC; 244 ctx->ct_sh.ioc_rights = SMBM_DEFAULT; 245 ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER; 246 ctx->ct_sh.ioc_group = SMBM_ANY_GROUP; 247 248 nb_ctx_setscope(ctx->ct_nb, ""); 249 250 /* 251 * if the user name is not specified some other way, 252 * use the current user name (built-in default) 253 */ 254 if (getpwuid_r(geteuid(), &pw, pwbuf, sizeof (pwbuf)) != NULL) 255 smb_ctx_setuser(ctx, pw.pw_name, 0); 256 257 /* 258 * Set a built-in default domain (workgroup). 259 * XXX: What's the best default? Use "?" instead? 260 * Using the Windows/NT default for now. 261 */ 262 smb_ctx_setworkgroup(ctx, "WORKGROUP", 0); 263 264 /* 265 * Parse the UNC path. Values from here are 266 * marked as "from CMD". 267 */ 268 if (argv == NULL) 269 goto done; 270 for (opt = 1; opt < argc; opt++) { 271 cp = argv[opt]; 272 if (strncmp(cp, "//", 2) != 0) 273 continue; 274 error = smb_ctx_parseunc(ctx, cp, sharetype, &cp); 275 if (error) 276 return (error); 277 break; 278 } 279 280 /* 281 * Parse options, if any. Values from here too 282 * are marked as "from CMD". 283 */ 284 while (error == 0 && (opt = cf_getopt(argc, argv, ":AU:E:L:")) != -1) { 285 arg = cf_optarg; 286 switch (opt) { 287 case 'A': 288 aflg = 1; 289 error = smb_ctx_setuser(ctx, "", TRUE); 290 error = smb_ctx_setpassword(ctx, "", TRUE); 291 ctx->ct_flags |= SMBCF_NOPWD; 292 break; 293 case 'E': 294 #if 0 /* We don't support any "charset" stuff. (ignore -E) */ 295 error = smb_ctx_setcharset(ctx, arg); 296 if (error) 297 return (error); 298 #endif 299 break; 300 case 'L': 301 #if 0 /* Use the standard environment variables (ignore -L) */ 302 error = nls_setlocale(optarg); 303 if (error) 304 break; 305 #endif 306 break; 307 case 'U': 308 uflg = 1; 309 error = smb_ctx_setuser(ctx, arg, TRUE); 310 break; 311 } 312 } 313 if (aflg && uflg) { 314 printf(gettext("-A and -U flags are exclusive.\n")); 315 return (1); 316 } 317 cf_optind = cf_optreset = 1; 318 319 done: 320 if (smb_debug) 321 dump_ctx("after smb_ctx_init", ctx); 322 323 return (error); 324 } 325 326 void 327 smb_ctx_done(struct smb_ctx *ctx) 328 { 329 330 rpc_cleanup_smbctx(ctx); 331 332 /* Kerberos stuff. See smb_ctx_krb5init() */ 333 if (ctx->ct_krb5ctx) { 334 if (ctx->ct_krb5cp) 335 krb5_free_principal(ctx->ct_krb5ctx, ctx->ct_krb5cp); 336 krb5_free_context(ctx->ct_krb5ctx); 337 } 338 339 if (ctx->ct_fd != -1) 340 close(ctx->ct_fd); 341 #if 0 /* XXX: not pointers anymore */ 342 if (&ctx->ct_ssn.ioc_server) 343 nb_snbfree(&ctx->ct_ssn.ioc_server); 344 if (&ctx->ct_ssn.ioc_local) 345 nb_snbfree(&ctx->ct_ssn.ioc_local); 346 #endif 347 if (ctx->ct_srvaddr) 348 free(ctx->ct_srvaddr); 349 if (ctx->ct_nb) 350 nb_ctx_done(ctx->ct_nb); 351 if (ctx->ct_secblob) 352 free(ctx->ct_secblob); 353 if (ctx->ct_origshare) 354 free(ctx->ct_origshare); 355 if (ctx->ct_fullserver) 356 free(ctx->ct_fullserver); 357 } 358 359 static int 360 getsubstring(const char *p, uchar_t sep, char *dest, int maxlen, 361 const char **next) 362 { 363 int len; 364 365 maxlen--; 366 for (len = 0; len < maxlen && *p != sep; p++, len++, dest++) { 367 if (*p == 0) 368 return (EINVAL); 369 *dest = *p; 370 } 371 *dest = 0; 372 *next = *p ? p + 1 : p; 373 return (0); 374 } 375 376 /* 377 * Parse the UNC path. Here we expect something like 378 * "//[workgroup;][user[:password]@]host[/share[/path]]" 379 * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt 380 * Values found here are marked as "from CMD". 381 */ 382 int 383 smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype, 384 const char **next) 385 { 386 const char *p = unc; 387 char *p1, *colon, *servername; 388 char tmp[1024]; 389 char tmp2[1024]; 390 int error; 391 392 ctx->ct_parsedlevel = SMBL_NONE; 393 if (*p++ != '/' || *p++ != '/') { 394 smb_error(dgettext(TEXT_DOMAIN, 395 "UNC should start with '//'"), 0); 396 return (EINVAL); 397 } 398 p1 = tmp; 399 error = getsubstring(p, ';', p1, sizeof (tmp), &p); 400 if (!error) { 401 if (*p1 == 0) { 402 smb_error(dgettext(TEXT_DOMAIN, 403 "empty workgroup name"), 0); 404 return (EINVAL); 405 } 406 nls_str_upper(tmp, tmp); 407 error = smb_ctx_setworkgroup(ctx, unpercent(tmp), TRUE); 408 if (error) 409 return (error); 410 } 411 colon = (char *)p; 412 error = getsubstring(p, '@', p1, sizeof (tmp), &p); 413 if (!error) { 414 if (ctx->ct_maxlevel < SMBL_VC) { 415 smb_error(dgettext(TEXT_DOMAIN, 416 "no user name required"), 0); 417 return (EINVAL); 418 } 419 p1 = strchr(tmp, ':'); 420 if (p1) { 421 colon += p1 - tmp; 422 *p1++ = (char)0; 423 error = smb_ctx_setpassword(ctx, unpercent(p1), TRUE); 424 if (error) 425 return (error); 426 if (p - colon > 2) 427 memset(colon+1, '*', p - colon - 2); 428 } 429 p1 = tmp; 430 if (*p1 == 0) { 431 smb_error(dgettext(TEXT_DOMAIN, 432 "empty user name"), 0); 433 return (EINVAL); 434 } 435 error = smb_ctx_setuser(ctx, unpercent(tmp), TRUE); 436 if (error) 437 return (error); 438 ctx->ct_parsedlevel = SMBL_VC; 439 } 440 error = getsubstring(p, '/', p1, sizeof (tmp), &p); 441 if (error) { 442 error = getsubstring(p, '\0', p1, sizeof (tmp), &p); 443 if (error) { 444 smb_error(dgettext(TEXT_DOMAIN, 445 "no server name found"), 0); 446 return (error); 447 } 448 } 449 if (*p1 == 0) { 450 smb_error(dgettext(TEXT_DOMAIN, "empty server name"), 0); 451 return (EINVAL); 452 } 453 454 455 /* 456 * It's safe to uppercase this string, which 457 * consists of ascii characters that should 458 * be uppercased, %s, and ascii characters representing 459 * hex digits 0-9 and A-F (already uppercased, and 460 * if not uppercased they need to be). However, 461 * it is NOT safe to uppercase after it has been 462 * converted, below! 463 */ 464 465 nls_str_upper(tmp2, tmp); 466 467 /* 468 * scan for % in the string. 469 * If we find one, convert 470 * to the assumed codepage. 471 */ 472 473 if (strchr(tmp2, '%')) { 474 /* use the 1st buffer, we don't need the old string */ 475 servername = tmp; 476 if (!(servername = convert_utf8_to_wincs(unpercent(tmp2)))) { 477 smb_error(dgettext(TEXT_DOMAIN, "bad server name"), 0); 478 return (EINVAL); 479 } 480 /* 481 * Converts utf8 to win equivalent of 482 * what is configured on this machine. 483 * Note that we are assuming this is the 484 * encoding used on the server, and that 485 * assumption might be incorrect. This is 486 * the best we can do now, and we should 487 * move to use port 445 to avoid having 488 * to worry about server codepages. 489 */ 490 } else /* no conversion needed */ 491 servername = tmp2; 492 493 smb_ctx_setserver(ctx, servername); 494 error = smb_ctx_setfullserver(ctx, servername); 495 496 if (error) 497 return (error); 498 if (sharetype == SMB_ST_NONE) { 499 *next = p; 500 return (0); 501 } 502 if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) { 503 smb_error(dgettext(TEXT_DOMAIN, "no share name required"), 0); 504 return (EINVAL); 505 } 506 error = getsubstring(p, '/', p1, sizeof (tmp), &p); 507 if (error) { 508 error = getsubstring(p, '\0', p1, sizeof (tmp), &p); 509 if (error) { 510 smb_error(dgettext(TEXT_DOMAIN, 511 "unexpected end of line"), 0); 512 return (error); 513 } 514 } 515 if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE && 516 !(ctx->ct_flags & SMBCF_BROWSEOK)) { 517 smb_error(dgettext(TEXT_DOMAIN, "empty share name"), 0); 518 return (EINVAL); 519 } 520 *next = p; 521 if (*p1 == 0) 522 return (0); 523 error = smb_ctx_setshare(ctx, unpercent(p1), sharetype); 524 return (error); 525 } 526 527 int 528 smb_ctx_setcharset(struct smb_ctx *ctx, const char *arg) 529 { 530 char *cp, *servercs, *localcs; 531 int cslen = sizeof (ctx->ct_ssn.ioc_localcs); 532 int scslen, lcslen, error; 533 534 cp = strchr(arg, ':'); 535 lcslen = cp ? (cp - arg) : 0; 536 if (lcslen == 0 || lcslen >= cslen) { 537 smb_error(dgettext(TEXT_DOMAIN, 538 "invalid local charset specification (%s)"), 0, arg); 539 return (EINVAL); 540 } 541 scslen = (size_t)strlen(++cp); 542 if (scslen == 0 || scslen >= cslen) { 543 smb_error(dgettext(TEXT_DOMAIN, 544 "invalid server charset specification (%s)"), 0, arg); 545 return (EINVAL); 546 } 547 localcs = memcpy(ctx->ct_ssn.ioc_localcs, arg, lcslen); 548 localcs[lcslen] = 0; 549 servercs = strcpy(ctx->ct_ssn.ioc_servercs, cp); 550 error = nls_setrecode(localcs, servercs); 551 if (error == 0) 552 return (0); 553 smb_error(dgettext(TEXT_DOMAIN, 554 "can't initialize iconv support (%s:%s)"), 555 error, localcs, servercs); 556 localcs[0] = 0; 557 servercs[0] = 0; 558 return (error); 559 } 560 561 int 562 smb_ctx_setfullserver(struct smb_ctx *ctx, const char *name) 563 { 564 ctx->ct_fullserver = strdup(name); 565 if (ctx->ct_fullserver == NULL) 566 return (ENOMEM); 567 return (0); 568 } 569 570 /* 571 * XXX TODO FIXME etc etc 572 * If the call to nbns_getnodestatus(...) fails we can try one of two other 573 * methods; use a name of "*SMBSERVER", which is supported by Samba (at least) 574 * or, as a last resort, try the "truncate-at-dot" heuristic. 575 * And the heuristic really should attempt truncation at 576 * each dot in turn, left to right. 577 * 578 * These fallback heuristics should be triggered when the attempt to open the 579 * session fails instead of in the code below. 580 * 581 * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt 582 */ 583 int 584 smb_ctx_getnbname(struct smb_ctx *ctx, struct sockaddr *sap) 585 { 586 char server[SMB_MAXSRVNAMELEN + 1]; 587 char workgroup[SMB_MAXUSERNAMELEN + 1]; 588 int error; 589 #if 0 590 char *dot; 591 #endif 592 593 server[0] = workgroup[0] = '\0'; 594 error = nbns_getnodestatus(sap, ctx->ct_nb, server, workgroup); 595 if (error == 0) { 596 /* 597 * Used to set our domain name to be the same as 598 * the server's domain name. Unnecessary at best, 599 * and wrong for accounts in a trusted domain. 600 */ 601 #ifdef APPLE 602 if (workgroup[0] && !ctx->ct_ssn.ioc_workgroup[0]) 603 smb_ctx_setworkgroup(ctx, workgroup, 0); 604 #endif 605 if (server[0]) 606 smb_ctx_setserver(ctx, server); 607 } else { 608 if (smb_verbose) 609 smb_error(dgettext(TEXT_DOMAIN, 610 "Failed to get NetBIOS node status."), 0); 611 if (ctx->ct_ssn.ioc_srvname[0] == (char)0) 612 smb_ctx_setserver(ctx, "*SMBSERVER"); 613 } 614 #if 0 615 if (server[0] == (char)0) { 616 dot = strchr(ctx->ct_fullserver, '.'); 617 if (dot) 618 *dot = '\0'; 619 if (strlen(ctx->ct_fullserver) <= SMB_MAXSRVNAMELEN) { 620 /* 621 * don't uppercase the server name. it comes from 622 * NBNS and uppercasing can clobber the characters 623 */ 624 strcpy(ctx->ct_ssn.ioc_srvname, ctx->ct_fullserver); 625 error = 0; 626 } else { 627 error = -1; 628 } 629 if (dot) 630 *dot = '.'; 631 } 632 #endif 633 return (error); 634 } 635 636 /* this routine does not uppercase the server name */ 637 void 638 smb_ctx_setserver(struct smb_ctx *ctx, const char *name) 639 { 640 /* don't uppercase the server name */ 641 if (strlen(name) > SMB_MAXSRVNAMELEN) { /* NB limit is 15 */ 642 ctx->ct_ssn.ioc_srvname[0] = '\0'; 643 } else 644 strcpy(ctx->ct_ssn.ioc_srvname, name); 645 } 646 647 int 648 smb_ctx_setuser(struct smb_ctx *ctx, const char *name, int from_cmd) 649 { 650 651 if (strlen(name) >= SMB_MAXUSERNAMELEN) { 652 smb_error(dgettext(TEXT_DOMAIN, 653 "user name '%s' too long"), 0, name); 654 return (ENAMETOOLONG); 655 } 656 657 /* 658 * Don't overwrite a value from the command line 659 * with one from anywhere else. 660 */ 661 if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_USR)) 662 return (0); 663 664 /* don't uppercase the username, just copy it. */ 665 strcpy(ctx->ct_ssn.ioc_user, name); 666 667 /* Mark this as "from the command line". */ 668 if (from_cmd) 669 ctx->ct_flags |= SMBCF_CMD_USR; 670 671 return (0); 672 } 673 674 /* 675 * Never uppercase the workgroup 676 * name here, because it might come 677 * from a Windows codepage encoding. 678 * 679 * Don't overwrite a domain name from the 680 * command line with one from anywhere else. 681 * See smb_ctx_init() for notes about this. 682 */ 683 int 684 smb_ctx_setworkgroup(struct smb_ctx *ctx, const char *name, int from_cmd) 685 { 686 687 if (strlen(name) >= SMB_MAXUSERNAMELEN) { 688 smb_error(dgettext(TEXT_DOMAIN, 689 "workgroup name '%s' too long"), 0, name); 690 return (ENAMETOOLONG); 691 } 692 693 /* 694 * Don't overwrite a value from the command line 695 * with one from anywhere else. 696 */ 697 if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_DOM)) 698 return (0); 699 700 strcpy(ctx->ct_ssn.ioc_workgroup, name); 701 702 /* Mark this as "from the command line". */ 703 if (from_cmd) 704 ctx->ct_flags |= SMBCF_CMD_DOM; 705 706 return (0); 707 } 708 709 int 710 smb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd, int from_cmd) 711 { 712 713 if (passwd == NULL) /* XXX Huh? */ 714 return (EINVAL); 715 if (strlen(passwd) >= SMB_MAXPASSWORDLEN) { 716 smb_error(dgettext(TEXT_DOMAIN, "password too long"), 0); 717 return (ENAMETOOLONG); 718 } 719 720 /* 721 * Don't overwrite a value from the command line 722 * with one from anywhere else. 723 */ 724 if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_PW)) 725 return (0); 726 727 if (strncmp(passwd, "$$1", 3) == 0) 728 smb_simpledecrypt(ctx->ct_ssn.ioc_password, passwd); 729 else 730 strcpy(ctx->ct_ssn.ioc_password, passwd); 731 strcpy(ctx->ct_sh.ioc_password, ctx->ct_ssn.ioc_password); 732 733 /* Mark this as "from the command line". */ 734 if (from_cmd) 735 ctx->ct_flags |= SMBCF_CMD_PW; 736 737 return (0); 738 } 739 740 int 741 smb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype) 742 { 743 if (strlen(share) >= SMB_MAXSHARENAMELEN) { 744 smb_error(dgettext(TEXT_DOMAIN, 745 "share name '%s' too long"), 0, share); 746 return (ENAMETOOLONG); 747 } 748 if (ctx->ct_origshare) 749 free(ctx->ct_origshare); 750 if ((ctx->ct_origshare = strdup(share)) == NULL) 751 return (ENOMEM); 752 nls_str_upper(ctx->ct_sh.ioc_share, share); 753 if (share[0] != 0) 754 ctx->ct_parsedlevel = SMBL_SHARE; 755 ctx->ct_sh.ioc_stype = stype; 756 return (0); 757 } 758 759 int 760 smb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr) 761 { 762 if (addr == NULL || addr[0] == 0) 763 return (EINVAL); 764 if (ctx->ct_srvaddr) 765 free(ctx->ct_srvaddr); 766 if ((ctx->ct_srvaddr = strdup(addr)) == NULL) 767 return (ENOMEM); 768 return (0); 769 } 770 771 static int 772 smb_parse_owner(char *pair, uid_t *uid, gid_t *gid) 773 { 774 struct group gr; 775 struct passwd pw; 776 char buf[NSS_BUFLEN_PASSWD]; 777 char *cp; 778 779 cp = strchr(pair, ':'); 780 if (cp) { 781 *cp++ = '\0'; 782 if (*cp) { 783 if (getgrnam_r(cp, &gr, buf, sizeof (buf)) != NULL) { 784 *gid = gr.gr_gid; 785 } else 786 smb_error(dgettext(TEXT_DOMAIN, 787 "Invalid group name %s, ignored"), 0, cp); 788 } 789 } 790 if (*pair) { 791 if (getpwnam_r(pair, &pw, buf, sizeof (buf)) != NULL) { 792 *uid = pw.pw_uid; 793 } else 794 smb_error(dgettext(TEXT_DOMAIN, 795 "Invalid user name %s, ignored"), 0, pair); 796 } 797 798 return (0); 799 } 800 801 /* 802 * Commands use this with getopt. See: 803 * STDPARAM_OPT, STDPARAM_ARGS 804 * Called after smb_ctx_readrc(). 805 */ 806 int 807 smb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg) 808 { 809 int error = 0; 810 char *p, *cp; 811 char tmp[1024]; 812 813 switch (opt) { 814 case 'A': 815 case 'U': 816 /* Handled in smb_ctx_init() */ 817 break; 818 case 'I': 819 error = smb_ctx_setsrvaddr(ctx, arg); 820 break; 821 case 'M': 822 ctx->ct_ssn.ioc_rights = strtol(arg, &cp, 8); 823 if (*cp == '/') { 824 ctx->ct_sh.ioc_rights = strtol(cp + 1, &cp, 8); 825 ctx->ct_flags |= SMBCF_SRIGHTS; 826 } 827 break; 828 case 'N': 829 ctx->ct_flags |= SMBCF_NOPWD; 830 break; 831 case 'O': 832 p = strdup(arg); 833 cp = strchr(p, '/'); 834 if (cp) { 835 *cp++ = '\0'; 836 error = smb_parse_owner(cp, &ctx->ct_sh.ioc_owner, 837 &ctx->ct_sh.ioc_group); 838 } 839 if (*p && error == 0) { 840 error = smb_parse_owner(cp, &ctx->ct_ssn.ioc_owner, 841 &ctx->ct_ssn.ioc_group); 842 } 843 free(p); 844 break; 845 case 'P': 846 /* ctx->ct_ssn.ioc_opt |= SMBCOPT_PERMANENT; */ 847 break; 848 case 'R': 849 ctx->ct_ssn.ioc_retrycount = atoi(arg); 850 break; 851 case 'T': 852 ctx->ct_ssn.ioc_timeout = atoi(arg); 853 break; 854 case 'W': 855 nls_str_upper(tmp, arg); 856 error = smb_ctx_setworkgroup(ctx, tmp, TRUE); 857 break; 858 } 859 return (error); 860 } 861 862 #if 0 863 static void 864 smb_hexdump(const uchar_t *buf, int len) { 865 int ofs = 0; 866 867 while (len--) { 868 if (ofs % 16 == 0) 869 printf("\n%02X: ", ofs); 870 printf("%02x ", *buf++); 871 ofs++; 872 } 873 printf("\n"); 874 } 875 #endif 876 877 878 static int 879 smb_addiconvtbl(const char *to, const char *from, const uchar_t *tbl) 880 { 881 int error; 882 883 /* 884 * Not able to find out what is the work of this routine till 885 * now. Still investigating. 886 * REVISIT 887 */ 888 #ifdef KICONV_SUPPORT 889 error = kiconv_add_xlat_table(to, from, tbl); 890 if (error && error != EEXIST) { 891 smb_error(dgettext(TEXT_DOMAIN, 892 "can not setup kernel iconv table (%s:%s)"), 893 error, from, to); 894 return (error); 895 } 896 #endif 897 return (0); 898 } 899 900 /* 901 * Verify context before connect operation(s), 902 * lookup specified server and try to fill all forgotten fields. 903 */ 904 int 905 smb_ctx_resolve(struct smb_ctx *ctx) 906 { 907 struct smbioc_ossn *ssn = &ctx->ct_ssn; 908 struct smbioc_oshare *sh = &ctx->ct_sh; 909 struct nb_name nn; 910 struct sockaddr *sap; 911 struct sockaddr_nb *salocal, *saserver; 912 char *cp; 913 uchar_t cstbl[256]; 914 uint_t i; 915 int error = 0; 916 int browseok = ctx->ct_flags & SMBCF_BROWSEOK; 917 int renego = 0; 918 919 ctx->ct_flags &= ~SMBCF_RESOLVED; 920 if (isatty(STDIN_FILENO)) 921 browseok = 0; 922 if (ctx->ct_fullserver == NULL || ctx->ct_fullserver[0] == 0) { 923 smb_error(dgettext(TEXT_DOMAIN, 924 "no server name specified"), 0); 925 return (EINVAL); 926 } 927 if (ctx->ct_minlevel >= SMBL_SHARE && sh->ioc_share[0] == 0 && 928 !browseok) { 929 smb_error(dgettext(TEXT_DOMAIN, 930 "no share name specified for %s@%s"), 931 0, ssn->ioc_user, ssn->ioc_srvname); 932 return (EINVAL); 933 } 934 error = nb_ctx_resolve(ctx->ct_nb); 935 if (error) 936 return (error); 937 if (ssn->ioc_localcs[0] == 0) 938 strcpy(ssn->ioc_localcs, "default"); /* XXX: locale name ? */ 939 error = smb_addiconvtbl("tolower", ssn->ioc_localcs, nls_lower); 940 if (error) 941 return (error); 942 error = smb_addiconvtbl("toupper", ssn->ioc_localcs, nls_upper); 943 if (error) 944 return (error); 945 if (ssn->ioc_servercs[0] != 0) { 946 for (i = 0; i < sizeof (cstbl); i++) 947 cstbl[i] = i; 948 nls_mem_toext(cstbl, cstbl, sizeof (cstbl)); 949 error = smb_addiconvtbl(ssn->ioc_servercs, ssn->ioc_localcs, 950 cstbl); 951 if (error) 952 return (error); 953 for (i = 0; i < sizeof (cstbl); i++) 954 cstbl[i] = i; 955 nls_mem_toloc(cstbl, cstbl, sizeof (cstbl)); 956 error = smb_addiconvtbl(ssn->ioc_localcs, ssn->ioc_servercs, 957 cstbl); 958 if (error) 959 return (error); 960 } 961 /* 962 * If we have an explicit address set for the server in 963 * an "addr=X" setting in .nsmbrc or SMF, just try using a 964 * gethostbyname() lookup for it. 965 */ 966 if (ctx->ct_srvaddr) { 967 error = nb_resolvehost_in(ctx->ct_srvaddr, &sap); 968 if (error == 0) 969 (void) smb_ctx_getnbname(ctx, sap); 970 } else 971 error = -1; 972 973 /* 974 * Next try a gethostbyname() lookup on the original user- 975 * specified server name. This is similar to Windows 976 * NBT option "Use DNS for name resolution." 977 */ 978 if (error && ctx->ct_fullserver) { 979 error = nb_resolvehost_in(ctx->ct_fullserver, &sap); 980 if (error == 0) 981 (void) smb_ctx_getnbname(ctx, sap); 982 } 983 984 /* 985 * Finally, try the shorter, upper-cased ssn->ioc_srvname 986 * with a NBNS/WINS lookup if the "nbns_enable" property is 987 * true (the default). nbns_resolvename() may unicast to the 988 * "nbns" server or broadcast on the subnet. 989 */ 990 if (error && ssn->ioc_srvname[0] && 991 ctx->ct_nb->nb_flags & NBCF_NS_ENABLE) { 992 error = nbns_resolvename(ssn->ioc_srvname, 993 ctx->ct_nb, &sap); 994 /* 995 * Used to get the NetBIOS node status here. 996 * Not necessary (we have the NetBIOS name). 997 */ 998 } 999 if (error) { 1000 smb_error(dgettext(TEXT_DOMAIN, 1001 "can't get server address"), error); 1002 return (error); 1003 } 1004 1005 /* XXX: no nls_str_upper(ssn->ioc_srvname) here? */ 1006 1007 assert(sizeof (nn.nn_name) == sizeof (ssn->ioc_srvname)); 1008 memcpy(nn.nn_name, ssn->ioc_srvname, NB_NAMELEN); 1009 nn.nn_type = NBT_SERVER; 1010 nn.nn_scope = ctx->ct_nb->nb_scope; 1011 1012 error = nb_sockaddr(sap, &nn, &saserver); 1013 memcpy(&ctx->ct_srvinaddr, sap, sizeof (struct sockaddr_in)); 1014 nb_snbfree(sap); 1015 if (error) { 1016 smb_error(dgettext(TEXT_DOMAIN, 1017 "can't allocate server address"), error); 1018 return (error); 1019 } 1020 /* We know it's a NetBIOS address here. */ 1021 bcopy(saserver, &ssn->ioc_server.nb, 1022 sizeof (struct sockaddr_nb)); 1023 if (ctx->ct_locname[0] == 0) { 1024 error = nb_getlocalname(ctx->ct_locname, 1025 SMB_MAXUSERNAMELEN + 1); 1026 if (error) { 1027 smb_error(dgettext(TEXT_DOMAIN, 1028 "can't get local name"), error); 1029 return (error); 1030 } 1031 nls_str_upper(ctx->ct_locname, ctx->ct_locname); 1032 } 1033 1034 /* XXX: no nls_str_upper(ctx->ct_locname); here? */ 1035 1036 memcpy(nn.nn_name, ctx->ct_locname, NB_NAMELEN); 1037 nn.nn_type = NBT_WKSTA; 1038 nn.nn_scope = ctx->ct_nb->nb_scope; 1039 1040 error = nb_sockaddr(NULL, &nn, &salocal); 1041 if (error) { 1042 nb_snbfree((struct sockaddr *)saserver); 1043 smb_error(dgettext(TEXT_DOMAIN, 1044 "can't allocate local address"), error); 1045 return (error); 1046 } 1047 1048 /* We know it's a NetBIOS address here. */ 1049 bcopy(salocal, &ssn->ioc_local.nb, 1050 sizeof (struct sockaddr_nb)); 1051 1052 error = smb_ctx_negotiate(ctx, SMBL_SHARE, SMBLK_CREATE, 1053 ssn->ioc_workgroup); 1054 if (error) 1055 return (error); 1056 ctx->ct_flags &= ~SMBCF_AUTHREQ; 1057 if (!ctx->ct_secblob && browseok && !sh->ioc_share[0] && 1058 !(ctx->ct_flags & SMBCF_XXX)) { 1059 /* assert: anon share list is subset of overall server shares */ 1060 error = smb_browse(ctx, 1); 1061 if (error) /* user cancel or other error? */ 1062 return (error); 1063 /* 1064 * A share was selected, authenticate button was pressed, 1065 * or anon-authentication failed getting browse list. 1066 */ 1067 } 1068 if ((ctx->ct_secblob == NULL) && (ctx->ct_flags & SMBCF_AUTHREQ || 1069 (ssn->ioc_password[0] == '\0' && 1070 !(ctx->ct_flags & SMBCF_NOPWD)))) { 1071 reauth: 1072 /* 1073 * This function is implemented in both 1074 * ui-apple.c and ui-sun.c so let's try to 1075 * keep the same interface. Not sure why 1076 * they didn't just pass ssn here. 1077 */ 1078 error = smb_get_authentication( 1079 ssn->ioc_workgroup, sizeof (ssn->ioc_workgroup) - 1, 1080 ssn->ioc_user, sizeof (ssn->ioc_user) - 1, 1081 ssn->ioc_password, sizeof (ssn->ioc_password) - 1, 1082 ssn->ioc_srvname, ctx); 1083 if (error) 1084 return (error); 1085 } 1086 /* 1087 * if we have a session it is either anonymous 1088 * or from a stale authentication. re-negotiating 1089 * gets us ready for a fresh session 1090 */ 1091 if (ctx->ct_flags & SMBCF_SSNACTIVE || renego) { 1092 renego = 0; 1093 /* don't clobber workgroup name, pass null arg */ 1094 error = smb_ctx_negotiate(ctx, SMBL_SHARE, SMBLK_CREATE, NULL); 1095 if (error) 1096 return (error); 1097 } 1098 if (browseok && !sh->ioc_share[0]) { 1099 ctx->ct_flags &= ~SMBCF_AUTHREQ; 1100 error = smb_browse(ctx, 0); 1101 if (ctx->ct_flags & SMBCF_KCFOUND && smb_autherr(error)) { 1102 smb_error(dgettext(TEXT_DOMAIN, 1103 "smb_ctx_resolve: bad keychain entry"), 0); 1104 ctx->ct_flags |= SMBCF_KCBAD; 1105 renego = 1; 1106 goto reauth; 1107 } 1108 if (error) /* auth, user cancel, or other error */ 1109 return (error); 1110 /* 1111 * Re-authenticate button was pressed? 1112 */ 1113 if (ctx->ct_flags & SMBCF_AUTHREQ) 1114 goto reauth; 1115 if (!sh->ioc_share[0] && !(ctx->ct_flags & SMBCF_XXX)) { 1116 smb_error(dgettext(TEXT_DOMAIN, 1117 "no share specified for %s@%s"), 1118 0, ssn->ioc_user, ssn->ioc_srvname); 1119 return (EINVAL); 1120 } 1121 } 1122 ctx->ct_flags |= SMBCF_RESOLVED; 1123 1124 if (smb_debug) 1125 dump_ctx("after smb_ctx_resolve", ctx); 1126 1127 return (0); 1128 } 1129 1130 int 1131 smb_open_driver() 1132 { 1133 char buf[20]; 1134 int err, fd, i; 1135 uint32_t version; 1136 1137 /* 1138 * First try to open as clone 1139 */ 1140 fd = open("/dev/"NSMB_NAME, O_RDWR); 1141 if (fd >= 0) 1142 goto opened; 1143 1144 err = errno; /* from open */ 1145 #ifdef APPLE 1146 /* 1147 * well, no clone capabilities available - we have to scan 1148 * all devices in order to get free one 1149 */ 1150 for (i = 0; i < 1024; i++) { 1151 snprintf(buf, sizeof (buf), "/dev/%s%d", NSMB_NAME, i); 1152 fd = open(buf, O_RDWR); 1153 if (fd >= 0) 1154 goto opened; 1155 if (i && POWEROF2(i+1)) 1156 smb_error(dgettext(TEXT_DOMAIN, 1157 "%d failures to open smb device"), errno, i+1); 1158 } 1159 err = ENOENT; 1160 #endif 1161 smb_error(dgettext(TEXT_DOMAIN, 1162 "failed to open %s"), err, "/dev/" NSMB_NAME); 1163 return (-1); 1164 1165 opened: 1166 /* 1167 * Check the driver version (paranoia) 1168 * Do this BEFORE any other ioctl calls. 1169 */ 1170 if (ioctl(fd, SMBIOC_GETVERS, &version) < 0) { 1171 err = errno; 1172 smb_error(dgettext(TEXT_DOMAIN, 1173 "failed to get driver version"), err); 1174 close(fd); 1175 return (-1); 1176 } 1177 if (version != NSMB_VERSION) { 1178 smb_error(dgettext(TEXT_DOMAIN, 1179 "incorrect driver version"), 0); 1180 close(fd); 1181 return (-1); 1182 } 1183 1184 return (fd); 1185 } 1186 1187 static int 1188 smb_ctx_gethandle(struct smb_ctx *ctx) 1189 { 1190 int err, fd; 1191 1192 if (ctx->ct_fd != -1) { 1193 rpc_cleanup_smbctx(ctx); 1194 close(ctx->ct_fd); 1195 ctx->ct_fd = -1; 1196 ctx->ct_flags &= ~SMBCF_SSNACTIVE; 1197 } 1198 1199 fd = smb_open_driver(); 1200 if (fd < 0) 1201 return (ENODEV); 1202 1203 ctx->ct_fd = fd; 1204 return (0); 1205 } 1206 1207 int 1208 smb_ctx_ioctl(struct smb_ctx *ctx, int inum, struct smbioc_lookup *rqp) 1209 { 1210 size_t siz = DEF_SEC_TOKEN_LEN; 1211 int rc = 0; 1212 struct sockaddr sap1, sap2; 1213 int i; 1214 1215 if (rqp->ioc_ssn.ioc_outtok) 1216 free(rqp->ioc_ssn.ioc_outtok); 1217 rqp->ioc_ssn.ioc_outtoklen = siz; 1218 rqp->ioc_ssn.ioc_outtok = malloc(siz+1); 1219 if (rqp->ioc_ssn.ioc_outtok == NULL) 1220 return (ENOMEM); 1221 bzero(rqp->ioc_ssn.ioc_outtok, siz+1); 1222 /* Note: No longer put length in outtok[0] */ 1223 /* *((int *)rqp->ioc_ssn.ioc_outtok) = (int)siz; */ 1224 1225 seteuid(eff_uid); /* restore setuid root briefly */ 1226 if (ioctl(ctx->ct_fd, inum, rqp) == -1) { 1227 rc = errno; 1228 goto out; 1229 } 1230 if (rqp->ioc_ssn.ioc_outtoklen <= siz) 1231 goto out; 1232 1233 /* 1234 * Operation completed, but our output token wasn't large enough. 1235 * The re-call below only pulls the token from the kernel. 1236 */ 1237 siz = rqp->ioc_ssn.ioc_outtoklen; 1238 free(rqp->ioc_ssn.ioc_outtok); 1239 rqp->ioc_ssn.ioc_outtok = malloc(siz + 1); 1240 if (rqp->ioc_ssn.ioc_outtok == NULL) { 1241 rc = ENOMEM; 1242 goto out; 1243 } 1244 bzero(rqp->ioc_ssn.ioc_outtok, siz+1); 1245 /* Note: No longer put length in outtok[0] */ 1246 /* *((int *)rqp->ioc_ssn.ioc_outtok) = siz; */ 1247 if (ioctl(ctx->ct_fd, inum, rqp) == -1) 1248 rc = errno; 1249 out: 1250 seteuid(real_uid); /* and back to real user */ 1251 return (rc); 1252 } 1253 1254 1255 /* 1256 * adds a GSSAPI wrapper 1257 */ 1258 char * 1259 smb_ctx_tkt2gtok(uchar_t *tkt, ulong_t tktlen, 1260 uchar_t **gtokp, ulong_t *gtoklenp) 1261 { 1262 ulong_t bloblen = tktlen; 1263 ulong_t len; 1264 uchar_t krbapreq[2] = "\x01\x00"; /* see RFC 1964 */ 1265 char *failure; 1266 uchar_t *blob = NULL; /* result */ 1267 uchar_t *b; 1268 1269 bloblen += sizeof (krbapreq); 1270 bloblen += g_stcMechOIDList[spnego_mech_oid_Kerberos_V5].iLen; 1271 len = bloblen; 1272 bloblen = ASNDerCalcTokenLength(bloblen, bloblen); 1273 failure = dgettext(TEXT_DOMAIN, "smb_ctx_tkt2gtok malloc"); 1274 if (!(blob = malloc(bloblen))) 1275 goto out; 1276 b = blob; 1277 b += ASNDerWriteToken(b, SPNEGO_NEGINIT_APP_CONSTRUCT, NULL, len); 1278 b += ASNDerWriteOID(b, spnego_mech_oid_Kerberos_V5); 1279 memcpy(b, krbapreq, sizeof (krbapreq)); 1280 b += sizeof (krbapreq); 1281 failure = dgettext(TEXT_DOMAIN, "smb_ctx_tkt2gtok insanity check"); 1282 if (b + tktlen != blob + bloblen) 1283 goto out; 1284 memcpy(b, tkt, tktlen); 1285 *gtoklenp = bloblen; 1286 *gtokp = blob; 1287 failure = NULL; 1288 out:; 1289 if (blob && failure) 1290 free(blob); 1291 return (failure); 1292 } 1293 1294 1295 /* 1296 * Initialization for Kerberos, pulled out of smb_ctx_principal2tkt. 1297 * This just gets our cached credentials, if we have any. 1298 * Based on the "klist" command. 1299 */ 1300 char * 1301 smb_ctx_krb5init(struct smb_ctx *ctx) 1302 { 1303 char *failure; 1304 krb5_error_code kerr; 1305 krb5_context kctx = NULL; 1306 krb5_ccache kcc = NULL; 1307 krb5_principal kprin = NULL; 1308 1309 kerr = krb5_init_context(&kctx); 1310 if (kerr) { 1311 failure = "krb5_init_context"; 1312 goto out; 1313 } 1314 ctx->ct_krb5ctx = kctx; 1315 1316 /* non-default would instead use krb5_cc_resolve */ 1317 kerr = krb5_cc_default(kctx, &kcc); 1318 if (kerr) { 1319 failure = "krb5_cc_default"; 1320 goto out; 1321 } 1322 ctx->ct_krb5cc = kcc; 1323 1324 /* 1325 * Get the client principal (ticket), 1326 * or find out if we don't have one. 1327 */ 1328 kerr = krb5_cc_get_principal(kctx, kcc, &kprin); 1329 if (kerr) { 1330 failure = "krb5_cc_get_principal"; 1331 goto out; 1332 } 1333 ctx->ct_krb5cp = kprin; 1334 1335 if (smb_verbose) { 1336 fprintf(stderr, gettext("Ticket cache: %s:%s\n"), 1337 krb5_cc_get_type(kctx, kcc), 1338 krb5_cc_get_name(kctx, kcc)); 1339 } 1340 failure = NULL; 1341 1342 out: 1343 return (failure); 1344 } 1345 1346 1347 /* 1348 * See "Windows 2000 Kerberos Interoperability" paper by 1349 * Christopher Nebergall. RC4 HMAC is the W2K default but 1350 * Samba support lagged (not due to Samba itself, but due to OS' 1351 * Kerberos implementations.) 1352 * 1353 * Only session enc type should matter, not ticket enc type, 1354 * per Sam Hartman on krbdev. 1355 * 1356 * Preauthentication failure topics in krb-protocol may help here... 1357 * try "John Brezak" and/or "Clifford Neuman" too. 1358 */ 1359 static krb5_enctype kenctypes[] = { 1360 ENCTYPE_ARCFOUR_HMAC, /* defined in Tiger krb5.h */ 1361 ENCTYPE_DES_CBC_MD5, 1362 ENCTYPE_DES_CBC_CRC, 1363 ENCTYPE_NULL 1364 }; 1365 1366 /* 1367 * Obtain a kerberos ticket... 1368 * (if TLD != "gov" then pray first) 1369 */ 1370 char * 1371 smb_ctx_principal2tkt( 1372 struct smb_ctx *ctx, char *prin, 1373 uchar_t **tktp, ulong_t *tktlenp) 1374 { 1375 char *failure; 1376 krb5_context kctx = NULL; 1377 krb5_error_code kerr; 1378 krb5_ccache kcc = NULL; 1379 krb5_principal kprin = NULL, cprn = NULL; 1380 krb5_creds kcreds, *kcredsp = NULL; 1381 krb5_auth_context kauth = NULL; 1382 krb5_data kdata, kdata0; 1383 uchar_t *tkt; 1384 1385 memset((char *)&kcreds, 0, sizeof (kcreds)); 1386 kdata0.length = 0; 1387 1388 /* These shoud have been done in smb_ctx_krb5init() */ 1389 if (ctx->ct_krb5ctx == NULL || 1390 ctx->ct_krb5cc == NULL || 1391 ctx->ct_krb5cp == NULL) { 1392 failure = "smb_ctx_krb5init"; 1393 goto out; 1394 } 1395 kctx = ctx->ct_krb5ctx; 1396 kcc = ctx->ct_krb5cc; 1397 cprn = ctx->ct_krb5cp; 1398 1399 failure = "krb5_set_default_tgs_enctypes"; 1400 if ((kerr = krb5_set_default_tgs_enctypes(kctx, kenctypes))) 1401 goto out; 1402 /* 1403 * The following is an unrolling of krb5_mk_req. Something like: 1404 * krb5_mk_req(kctx, &kauth, 0, service(prin), hostname(prin), 1405 * &kdata0, kcc, &kdata);) 1406 * ...except we needed krb5_parse_name not krb5_sname_to_principal. 1407 */ 1408 failure = "krb5_parse_name"; 1409 if ((kerr = krb5_parse_name(kctx, prin, &kprin))) 1410 goto out; 1411 failure = "krb5_copy_principal(server)"; 1412 if ((kerr = krb5_copy_principal(kctx, kprin, &kcreds.server))) 1413 goto out; 1414 failure = "krb5_copy_principal(client)"; 1415 if ((kerr = krb5_copy_principal(kctx, cprn, &kcreds.client))) 1416 goto out; 1417 failure = "krb5_get_credentials"; 1418 if ((kerr = krb5_get_credentials(kctx, 0, kcc, &kcreds, &kcredsp))) 1419 goto out; 1420 failure = "krb5_mk_req_extended"; 1421 if ((kerr = krb5_mk_req_extended(kctx, &kauth, 0, &kdata0, kcredsp, 1422 &kdata))) 1423 goto out; 1424 failure = "malloc"; 1425 if (!(tkt = malloc(kdata.length))) { 1426 krb5_free_data_contents(kctx, &kdata); 1427 goto out; 1428 } 1429 *tktlenp = kdata.length; 1430 memcpy(tkt, kdata.data, kdata.length); 1431 krb5_free_data_contents(kctx, &kdata); 1432 *tktp = tkt; 1433 failure = NULL; 1434 out:; 1435 if (kerr) { 1436 if (!failure) 1437 failure = "smb_ctx_principal2tkt"; 1438 /* 1439 * Avoid logging the typical "No credentials cache found" 1440 */ 1441 if (kerr != KRB5_FCC_NOFILE || 1442 strcmp(failure, "krb5_cc_get_principal")) 1443 com_err(__progname, kerr, failure); 1444 } 1445 if (kauth) 1446 krb5_auth_con_free(kctx, kauth); 1447 if (kcredsp) 1448 krb5_free_creds(kctx, kcredsp); 1449 if (kcreds.server || kcreds.client) 1450 krb5_free_cred_contents(kctx, &kcreds); 1451 if (kprin) 1452 krb5_free_principal(kctx, kprin); 1453 1454 /* Free kctx in smb_ctx_done */ 1455 1456 return (failure); 1457 } 1458 1459 char * 1460 smb_ctx_principal2blob( 1461 struct smb_ctx *ctx, 1462 smbioc_ossn_t *ssn, 1463 char *prin) 1464 { 1465 int rc = 0; 1466 char *failure; 1467 uchar_t *tkt = NULL; 1468 ulong_t tktlen; 1469 uchar_t *gtok = NULL; /* gssapi token */ 1470 ulong_t gtoklen; /* gssapi token length */ 1471 SPNEGO_TOKEN_HANDLE stok = NULL; /* spnego token */ 1472 void *blob = NULL; /* result */ 1473 ulong_t bloblen; /* result length */ 1474 1475 if ((failure = smb_ctx_principal2tkt(ctx, prin, &tkt, &tktlen))) 1476 goto out; 1477 if ((failure = smb_ctx_tkt2gtok(tkt, tktlen, >ok, >oklen))) 1478 goto out; 1479 /* 1480 * RFC says to send NegTokenTarg now. So does MS docs. But 1481 * win2k gives ERRbaduid if we do... we must send 1482 * another NegTokenInit now! 1483 */ 1484 failure = "spnegoCreateNegTokenInit"; 1485 if ((rc = spnegoCreateNegTokenInit(spnego_mech_oid_Kerberos_V5_Legacy, 1486 0, gtok, gtoklen, NULL, 0, &stok))) 1487 goto out; 1488 failure = "spnegoTokenGetBinary(NULL)"; 1489 rc = spnegoTokenGetBinary(stok, NULL, &bloblen); 1490 if (rc != SPNEGO_E_BUFFER_TOO_SMALL) 1491 goto out; 1492 failure = "malloc"; 1493 if (!(blob = malloc((size_t)bloblen))) 1494 goto out; 1495 /* No longer store length at start of blob. */ 1496 /* *blob = bloblen; */ 1497 failure = "spnegoTokenGetBinary"; 1498 if ((rc = spnegoTokenGetBinary(stok, blob, &bloblen))) 1499 goto out; 1500 ssn->ioc_intoklen = bloblen; 1501 ssn->ioc_intok = blob; 1502 failure = NULL; 1503 out:; 1504 if (rc) { 1505 /* XXX better is to embed rc in failure */ 1506 smb_error(dgettext(TEXT_DOMAIN, 1507 "spnego principal2blob error %d"), 0, -rc); 1508 if (!failure) 1509 failure = "spnego"; 1510 } 1511 if (blob && failure) 1512 free(blob); 1513 if (stok) 1514 spnegoFreeData(stok); 1515 if (gtok) 1516 free(gtok); 1517 if (tkt) 1518 free(tkt); 1519 return (failure); 1520 } 1521 1522 1523 #if 0 1524 void 1525 prblob(uchar_t *b, size_t len) 1526 { 1527 while (len--) 1528 fprintf(stderr, "%02x", *b++); 1529 fprintf(stderr, "\n"); 1530 } 1531 #endif 1532 1533 1534 /* 1535 * We navigate the SPNEGO & ASN1 encoding to find a kerberos principal 1536 * Note: driver no longer puts length at start of blob. 1537 */ 1538 char * 1539 smb_ctx_blob2principal( 1540 struct smb_ctx *ctx, 1541 smbioc_ossn_t *ssn, 1542 char **prinp) 1543 { 1544 uchar_t *blob = ssn->ioc_outtok; 1545 size_t len = ssn->ioc_outtoklen; 1546 int rc = 0; 1547 SPNEGO_TOKEN_HANDLE stok = NULL; 1548 int indx = 0; 1549 char *failure; 1550 uchar_t flags = 0; 1551 unsigned long plen = 0; 1552 uchar_t *prin; 1553 1554 #if 0 1555 fprintf(stderr, "blob from negotiate:\n"); 1556 prblob(blob, len); 1557 #endif 1558 1559 /* Skip the GUID */ 1560 assert(len >= SMB_GUIDLEN); 1561 blob += SMB_GUIDLEN; 1562 len -= SMB_GUIDLEN; 1563 1564 failure = "spnegoInitFromBinary"; 1565 if ((rc = spnegoInitFromBinary(blob, len, &stok))) 1566 goto out; 1567 /* 1568 * Needn't use new Kerberos OID - the Legacy one is fine. 1569 */ 1570 failure = "spnegoIsMechTypeAvailable"; 1571 if (spnegoIsMechTypeAvailable(stok, spnego_mech_oid_Kerberos_V5_Legacy, 1572 &indx)) 1573 goto out; 1574 /* 1575 * Ignoring optional context flags for now. May want to pass 1576 * them to krb5 layer. XXX 1577 */ 1578 if (!spnegoGetContextFlags(stok, &flags)) 1579 fprintf(stderr, dgettext(TEXT_DOMAIN, 1580 "spnego context flags 0x%x\n"), flags); 1581 failure = "spnegoGetMechListMIC(NULL)"; 1582 rc = spnegoGetMechListMIC(stok, NULL, &plen); 1583 if (rc != SPNEGO_E_BUFFER_TOO_SMALL) 1584 goto out; 1585 failure = "malloc"; 1586 if (!(prin = malloc(plen + 1))) 1587 goto out; 1588 failure = "spnegoGetMechListMIC"; 1589 if ((rc = spnegoGetMechListMIC(stok, prin, &plen))) { 1590 free(prin); 1591 goto out; 1592 } 1593 prin[plen] = '\0'; 1594 *prinp = (char *)prin; 1595 failure = NULL; 1596 out:; 1597 if (stok) 1598 spnegoFreeData(stok); 1599 if (rc) { 1600 /* XXX better is to embed rc in failure */ 1601 smb_error(dgettext(TEXT_DOMAIN, 1602 "spnego blob2principal error %d"), 0, -rc); 1603 if (!failure) 1604 failure = "spnego"; 1605 } 1606 return (failure); 1607 } 1608 1609 1610 int 1611 smb_ctx_negotiate(struct smb_ctx *ctx, int level, int flags, char *workgroup) 1612 { 1613 struct smbioc_lookup rq; 1614 int error = 0; 1615 char *failure = NULL; 1616 char *principal = NULL; 1617 char c; 1618 int i; 1619 ssize_t *outtoklen; 1620 uchar_t *blob; 1621 1622 /* 1623 * We leave ct_secblob set iff extended security 1624 * negotiation succeeds. 1625 */ 1626 if (ctx->ct_secblob) { 1627 free(ctx->ct_secblob); 1628 ctx->ct_secblob = NULL; 1629 } 1630 #ifdef XXX 1631 if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) { 1632 smb_error(dgettext(TEXT_DOMAIN, 1633 "smb_ctx_lookup() data is not resolved"), 0); 1634 return (EINVAL); 1635 } 1636 #endif 1637 if ((error = smb_ctx_gethandle(ctx))) 1638 return (error); 1639 1640 bzero(&rq, sizeof (rq)); 1641 bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn)); 1642 bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare)); 1643 1644 /* 1645 * Find out if we have a Kerberos ticket, 1646 * and only offer SPNEGO if we have one. 1647 */ 1648 failure = smb_ctx_krb5init(ctx); 1649 if (failure) { 1650 if (smb_verbose) 1651 smb_error(failure, 0); 1652 goto out; 1653 } 1654 1655 rq.ioc_flags = flags; 1656 rq.ioc_level = level; 1657 rq.ioc_ssn.ioc_opt |= SMBVOPT_EXT_SEC; 1658 error = smb_ctx_ioctl(ctx, SMBIOC_NEGOTIATE, &rq); 1659 if (error) { 1660 failure = dgettext(TEXT_DOMAIN, "negotiate failed"); 1661 smb_error(failure, error); 1662 if (error == ETIMEDOUT) 1663 return (error); 1664 goto out; 1665 } 1666 /* 1667 * If the server capabilities did not include 1668 * SMB_CAP_EXT_SECURITY then the driver clears 1669 * the flag SMBVOPT_EXT_SEC for us. 1670 * XXX: should add the capabilities to ioc_ssn 1671 * XXX: see comment in driver - smb_usr.c 1672 */ 1673 failure = dgettext(TEXT_DOMAIN, "SPNEGO unsupported"); 1674 if ((rq.ioc_ssn.ioc_opt & SMBVOPT_EXT_SEC) == 0) { 1675 if (smb_verbose) 1676 smb_error(failure, 0); 1677 /* 1678 * Do regular (old style) NTLM or NTLMv2 1679 * Nothing more to do here in negotiate. 1680 */ 1681 return (0); 1682 } 1683 1684 /* 1685 * Capabilities DO include SMB_CAP_EXT_SECURITY, 1686 * so this should be an SPNEGO security blob. 1687 * Parse the ASN.1/DER, prepare response(s). 1688 * XXX: Handle STATUS_MORE_PROCESSING_REQUIRED? 1689 * XXX: Requires additional session setup calls. 1690 */ 1691 if (rq.ioc_ssn.ioc_outtoklen <= SMB_GUIDLEN) 1692 goto out; 1693 /* some servers send padding junk */ 1694 blob = rq.ioc_ssn.ioc_outtok; 1695 if (blob[0] == 0) 1696 goto out; 1697 1698 failure = smb_ctx_blob2principal( 1699 ctx, &rq.ioc_ssn, &principal); 1700 if (failure) 1701 goto out; 1702 failure = smb_ctx_principal2blob( 1703 ctx, &rq.ioc_ssn, principal); 1704 if (failure) 1705 goto out; 1706 1707 /* Success! Save the blob to send next. */ 1708 ctx->ct_secblob = rq.ioc_ssn.ioc_intok; 1709 ctx->ct_secbloblen = rq.ioc_ssn.ioc_intoklen; 1710 rq.ioc_ssn.ioc_intok = NULL; 1711 1712 out: 1713 if (principal) 1714 free(principal); 1715 if (rq.ioc_ssn.ioc_intok) 1716 free(rq.ioc_ssn.ioc_intok); 1717 if (rq.ioc_ssn.ioc_outtok) 1718 free(rq.ioc_ssn.ioc_outtok); 1719 if (!failure) 1720 return (0); /* Success! */ 1721 1722 /* 1723 * Negotiate failed with "extended security". 1724 * 1725 * XXX: If we are doing SPNEGO correctly, 1726 * we should never get here unless the user 1727 * supplied invalid authentication data, 1728 * or we saw some kind of protocol error. 1729 * 1730 * XXX: The error message below should be 1731 * XXX: unconditional (remove "if verbose") 1732 * XXX: but not until we have "NTLMSSP" 1733 * Avoid spew for anticipated failure modes 1734 * but enable this with the verbose flag 1735 */ 1736 if (smb_verbose) { 1737 smb_error(dgettext(TEXT_DOMAIN, 1738 "%s (extended security negotiate)"), error, failure); 1739 } 1740 1741 /* 1742 * XXX: Try again using NTLM (or NTLMv2) 1743 * XXX: Normal clients don't do this. 1744 * XXX: Should just return an error, but 1745 * keep the fall-back to NTLM for now. 1746 * 1747 * Start over with a new connection. 1748 */ 1749 if ((error = smb_ctx_gethandle(ctx))) 1750 return (error); 1751 bzero(&rq, sizeof (rq)); 1752 bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn)); 1753 bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare)); 1754 rq.ioc_flags = flags; 1755 rq.ioc_level = level; 1756 /* Note: NO SMBVOPT_EXT_SEC */ 1757 error = smb_ctx_ioctl(ctx, SMBIOC_NEGOTIATE, &rq); 1758 if (error) { 1759 failure = dgettext(TEXT_DOMAIN, "negotiate failed"); 1760 smb_error(failure, error); 1761 rpc_cleanup_smbctx(ctx); 1762 close(ctx->ct_fd); 1763 ctx->ct_fd = -1; 1764 return (error); 1765 } 1766 1767 /* 1768 * Used to copy the workgroup out of the SMB_NEGOTIATE response 1769 * here, to default our domain name to be the same as the server. 1770 * Not a good idea: Unnecessary at best, and sometimes wrong, i.e. 1771 * when our account is in a trusted domain. 1772 */ 1773 1774 return (error); 1775 } 1776 1777 1778 int 1779 smb_ctx_tdis(struct smb_ctx *ctx) 1780 { 1781 struct smbioc_lookup rq; /* XXX may be used, someday */ 1782 int error = 0; 1783 1784 if (ctx->ct_fd < 0) { 1785 smb_error(dgettext(TEXT_DOMAIN, 1786 "tree disconnect without handle?!"), 0); 1787 return (EINVAL); 1788 } 1789 if (!(ctx->ct_flags & SMBCF_SSNACTIVE)) { 1790 smb_error(dgettext(TEXT_DOMAIN, 1791 "tree disconnect without session?!"), 0); 1792 return (EINVAL); 1793 } 1794 bzero(&rq, sizeof (rq)); 1795 bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn)); 1796 bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare)); 1797 if (ioctl(ctx->ct_fd, SMBIOC_TDIS, &rq) == -1) { 1798 error = errno; 1799 smb_error(dgettext(TEXT_DOMAIN, 1800 "tree disconnect failed"), error); 1801 } 1802 return (error); 1803 } 1804 1805 1806 int 1807 smb_ctx_lookup(struct smb_ctx *ctx, int level, int flags) 1808 { 1809 struct smbioc_lookup rq; 1810 int error = 0; 1811 char *failure = NULL; 1812 1813 if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) { 1814 smb_error(dgettext(TEXT_DOMAIN, 1815 "smb_ctx_lookup() data is not resolved"), 0); 1816 return (EINVAL); 1817 } 1818 if (ctx->ct_fd < 0) { 1819 smb_error(dgettext(TEXT_DOMAIN, 1820 "handle from smb_ctx_nego() gone?!"), 0); 1821 return (EINVAL); 1822 } 1823 if (!(flags & SMBLK_CREATE)) 1824 return (0); 1825 bzero(&rq, sizeof (rq)); 1826 bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn)); 1827 bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare)); 1828 rq.ioc_flags = flags; 1829 rq.ioc_level = level; 1830 1831 /* 1832 * Iff we have a security blob, we're using 1833 * extended security... 1834 */ 1835 if (ctx->ct_secblob) { 1836 rq.ioc_ssn.ioc_opt |= SMBVOPT_EXT_SEC; 1837 if (!(ctx->ct_flags & SMBCF_SSNACTIVE)) { 1838 rq.ioc_ssn.ioc_intok = ctx->ct_secblob; 1839 rq.ioc_ssn.ioc_intoklen = ctx->ct_secbloblen; 1840 error = smb_ctx_ioctl(ctx, SMBIOC_SSNSETUP, &rq); 1841 } 1842 rq.ioc_ssn.ioc_intok = NULL; 1843 if (error) { 1844 failure = dgettext(TEXT_DOMAIN, 1845 "session setup failed"); 1846 } else { 1847 ctx->ct_flags |= SMBCF_SSNACTIVE; 1848 if ((error = smb_ctx_ioctl(ctx, SMBIOC_TCON, &rq))) 1849 failure = dgettext(TEXT_DOMAIN, 1850 "tree connect failed"); 1851 } 1852 if (rq.ioc_ssn.ioc_intok) 1853 free(rq.ioc_ssn.ioc_intok); 1854 if (rq.ioc_ssn.ioc_outtok) 1855 free(rq.ioc_ssn.ioc_outtok); 1856 if (!failure) 1857 return (0); 1858 smb_error(dgettext(TEXT_DOMAIN, 1859 "%s (extended security lookup2)"), error, failure); 1860 /* unwise to failback to NTLM now */ 1861 return (error); 1862 } 1863 1864 /* 1865 * Otherwise we're doing plain old NTLM 1866 */ 1867 seteuid(eff_uid); /* restore setuid root briefly */ 1868 if ((ctx->ct_flags & SMBCF_SSNACTIVE) == 0) { 1869 /* 1870 * This is the magic that tells the driver to 1871 * copy the password from the keychain, and 1872 * whether to use the system name or the 1873 * account domain to lookup the keychain. 1874 */ 1875 if (ctx->ct_flags & SMBCF_KCFOUND) 1876 rq.ioc_ssn.ioc_opt |= SMBVOPT_USE_KEYCHAIN; 1877 if (ctx->ct_flags & SMBCF_KCDOMAIN) 1878 rq.ioc_ssn.ioc_opt |= SMBVOPT_KC_DOMAIN; 1879 if (ioctl(ctx->ct_fd, SMBIOC_SSNSETUP, &rq) < 0) { 1880 error = errno; 1881 failure = dgettext(TEXT_DOMAIN, "session setup"); 1882 goto out; 1883 } 1884 ctx->ct_flags |= SMBCF_SSNACTIVE; 1885 } 1886 if (ioctl(ctx->ct_fd, SMBIOC_TCON, &rq) == -1) { 1887 error = errno; 1888 failure = dgettext(TEXT_DOMAIN, "tree connect"); 1889 } 1890 1891 out: 1892 seteuid(real_uid); /* and back to real user */ 1893 if (failure) { 1894 error = errno; 1895 smb_error(dgettext(TEXT_DOMAIN, 1896 "%s phase failed"), error, failure); 1897 } 1898 return (error); 1899 } 1900 1901 /* 1902 * Return the hflags2 word for an smb_ctx. 1903 */ 1904 int 1905 smb_ctx_flags2(struct smb_ctx *ctx) 1906 { 1907 uint16_t flags2; 1908 1909 if (ioctl(ctx->ct_fd, SMBIOC_FLAGS2, &flags2) == -1) { 1910 smb_error(dgettext(TEXT_DOMAIN, 1911 "can't get flags2 for a session"), errno); 1912 return (-1); 1913 } 1914 printf(dgettext(TEXT_DOMAIN, "Flags2 value is %d\n"), flags2); 1915 return (flags2); 1916 } 1917 1918 /* 1919 * level values: 1920 * 0 - default 1921 * 1 - server 1922 * 2 - server:user 1923 * 3 - server:user:share 1924 */ 1925 static int 1926 smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level) 1927 { 1928 char *p; 1929 int error; 1930 1931 #ifdef NOT_DEFINED 1932 if (level > 0) { 1933 rc_getstringptr(smb_rc, sname, "charsets", &p); 1934 if (p) { 1935 error = smb_ctx_setcharset(ctx, p); 1936 if (error) 1937 smb_error(dgettext(TEXT_DOMAIN, 1938 "charset specification in the section '%s' ignored"), 1939 error, sname); 1940 } 1941 } 1942 #endif 1943 1944 if (level <= 1) { 1945 /* Section is: [default] or [server] */ 1946 1947 rc_getint(smb_rc, sname, "timeout", 1948 &ctx->ct_ssn.ioc_timeout); 1949 1950 #ifdef NOT_DEFINED 1951 rc_getint(smb_rc, sname, "retry_count", 1952 &ctx->ct_ssn.ioc_retrycount); 1953 rc_getstringptr(smb_rc, sname, "use_negprot_domain", &p); 1954 if (p && strcmp(p, "NO") == 0) 1955 ctx->ct_flags |= SMBCF_NONEGDOM; 1956 #endif 1957 1958 rc_getstringptr(smb_rc, sname, "minauth", &p); 1959 if (p) { 1960 /* 1961 * "minauth" was set in this section; override 1962 * the current minimum authentication setting. 1963 */ 1964 ctx->ct_ssn.ioc_opt &= ~SMBVOPT_MINAUTH; 1965 if (strcmp(p, "kerberos") == 0) { 1966 /* 1967 * Don't fall back to NTLMv2, NTLMv1, or 1968 * a clear text password. 1969 */ 1970 ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_KERBEROS; 1971 } else if (strcmp(p, "ntlmv2") == 0) { 1972 /* 1973 * Don't fall back to NTLMv1 or a clear 1974 * text password. 1975 */ 1976 ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NTLMV2; 1977 } else if (strcmp(p, "ntlm") == 0) { 1978 /* 1979 * Don't send the LM response over the wire. 1980 */ 1981 ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NTLM; 1982 } else if (strcmp(p, "lm") == 0) { 1983 /* 1984 * Fail if the server doesn't do encrypted 1985 * passwords. 1986 */ 1987 ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_LM; 1988 } else if (strcmp(p, "none") == 0) { 1989 /* 1990 * Anything goes. 1991 * (The following statement should be 1992 * optimized away.) 1993 */ 1994 /* LINTED */ 1995 ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NONE; 1996 } else { 1997 /* 1998 * Unknown minimum authentication level. 1999 */ 2000 smb_error(dgettext(TEXT_DOMAIN, 2001 "invalid minimum authentication level \"%s\" specified in the section %s"), 2002 0, p, sname); 2003 return (EINVAL); 2004 } 2005 } 2006 2007 /* 2008 * Domain name. Allow both keywords: 2009 * "workgroup", "domain" 2010 * 2011 * Note: these are NOT marked "from CMD". 2012 * See long comment at smb_ctx_init() 2013 */ 2014 rc_getstringptr(smb_rc, sname, "workgroup", &p); 2015 if (p) { 2016 nls_str_upper(p, p); 2017 error = smb_ctx_setworkgroup(ctx, p, 0); 2018 if (error) 2019 smb_error(dgettext(TEXT_DOMAIN, 2020 "workgroup specification in the " 2021 "section '%s' ignored"), error, sname); 2022 } 2023 rc_getstringptr(smb_rc, sname, "domain", &p); 2024 if (p) { 2025 nls_str_upper(p, p); 2026 error = smb_ctx_setworkgroup(ctx, p, 0); 2027 if (error) 2028 smb_error(dgettext(TEXT_DOMAIN, 2029 "domain specification in the " 2030 "section '%s' ignored"), error, sname); 2031 } 2032 2033 rc_getstringptr(smb_rc, sname, "user", &p); 2034 if (p) { 2035 error = smb_ctx_setuser(ctx, p, 0); 2036 if (error) 2037 smb_error(dgettext(TEXT_DOMAIN, 2038 "user specification in the " 2039 "section '%s' ignored"), error, sname); 2040 } 2041 } 2042 2043 if (level == 1) { 2044 /* Section is: [server] */ 2045 rc_getstringptr(smb_rc, sname, "addr", &p); 2046 if (p) { 2047 error = smb_ctx_setsrvaddr(ctx, p); 2048 if (error) { 2049 smb_error(dgettext(TEXT_DOMAIN, 2050 "invalid address specified in section %s"), 2051 0, sname); 2052 return (error); 2053 } 2054 } 2055 } 2056 2057 rc_getstringptr(smb_rc, sname, "password", &p); 2058 if (p) { 2059 error = smb_ctx_setpassword(ctx, p, 0); 2060 if (error) 2061 smb_error(dgettext(TEXT_DOMAIN, 2062 "password specification in the section '%s' ignored"), 2063 error, sname); 2064 } 2065 2066 return (0); 2067 } 2068 2069 /* 2070 * read rc file as follows: 2071 * 0: read [default] section 2072 * 1: override with [server] section 2073 * 2: override with [server:user] section 2074 * 3: override with [server:user:share] section 2075 * Since absence of rcfile is not fatal, silently ignore this fact. 2076 * smb_rc file should be closed by caller. 2077 */ 2078 int 2079 smb_ctx_readrc(struct smb_ctx *ctx) 2080 { 2081 char sname[SMB_MAXSRVNAMELEN + SMB_MAXUSERNAMELEN + 2082 SMB_MAXSHARENAMELEN + 4]; 2083 2084 if (smb_open_rcfile(ctx) != 0) 2085 goto done; 2086 2087 /* 2088 * default parameters (level=0) 2089 */ 2090 smb_ctx_readrcsection(ctx, "default", 0); 2091 nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0); 2092 2093 /* 2094 * If we don't have a server name, we can't read any of the 2095 * [server...] sections. 2096 */ 2097 if (ctx->ct_ssn.ioc_srvname[0] == 0) 2098 goto done; 2099 2100 /* 2101 * SERVER parameters. 2102 */ 2103 smb_ctx_readrcsection(ctx, ctx->ct_ssn.ioc_srvname, 1); 2104 2105 /* 2106 * If we don't have a user name, we can't read any of the 2107 * [server:user...] sections. 2108 */ 2109 if (ctx->ct_ssn.ioc_user[0] == 0) 2110 goto done; 2111 2112 /* 2113 * SERVER:USER parameters 2114 */ 2115 snprintf(sname, sizeof (sname), "%s:%s", 2116 ctx->ct_ssn.ioc_srvname, 2117 ctx->ct_ssn.ioc_user); 2118 smb_ctx_readrcsection(ctx, sname, 2); 2119 2120 /* 2121 * If we don't have a share name, we can't read any of the 2122 * [server:user:share] sections. 2123 */ 2124 if (ctx->ct_sh.ioc_share[0] != 0) { 2125 /* 2126 * SERVER:USER:SHARE parameters 2127 */ 2128 snprintf(sname, sizeof (sname), "%s:%s:%s", 2129 ctx->ct_ssn.ioc_srvname, 2130 ctx->ct_ssn.ioc_user, 2131 ctx->ct_sh.ioc_share); 2132 smb_ctx_readrcsection(ctx, sname, 3); 2133 } 2134 2135 done: 2136 if (smb_debug) 2137 dump_ctx("after smb_ctx_readrc", ctx); 2138 2139 return (0); 2140 } 2141