1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  25  */
  26 
  27 /*
  28  * This module provides the high level interface to the SAM RPC
  29  * functions.
  30  */
  31 
  32 #include <sys/types.h>
  33 #include <sys/isa_defs.h>
  34 #include <sys/byteorder.h>
  35 
  36 #include <alloca.h>
  37 
  38 #include <smbsrv/libsmb.h>
  39 #include <smbsrv/libmlsvc.h>
  40 #include <smb/ntaccess.h>
  41 #include <lsalib.h>
  42 #include <samlib.h>
  43 
  44 #ifdef _LITTLE_ENDIAN
  45 /* little-endian values on little-endian */
  46 #define htolel(x)       ((uint32_t)(x))
  47 #define letohl(x)       ((uint32_t)(x))
  48 #else   /* (BYTE_ORDER == LITTLE_ENDIAN) */
  49 /* little-endian values on big-endian (swap) */
  50 #define letohl(x)       BSWAP_32(x)
  51 #define htolel(x)       BSWAP_32(x)
  52 #endif  /* (BYTE_ORDER == LITTLE_ENDIAN) */
  53 
  54 /*
  55  * Valid values for the OEM OWF password encryption.
  56  */
  57 #define SAM_KEYLEN              16
  58 
  59 static void samr_fill_userpw(struct samr_user_password *, const char *);
  60 static void samr_make_encrypted_password(
  61         struct samr_encr_passwd *epw,
  62         char *new_pw_clear,
  63         uint8_t *crypt_key);
  64 
  65 
  66 /*
  67  * Todo: Implement "unjoin" domain, which would use the
  68  * sam_remove_trust_account code below.
  69  */
  70 
  71 /*
  72  * sam_remove_trust_account
  73  *
  74  * Attempt to remove the workstation trust account for this system.
  75  * Administrator access is required to perform this operation.
  76  *
  77  * Returns NT status codes.
  78  */
  79 DWORD
  80 sam_remove_trust_account(char *server, char *domain)
  81 {
  82         char account_name[SMB_SAMACCT_MAXLEN];
  83 
  84         if (smb_getsamaccount(account_name, SMB_SAMACCT_MAXLEN) != 0)
  85                 return (NT_STATUS_INTERNAL_ERROR);
  86 
  87         return (sam_delete_account(server, domain, account_name));
  88 }
  89 
  90 
  91 /*
  92  * sam_delete_account
  93  *
  94  * Attempt to remove an account from the SAM database on the specified
  95  * server.
  96  *
  97  * Returns NT status codes.
  98  */
  99 DWORD
 100 sam_delete_account(char *server, char *domain_name, char *account_name)
 101 {
 102         mlsvc_handle_t samr_handle;
 103         mlsvc_handle_t domain_handle;
 104         mlsvc_handle_t user_handle;
 105         smb_account_t ainfo;
 106         smb_sid_t *sid;
 107         DWORD access_mask;
 108         DWORD status;
 109         int rc;
 110         char user[SMB_USERNAME_MAXLEN];
 111 
 112         smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
 113 
 114         rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
 115             &samr_handle);
 116         if (rc != 0)
 117                 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
 118 
 119         sid = samr_lookup_domain(&samr_handle, domain_name);
 120         if (sid == NULL) {
 121                 status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
 122                 goto out_samr_hdl;
 123         }
 124 
 125         status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
 126             (struct samr_sid *)sid, &domain_handle);
 127         if (status != NT_STATUS_SUCCESS)
 128                 goto out_sid_ptr;
 129 
 130         status = samr_lookup_domain_names(&domain_handle, account_name, &ainfo);
 131         if (status != NT_STATUS_SUCCESS)
 132                 goto out_dom_hdl;
 133 
 134         access_mask = STANDARD_RIGHTS_EXECUTE | DELETE;
 135         status = samr_open_user(&domain_handle, access_mask,
 136             ainfo.a_rid, &user_handle);
 137         if (status != NT_STATUS_SUCCESS)
 138                 goto out_dom_hdl;
 139 
 140         status = samr_delete_user(&user_handle);
 141 
 142         (void) samr_close_handle(&user_handle);
 143 out_dom_hdl:
 144         (void) samr_close_handle(&domain_handle);
 145 out_sid_ptr:
 146         free(sid);
 147 out_samr_hdl:
 148         (void) samr_close_handle(&samr_handle);
 149 
 150         return (status);
 151 }
 152 
 153 
 154 /*
 155  * sam_lookup_name
 156  *
 157  * Lookup an account name in the SAM database on the specified domain
 158  * controller. Provides the account RID on success.
 159  *
 160  * Returns NT status codes.
 161  */
 162 DWORD
 163 sam_lookup_name(char *server, char *domain_name, char *account_name,
 164     DWORD *rid_ret)
 165 {
 166         mlsvc_handle_t samr_handle;
 167         mlsvc_handle_t domain_handle;
 168         smb_account_t ainfo;
 169         struct samr_sid *domain_sid;
 170         int rc;
 171         DWORD status;
 172         char user[SMB_USERNAME_MAXLEN];
 173 
 174         smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
 175 
 176         *rid_ret = 0;
 177 
 178         rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
 179             &samr_handle);
 180 
 181         if (rc != 0)
 182                 return (NT_STATUS_OPEN_FAILED);
 183 
 184         domain_sid = (struct samr_sid *)samr_lookup_domain(&samr_handle,
 185             domain_name);
 186         if (domain_sid == NULL) {
 187                 (void) samr_close_handle(&samr_handle);
 188                 return (NT_STATUS_NO_SUCH_DOMAIN);
 189         }
 190 
 191         status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
 192             domain_sid, &domain_handle);
 193         if (status == NT_STATUS_SUCCESS) {
 194                 status = samr_lookup_domain_names(&domain_handle,
 195                     account_name, &ainfo);
 196                 if (status == NT_STATUS_SUCCESS)
 197                         *rid_ret = ainfo.a_rid;
 198 
 199                 (void) samr_close_handle(&domain_handle);
 200         }
 201 
 202         (void) samr_close_handle(&samr_handle);
 203         return (status);
 204 }
 205 
 206 /*
 207  * sam_get_local_domains
 208  *
 209  * Query a remote server to get the list of local domains that it
 210  * supports.
 211  *
 212  * Returns NT status codes.
 213  */
 214 DWORD
 215 sam_get_local_domains(char *server, char *domain_name)
 216 {
 217         mlsvc_handle_t samr_handle;
 218         DWORD status;
 219         int rc;
 220         char user[SMB_USERNAME_MAXLEN];
 221 
 222         smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
 223 
 224         rc = samr_open(server, domain_name, user, SAM_ENUM_LOCAL_DOMAIN,
 225             &samr_handle);
 226         if (rc != 0)
 227                 return (NT_STATUS_OPEN_FAILED);
 228 
 229         status = samr_enum_local_domains(&samr_handle);
 230         (void) samr_close_handle(&samr_handle);
 231         return (status);
 232 }
 233 
 234 /*
 235  * Set the account control flags on some account for which we
 236  * have already opened a SAM handle with appropriate rights,
 237  * passed in here as sam_handle, along with the new flags.
 238  */
 239 DWORD
 240 netr_set_user_control(
 241         mlsvc_handle_t *user_handle,
 242         DWORD UserAccountControl)
 243 {
 244         struct samr_SetUserInfo16 info;
 245 
 246         info.UserAccountControl = UserAccountControl;
 247         return (samr_set_user_info(user_handle, 16, &info));
 248 }
 249 
 250 /*
 251  * Set the password on some account, for which we have already
 252  * opened a SAM handle with appropriate rights, passed in here
 253  * as sam_handle, along with the new password as cleartext.
 254  *
 255  * This builds a struct SAMPR_USER_INTERNAL5_INFORMATION [MS-SAMR]
 256  * containing the new password, encrypted with our session key.
 257  */
 258 DWORD
 259 netr_set_user_password(
 260         mlsvc_handle_t *user_handle,
 261         char *new_pw_clear)
 262 {
 263         unsigned char ssn_key[SMBAUTH_HASH_SZ];
 264         struct samr_SetUserInfo24 info;
 265 
 266         if (ndr_rpc_get_ssnkey(user_handle, ssn_key, SMBAUTH_HASH_SZ))
 267                 return (NT_STATUS_INTERNAL_ERROR);
 268 
 269         (void) memset(&info, 0, sizeof (info));
 270         samr_make_encrypted_password(&info.encr_pw, new_pw_clear, ssn_key);
 271 
 272         /* Rather not leave the session key around. */
 273         (void) memset(ssn_key, 0, sizeof (ssn_key));
 274 
 275         return (samr_set_user_info(user_handle, 24, &info));
 276 }
 277 
 278 /*
 279  * Change a password like NetUserChangePassword(),
 280  * but where we already know which AD server to use,
 281  * so we don't request the domain name or search for
 282  * an AD server for that domain here.
 283  */
 284 DWORD
 285 netr_change_password(
 286         char *server,
 287         char *account,
 288         char *old_pw_clear,
 289         char *new_pw_clear)
 290 {
 291         struct samr_encr_passwd epw;
 292         struct samr_encr_hash old_hash;
 293         uint8_t old_nt_hash[SAMR_PWHASH_LEN];
 294         uint8_t new_nt_hash[SAMR_PWHASH_LEN];
 295         mlsvc_handle_t handle;
 296         DWORD status;
 297 
 298         /*
 299          * Create an RPC handle to this server, bound to SAMR.
 300          */
 301         status = ndr_rpc_bind(&handle, server, "", "", "SAMR");
 302         if (status != NT_STATUS_SUCCESS)
 303                 return (status);
 304 
 305         /*
 306          * Encrypt the new p/w (plus random filler) with the
 307          * old password, and send the old p/w encrypted with
 308          * the new p/w hash to prove we know the old p/w.
 309          * Details:  [MS-SAMR 3.1.5.10.3]
 310          */
 311         (void) smb_auth_ntlm_hash(old_pw_clear, old_nt_hash);
 312         (void) smb_auth_ntlm_hash(new_pw_clear, new_nt_hash);
 313         samr_make_encrypted_password(&epw, new_pw_clear, old_nt_hash);
 314 
 315         (void) smb_auth_DES(old_hash.data, SAMR_PWHASH_LEN,
 316             new_nt_hash, 14, /* key */
 317             old_nt_hash, SAMR_PWHASH_LEN);
 318 
 319         /*
 320          * Finally, ready to try the OtW call.
 321          */
 322         status = samr_change_password(
 323             &handle, server, account,
 324             &epw, &old_hash);
 325 
 326         /* Avoid leaving cleartext (or equivalent) around. */
 327         (void) memset(old_nt_hash, 0, sizeof (old_nt_hash));
 328         (void) memset(new_nt_hash, 0, sizeof (new_nt_hash));
 329 
 330         ndr_rpc_unbind(&handle);
 331         return (status);
 332 }
 333 
 334 /*
 335  * Build an encrypted password, as used by samr_set_user_info
 336  * and samr_change_password.  Note: This builds the unencrypted
 337  * form in one union arm, and encrypts it in the other union arm.
 338  */
 339 void
 340 samr_make_encrypted_password(
 341         struct samr_encr_passwd *epw,
 342         char *new_pw_clear,
 343         uint8_t *crypt_key)
 344 {
 345         union {
 346                 struct samr_user_password u;
 347                 struct samr_encr_passwd e;
 348         } pwu;
 349 
 350         samr_fill_userpw(&pwu.u, new_pw_clear);
 351 
 352         (void) smb_auth_RC4(pwu.e.data, sizeof (pwu.e.data),
 353             crypt_key, SAMR_PWHASH_LEN,
 354             pwu.e.data, sizeof (pwu.e.data));
 355 
 356         (void) memcpy(epw->data, pwu.e.data, sizeof (pwu.e.data));
 357         (void) memset(pwu.e.data, 0, sizeof (pwu.e.data));
 358 }
 359 
 360 /*
 361  * This fills in a samr_user_password (a.k.a. SAMPR_USER_PASSWORD
 362  * in the MS Net API) which has the new password "right justified"
 363  * in the buffer, and any space on the left filled with random junk
 364  * to improve the quality of the encryption that is subsequently
 365  * applied to this buffer before it goes over the wire.
 366  */
 367 static void
 368 samr_fill_userpw(struct samr_user_password *upw, const char *new_pw)
 369 {
 370         smb_wchar_t *pbuf;
 371         uint32_t pwlen_bytes;
 372         size_t pwlen_wchars;
 373 
 374         /*
 375          * First fill the whole buffer with the random junk.
 376          * (Slightly less random when debugging:)
 377          */
 378 #ifdef DEBUG
 379         (void) memset(upw->Buffer, '*', sizeof (upw->Buffer));
 380 #else
 381         randomize((char *)upw->Buffer, sizeof (upw->Buffer));
 382 #endif
 383 
 384         /*
 385          * Now overwrite the last pwlen characters of
 386          * that buffer with the password, and set the
 387          * length field so the receiving end knows where
 388          * the junk ends and the real password starts.
 389          */
 390         pwlen_wchars = smb_wcequiv_strlen(new_pw) / 2;
 391         if (pwlen_wchars > SAMR_USER_PWLEN)
 392                 pwlen_wchars = SAMR_USER_PWLEN;
 393         pwlen_bytes = pwlen_wchars * 2;
 394 
 395         pbuf = &upw->Buffer[SAMR_USER_PWLEN - pwlen_wchars];
 396         (void) smb_mbstowcs(pbuf, new_pw, pwlen_wchars);
 397 
 398         /* Yes, this is in Bytes, not wchars. */
 399         upw->Length = htolel(pwlen_bytes);
 400 }