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