Print this page
    
NEX-18708 Domain logons may get STATUS_ACCESS_DENIED
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-14666 Need to provide SMB 2.1 Client
NEX-17187 panic in smbfs_acl_store
NEX-17231 smbfs create xattr files finds wrong file
NEX-17224 smbfs lookup EINVAL should be ENOENT
NEX-17260 SMB1 client fails to list directory after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
and: (cleanup)
1575 untangle libmlrpc from SMB server
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
NEX-4083 Upstream changes from illumos 5917 and 5995
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-2667 Wrong error when join domain with wrong password
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
SUP-621 ... join AD domain, ... ACCESS_DENIED when trying to open \lsarpc
NEX-2225 Unable to join NexentaStor to 2008 AD
NEX-2286 smbadm join error messages are uninformative
NEX-1852 re-enable Kerberos-style AD join (try 2)
NEX-1638 Updated DC Locator
 Includes work by: matt.barden@nexenta.com, kevin.crowe@nexenta.com
SFR-56 Identity Management for UNIX (IDMU) authentication support
re #13190 rb4312 idmapd error -9961 (No AD servers)
re #12435 rb3958 r10 is added 2 times to panic info
re #12393 rb3935 Kerberos and smbd disagree about who is our AD server
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c
          +++ new/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  
    | 
      ↓ open down ↓ | 
    12 lines elided | 
    
      ↑ open up ↑ | 
  
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23      - * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
       23 + * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  24   24   */
  25   25  
  26   26  /*
  27   27   * Utility functions to support the RPC interface library.
  28   28   */
  29   29  
  30   30  #include <stdio.h>
  31   31  #include <stdarg.h>
  32   32  #include <strings.h>
  33   33  #include <unistd.h>
  34   34  #include <netdb.h>
  35   35  #include <stdlib.h>
  36   36  #include <sys/time.h>
  37   37  #include <sys/systm.h>
  38   38  #include <note.h>
  39   39  #include <syslog.h>
  40   40  
  41   41  #include <smbsrv/libsmb.h>
  42   42  #include <smbsrv/libsmbns.h>
  43   43  #include <smbsrv/libmlsvc.h>
  44      -#include <smbsrv/ntaccess.h>
       44 +#include <smb/ntaccess.h>
  45   45  #include <smbsrv/smbinfo.h>
  46   46  #include <smbsrv/netrauth.h>
  47   47  #include <libsmbrdr.h>
  48   48  #include <lsalib.h>
  49   49  #include <samlib.h>
  50   50  #include <mlsvc.h>
  51   51  
  52   52  static DWORD
  53   53  mlsvc_join_rpc(smb_domainex_t *dxi,
  54   54          char *admin_user, char *admin_pw,
  55   55          char *machine_name, char *machine_pw);
  56   56  static DWORD
  57   57  mlsvc_join_noauth(smb_domainex_t *dxi,
  58   58          char *machine_name, char *machine_pw);
  59   59  
  60      -
       60 +/*
       61 + * This is called by smbd_dc_update just after we've learned about a
       62 + * new domain controller.  Make sure we can authenticate with this DC.
       63 + */
  61   64  DWORD
  62   65  mlsvc_netlogon(char *server, char *domain)
  63   66  {
  64      -        mlsvc_handle_t netr_handle;
  65   67          DWORD status;
  66   68  
  67      -        status = netr_open(server, domain, &netr_handle);
  68      -        if (status != 0) {
  69      -                syslog(LOG_NOTICE, "Failed to connect to %s "
  70      -                    "for domain %s (%s)", server, domain,
  71      -                    xlate_nt_status(status));
  72      -                return (status);
  73      -        }
  74      -
  75      -        status = netlogon_auth(server, &netr_handle, NETR_FLG_INIT);
       69 +        status = smb_netlogon_check(server, domain);
  76   70          if (status != NT_STATUS_SUCCESS) {
  77   71                  syslog(LOG_NOTICE, "Failed to establish NETLOGON "
  78   72                      "credential chain with DC: %s (%s)", server,
  79   73                      xlate_nt_status(status));
  80   74                  syslog(LOG_NOTICE, "The machine account information on the "
  81   75                      "domain controller does not match the local storage.");
  82   76                  syslog(LOG_NOTICE, "To correct this, use 'smbadm join'");
  83   77          }
  84      -        (void) netr_close(&netr_handle);
  85   78  
  86   79          return (status);
  87   80  }
  88   81  
  89   82  /*
  90   83   * Join the specified domain.  The method varies depending on whether
  91   84   * we're using "secure join" (using an administrative account to join)
  92   85   * or "unsecure join" (using a pre-created machine account).  In the
  93   86   * latter case, the machine account is created "by hand" before this
  94   87   * machine attempts to join, and we just change the password from the
  95   88   * (weak) default password for a new machine account to a random one.
  96   89   *
  97   90   * Returns NT status codes.
  98   91   */
  99   92  void
 100   93  mlsvc_join(smb_joininfo_t *info, smb_joinres_t *res)
 101   94  {
 102   95          static unsigned char zero_hash[SMBAUTH_HASH_SZ];
 103   96          char machine_name[SMB_SAMACCT_MAXLEN];
 104   97          char machine_pw[NETR_MACHINE_ACCT_PASSWD_MAX];
 105   98          unsigned char passwd_hash[SMBAUTH_HASH_SZ];
 106   99          smb_domainex_t dxi;
 107  100          smb_domain_t *di = &dxi.d_primary;
 108  101          DWORD status;
 109  102          int rc;
 110  103  
 111  104          bzero(&dxi, sizeof (dxi));
 112  105  
 113  106          /*
 114  107           * Domain join support: AD (Kerberos+LDAP) or MS-RPC?
 115  108           */
 116  109          boolean_t ads_enabled = smb_config_get_ads_enable();
 117  110  
 118  111          if (smb_getsamaccount(machine_name, sizeof (machine_name)) != 0) {
 119  112                  res->status = NT_STATUS_INVALID_COMPUTER_NAME;
 120  113                  return;
 121  114          }
 122  115  
 123  116          (void) smb_gen_random_passwd(machine_pw, sizeof (machine_pw));
 124  117  
 125  118          /*
 126  119           * Ensure that any previous membership of this domain has
 127  120           * been cleared from the environment before we start. This
 128  121           * will ensure that we don't attempt a NETLOGON_SAMLOGON
 129  122           * when attempting to find the PDC.
 130  123           */
 131  124          (void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE);
 132  125  
 133  126          if (info->domain_username[0] != '\0') {
 134  127                  (void) smb_auth_ntlm_hash(info->domain_passwd, passwd_hash);
 135  128                  smb_ipc_set(info->domain_username, passwd_hash);
 136  129          } else {
 137  130                  smb_ipc_set(MLSVC_ANON_USER, zero_hash);
 138  131          }
 139  132  
 140  133          /*
 141  134           * Tentatively set the idmap domain to the one we're joining,
 142  135           * so that the DC locator in idmap knows what to look for.
 143  136           * Ditto the SMB server domain.
 144  137           */
 145  138          if (smb_config_set_idmap_domain(info->domain_name) != 0)
 146  139                  syslog(LOG_NOTICE, "Failed to set idmap domain name");
 147  140          if (smb_config_refresh_idmap() != 0)
 148  141                  syslog(LOG_NOTICE, "Failed to refresh idmap service");
 149  142  
 150  143          /* Clear DNS local (ADS) lookup cache. */
 151  144          smb_ads_refresh(B_FALSE);
 152  145  
 153  146          /*
 154  147           * Locate a DC for this domain.  Intentionally bypass the
 155  148           * ddiscover service here because we're still joining.
 156  149           * This also allows better reporting of any failures.
 157  150           */
 158  151          status = smb_ads_lookup_msdcs(info->domain_name, &dxi.d_dci);
 159  152          if (status != NT_STATUS_SUCCESS) {
 160  153                  syslog(LOG_ERR,
 161  154                      "smbd: failed to locate AD server for domain %s (%s)",
 162  155                      info->domain_name, xlate_nt_status(status));
 163  156                  goto out;
 164  157          }
 165  158  
 166  159          /*
 167  160           * Found a DC.  Report what we found along with the return status
 168  161           * so that admin will know which AD server we were talking to.
 169  162           */
 170  163          (void) strlcpy(res->dc_name, dxi.d_dci.dc_name, MAXHOSTNAMELEN);
 171  164          syslog(LOG_INFO, "smbd: found AD server %s", dxi.d_dci.dc_name);
 172  165  
 173  166          /*
 174  167           * Domain discovery needs to authenticate with the AD server.
 175  168           * Disconnect any existing connection with the domain controller
 176  169           * to make sure we won't use any prior authentication context
 177  170           * our redirector might have.
 178  171           */
 179  172          mlsvc_disconnect(dxi.d_dci.dc_name);
 180  173  
 181  174          /*
 182  175           * Get the domain policy info (domain SID etc).
 183  176           * Here too, bypass the smb_ddiscover_service.
 184  177           */
 185  178          status = smb_ddiscover_main(info->domain_name, &dxi);
 186  179          if (status != NT_STATUS_SUCCESS) {
 187  180                  syslog(LOG_ERR,
 188  181                      "smbd: failed getting domain info for %s (%s)",
 189  182                      info->domain_name, xlate_nt_status(status));
 190  183                  goto out;
 191  184          }
 192  185          /*
 193  186           * After a successful smbd_ddiscover_main() call
 194  187           * we should call smb_domain_save() to update the
 195  188           * data shown by smbadm list.  Do that at the end,
 196  189           * only if all goes well with joining the domain.
 197  190           */
 198  191  
 199  192          /*
 200  193           * Create or update our machine account on the DC.
 201  194           * A non-null user means we do "secure join".
 202  195           */
 203  196          if (info->domain_username[0] != '\0') {
 204  197                  /*
 205  198                   * If enabled, try to join using AD Services.
 206  199                   */
 207  200                  status = NT_STATUS_UNSUCCESSFUL;
 208  201                  if (ads_enabled) {
 209  202                          res->join_err = smb_ads_join(di->di_fqname,
 210  203                              info->domain_username, info->domain_passwd,
 211  204                              machine_pw);
 212  205                          if (res->join_err == SMB_ADS_SUCCESS) {
 213  206                                  status = NT_STATUS_SUCCESS;
 214  207                          }
 215  208                  } else {
 216  209                          syslog(LOG_DEBUG, "use_ads=false (do RPC join)");
 217  210  
 218  211                          /*
 219  212                           * If ADS was disabled, join using RPC.
 220  213                           */
 221  214                          status = mlsvc_join_rpc(&dxi,
 222  215                              info->domain_username,
 223  216                              info->domain_passwd,
 224  217                              machine_name, machine_pw);
 225  218                  }
 226  219  
 227  220          } else {
 228  221                  /*
 229  222                   * Doing "Unsecure join" (pre-created account)
 230  223                   */
 231  224                  status = mlsvc_join_noauth(&dxi, machine_name, machine_pw);
 232  225          }
 233  226  
 234  227          if (status != NT_STATUS_SUCCESS)
 235  228                  goto out;
 236  229  
 237  230          /*
 238  231           * Make sure we can authenticate using the
 239  232           * (new, or updated) machine account.
 240  233           */
 241  234          (void) smb_auth_ntlm_hash(machine_pw, passwd_hash);
 242  235          smb_ipc_set(machine_name, passwd_hash);
 243  236          rc = smbrdr_logon(dxi.d_dci.dc_name, di->di_nbname, machine_name);
 244  237          if (rc != 0) {
 245  238                  syslog(LOG_NOTICE, "Authenticate with "
 246  239                      "new/updated machine account: %s",
 247  240                      strerror(rc));
 248  241                  res->join_err = SMB_ADJOIN_ERR_AUTH_NETLOGON;
 249  242                  status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
 250  243                  goto out;
 251  244          }
 252  245  
 253  246          /*
 254  247           * Store the new machine account password, and
 255  248           * SMB_CI_DOMAIN_MEMB etc.
 256  249           */
 257  250          rc = smb_setdomainprops(NULL, dxi.d_dci.dc_name, machine_pw);
 258  251          if (rc != 0) {
 259  252                  syslog(LOG_NOTICE,
 260  253                      "Failed to save machine account password");
 261  254                  res->join_err = SMB_ADJOIN_ERR_STORE_PROPS;
 262  255                  status = NT_STATUS_INTERNAL_DB_ERROR;
 263  256                  goto out;
 264  257          }
 265  258  
 266  259          /*
 267  260           * Update idmap config?
 268  261           * Already set the domain_name above.
 269  262           */
 270  263  
 271  264          /*
 272  265           * Save the SMB server config.  Sets: SMB_CI_DOMAIN_*
 273  266           * Should unify SMB vs idmap configs.
 274  267           */
 275  268          smb_config_setdomaininfo(di->di_nbname, di->di_fqname,
 276  269              di->di_sid,
 277  270              di->di_u.di_dns.ddi_forest,
 278  271              di->di_u.di_dns.ddi_guid);
 279  272          smb_ipc_commit();
 280  273          smb_domain_save();
 281  274  
 282  275          status = 0;
 283  276  
 284  277  out:
 285  278  
 286  279          if (status != 0) {
 287  280                  /*
 288  281                   * Undo the tentative domain settings.
 289  282                   */
 290  283                  (void) smb_config_set_idmap_domain("");
 291  284                  (void) smb_config_refresh_idmap();
 292  285                  smb_ipc_rollback();
 293  286          }
 294  287  
 295  288          /* Avoid leaving cleartext passwords around. */
 296  289          bzero(machine_pw, sizeof (machine_pw));
 297  290          bzero(passwd_hash, sizeof (passwd_hash));
 298  291  
 299  292          res->status = status;
 300  293  }
 301  294  
 302  295  static DWORD
 303  296  mlsvc_join_rpc(smb_domainex_t *dxi,
 304  297          char *admin_user, char *admin_pw,
 305  298          char *machine_name,  char *machine_pw)
 306  299  {
 307  300          mlsvc_handle_t samr_handle;
 308  301          mlsvc_handle_t domain_handle;
 309  302          mlsvc_handle_t user_handle;
 310  303          smb_account_t ainfo;
 311  304          char *server = dxi->d_dci.dc_name;
 312  305          smb_domain_t *di = &dxi->d_primary;
 313  306          DWORD account_flags;
 314  307          DWORD rid;
 315  308          DWORD status;
 316  309          int rc;
 317  310  
 318  311          /* Caller did smb_ipc_set() so we don't need the pw for now. */
 319  312          _NOTE(ARGUNUSED(admin_pw));
 320  313  
 321  314          rc = samr_open(server, di->di_nbname, admin_user,
 322  315              MAXIMUM_ALLOWED, &samr_handle);
 323  316          if (rc != 0) {
 324  317                  syslog(LOG_NOTICE, "sam_connect to server %s failed", server);
 325  318                  return (RPC_NT_SERVER_UNAVAILABLE);
 326  319          }
 327  320          /* have samr_handle */
 328  321  
 329  322          status = samr_open_domain(&samr_handle, MAXIMUM_ALLOWED,
 330  323              (struct samr_sid *)di->di_binsid, &domain_handle);
 331  324          if (status != NT_STATUS_SUCCESS)
 332  325                  goto out_samr_handle;
 333  326          /* have domain_handle */
 334  327  
 335  328          account_flags = SAMR_AF_WORKSTATION_TRUST_ACCOUNT;
 336  329          status = samr_create_user(&domain_handle, machine_name,
 337  330              account_flags, &rid, &user_handle);
 338  331          if (status == NT_STATUS_USER_EXISTS) {
 339  332                  status = samr_lookup_domain_names(&domain_handle,
 340  333                      machine_name, &ainfo);
 341  334                  if (status != NT_STATUS_SUCCESS)
 342  335                          goto out_domain_handle;
 343  336                  status = samr_open_user(&domain_handle, MAXIMUM_ALLOWED,
 344  337                      ainfo.a_rid, &user_handle);
 345  338          }
 346  339          if (status != NT_STATUS_SUCCESS) {
 347  340                  syslog(LOG_NOTICE,
 348  341                      "smbd: failed to open machine account (%s)",
 349  342                      xlate_nt_status(status));
 350  343                  goto out_domain_handle;
 351  344          }
 352  345  
 353  346          /*
 354  347           * The account exists, and we have user_handle open
 355  348           * on that account.  Set the password and flags.
 356  349           */
 357  350  
 358  351          status = netr_set_user_password(&user_handle, machine_pw);
 359  352          if (status != NT_STATUS_SUCCESS) {
 360  353                  syslog(LOG_NOTICE,
 361  354                      "smbd: failed to set machine account password (%s)",
 362  355                      xlate_nt_status(status));
 363  356                  goto out_user_handle;
 364  357          }
 365  358  
 366  359          account_flags |= SAMR_AF_DONT_EXPIRE_PASSWD;
 367  360          status = netr_set_user_control(&user_handle, account_flags);
 368  361          if (status != NT_STATUS_SUCCESS) {
 369  362                  syslog(LOG_NOTICE,
 370  363                      "Set machine account control flags: %s",
 371  364                      xlate_nt_status(status));
 372  365                  goto out_user_handle;
 373  366          }
 374  367  
 375  368  out_user_handle:
 376  369          (void) samr_close_handle(&user_handle);
 377  370  out_domain_handle:
 378  371          (void) samr_close_handle(&domain_handle);
 379  372  out_samr_handle:
 380  373          (void) samr_close_handle(&samr_handle);
 381  374  
 382  375          return (status);
 383  376  }
 384  377  
 385  378  /*
 386  379   * Doing "Unsecure join" (using a pre-created machine account).
 387  380   * All we need to do is change the password from the default
 388  381   * to a random string.
 389  382   *
 390  383   * Note: this is a work in progres.  Nexenta issue 11960
 391  384   * (allow joining an AD domain using a pre-created computer account)
 392  385   * It turns out that to change the machine account password,
 393  386   * we need to use a different RPC call, performed over the
 394  387   * NetLogon secure channel.  (See netr_server_password_set2)
 395  388   */
 396  389  static DWORD
 397  390  mlsvc_join_noauth(smb_domainex_t *dxi,
 398  391          char *machine_name, char *machine_pw)
 399  392  {
 400  393          char old_pw[SMB_SAMACCT_MAXLEN];
 401  394          DWORD status;
 402  395  
 403  396          /*
 404  397           * Compose the current (default) password for the
 405  398           * pre-created machine account, which is just the
 406  399           * account name in lower case, truncated to 14
 407  400           * characters.
 408  401           */
 409  402          if (smb_gethostname(old_pw, sizeof (old_pw), SMB_CASE_LOWER) != 0)
 410  403                  return (NT_STATUS_INTERNAL_ERROR);
 411  404          old_pw[14] = '\0';
 412  405  
 413  406          status = netr_change_password(dxi->d_dci.dc_name, machine_name,
 414  407              old_pw, machine_pw);
 415  408          if (status != NT_STATUS_SUCCESS) {
 416  409                  syslog(LOG_NOTICE,
  
    | 
      ↓ open down ↓ | 
    322 lines elided | 
    
      ↑ open up ↑ | 
  
 417  410                      "Change machine account password: %s",
 418  411                      xlate_nt_status(status));
 419  412          }
 420  413          return (status);
 421  414  }
 422  415  
 423  416  void
 424  417  mlsvc_disconnect(const char *server)
 425  418  {
 426  419          smbrdr_disconnect(server);
      420 +}
      421 +
      422 +/*
      423 + * A few more helper functions for RPC services.
      424 + */
      425 +
      426 +/*
      427 + * Check whether or not the specified user has administrator privileges,
      428 + * i.e. is a member of Domain Admins or Administrators.
      429 + * Returns true if the user is an administrator, otherwise returns false.
      430 + */
      431 +boolean_t
      432 +ndr_is_admin(ndr_xa_t *xa)
      433 +{
      434 +        smb_netuserinfo_t *ctx = xa->pipe->np_user;
      435 +
      436 +        return (ctx->ui_flags & SMB_ATF_ADMIN);
      437 +}
      438 +
      439 +/*
      440 + * Check whether or not the specified user has power-user privileges,
      441 + * i.e. is a member of Domain Admins, Administrators or Power Users.
      442 + * This is typically required for operations such as managing shares.
      443 + * Returns true if the user is a power user, otherwise returns false.
      444 + */
      445 +boolean_t
      446 +ndr_is_poweruser(ndr_xa_t *xa)
      447 +{
      448 +        smb_netuserinfo_t *ctx = xa->pipe->np_user;
      449 +
      450 +        return ((ctx->ui_flags & SMB_ATF_ADMIN) ||
      451 +            (ctx->ui_flags & SMB_ATF_POWERUSER));
      452 +}
      453 +
      454 +int32_t
      455 +ndr_native_os(ndr_xa_t *xa)
      456 +{
      457 +        smb_netuserinfo_t *ctx = xa->pipe->np_user;
      458 +
      459 +        return (ctx->ui_native_os);
 427  460  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX