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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Utility functions to support the RPC interface library. 28 */ 29 30 #include <stdio.h> 31 #include <stdarg.h> 32 #include <strings.h> 33 #include <unistd.h> 34 #include <netdb.h> 35 #include <stdlib.h> 36 37 #include <sys/time.h> 38 #include <sys/systm.h> 39 40 #include <smbsrv/libsmb.h> 41 #include <smbsrv/libsmbrdr.h> 42 #include <smbsrv/libsmbns.h> 43 #include <smbsrv/libmlsvc.h> 44 45 #include <smbsrv/smbinfo.h> 46 #include <smbsrv/lsalib.h> 47 #include <smbsrv/samlib.h> 48 #include <smbsrv/mlsvc_util.h> 49 #include <smbsrv/mlsvc.h> 50 51 /* Domain join support (using MS-RPC) */ 52 static boolean_t mlsvc_ntjoin_support = B_FALSE; 53 54 extern int netr_open(char *, char *, mlsvc_handle_t *); 55 extern int netr_close(mlsvc_handle_t *); 56 extern DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD); 57 extern int mlsvc_user_getauth(char *, char *, smb_auth_info_t *); 58 59 /* 60 * Compare the supplied domain name with the local hostname. 61 * We need to deal with both server names and fully-qualified 62 * domain names. 63 * 64 * Returns: 65 * 0 The specified domain is not the local domain, 66 * 1 The Specified domain is the local domain. 67 * -1 Invalid parameter or unable to get the local 68 * system information. 69 */ 70 int 71 mlsvc_is_local_domain(const char *domain) 72 { 73 char hostname[MAXHOSTNAMELEN]; 74 int rc; 75 76 if (smb_config_get_secmode() == SMB_SECMODE_WORKGRP) 77 return (1); 78 79 if (strchr(domain, '.') != NULL) 80 rc = smb_getfqhostname(hostname, MAXHOSTNAMELEN); 81 else { 82 if (strlen(domain) < NETBIOS_NAME_SZ) 83 rc = smb_getnetbiosname(hostname, MAXHOSTNAMELEN); 84 else 85 rc = smb_gethostname(hostname, MAXHOSTNAMELEN, 1); 86 } 87 88 if (rc != 0) 89 return (-1); 90 91 if (strcasecmp(domain, hostname) == 0) 92 return (1); 93 94 return (0); 95 } 96 97 /* 98 * mlsvc_lookup_name 99 * 100 * This is just a wrapper for lsa_lookup_name. 101 * 102 * The memory for the sid is allocated using malloc so the caller should 103 * call free when it is no longer required. 104 */ 105 uint32_t 106 mlsvc_lookup_name(char *account, smb_sid_t **sid, uint16_t *sid_type) 107 { 108 smb_userinfo_t *ainfo; 109 uint32_t status; 110 111 if ((ainfo = mlsvc_alloc_user_info()) == NULL) 112 return (NT_STATUS_NO_MEMORY); 113 114 status = lsa_lookup_name(NULL, account, *sid_type, ainfo); 115 if (status == NT_STATUS_SUCCESS) { 116 *sid = ainfo->user_sid; 117 ainfo->user_sid = NULL; 118 *sid_type = ainfo->sid_name_use; 119 } 120 121 mlsvc_free_user_info(ainfo); 122 return (status); 123 } 124 125 /* 126 * mlsvc_lookup_sid 127 * 128 * This is just a wrapper for lsa_lookup_sid. 129 * 130 * The allocated memory for the returned name must be freed by caller upon 131 * successful return. 132 */ 133 uint32_t 134 mlsvc_lookup_sid(smb_sid_t *sid, char **name) 135 { 136 smb_userinfo_t *ainfo; 137 uint32_t status; 138 int namelen; 139 140 if ((ainfo = mlsvc_alloc_user_info()) == NULL) 141 return (NT_STATUS_NO_MEMORY); 142 143 status = lsa_lookup_sid(sid, ainfo); 144 if (status == NT_STATUS_SUCCESS) { 145 namelen = strlen(ainfo->domain_name) + strlen(ainfo->name) + 2; 146 if ((*name = malloc(namelen)) == NULL) { 147 mlsvc_free_user_info(ainfo); 148 return (NT_STATUS_NO_MEMORY); 149 } 150 (void) snprintf(*name, namelen, "%s\\%s", 151 ainfo->domain_name, ainfo->name); 152 } 153 154 mlsvc_free_user_info(ainfo); 155 return (status); 156 } 157 158 /* 159 * mlsvc_alloc_user_info 160 * 161 * Allocate a user_info structure and set the contents to zero. A 162 * pointer to the user_info structure is returned. 163 */ 164 smb_userinfo_t * 165 mlsvc_alloc_user_info(void) 166 { 167 smb_userinfo_t *user_info; 168 169 user_info = (smb_userinfo_t *)malloc(sizeof (smb_userinfo_t)); 170 if (user_info == NULL) 171 return (NULL); 172 173 bzero(user_info, sizeof (smb_userinfo_t)); 174 return (user_info); 175 } 176 177 /* 178 * mlsvc_free_user_info 179 * 180 * Free a user_info structure. This function ensures that the contents 181 * of the user_info are freed as well as the user_info itself. 182 */ 183 void 184 mlsvc_free_user_info(smb_userinfo_t *user_info) 185 { 186 if (user_info) { 187 mlsvc_release_user_info(user_info); 188 free(user_info); 189 } 190 } 191 192 /* 193 * mlsvc_release_user_info 194 * 195 * Release the contents of a user_info structure and zero out the 196 * elements but do not free the user_info structure itself. This 197 * function cleans out the structure so that it can be reused without 198 * worrying about stale contents. 199 */ 200 void 201 mlsvc_release_user_info(smb_userinfo_t *user_info) 202 { 203 int i; 204 205 if (user_info == NULL) 206 return; 207 208 free(user_info->name); 209 free(user_info->domain_sid); 210 free(user_info->domain_name); 211 free(user_info->groups); 212 213 if (user_info->n_other_grps) { 214 for (i = 0; i < user_info->n_other_grps; i++) 215 free(user_info->other_grps[i].sid); 216 217 free(user_info->other_grps); 218 } 219 220 free(user_info->session_key); 221 free(user_info->user_sid); 222 free(user_info->pgrp_sid); 223 bzero(user_info, sizeof (smb_userinfo_t)); 224 } 225 226 /* 227 * mlsvc_setadmin_user_info 228 * 229 * Determines if the given user is the domain Administrator or a 230 * member of Domain Admins or Administrators group and set the 231 * user_info->flags accordingly. 232 */ 233 void 234 mlsvc_setadmin_user_info(smb_userinfo_t *user_info) 235 { 236 nt_domain_t *domain; 237 smb_group_t grp; 238 int rc, i; 239 240 if ((domain = nt_domain_lookupbytype(NT_DOMAIN_PRIMARY)) == NULL) 241 return; 242 243 if (!smb_sid_cmp((smb_sid_t *)user_info->domain_sid, domain->sid)) 244 return; 245 246 if (user_info->rid == DOMAIN_USER_RID_ADMIN) 247 user_info->flags |= SMB_UINFO_FLAG_DADMIN; 248 else if (user_info->primary_group_rid == DOMAIN_GROUP_RID_ADMINS) 249 user_info->flags |= SMB_UINFO_FLAG_DADMIN; 250 else { 251 for (i = 0; i < user_info->n_groups; i++) 252 if (user_info->groups[i].rid == DOMAIN_GROUP_RID_ADMINS) 253 user_info->flags |= SMB_UINFO_FLAG_DADMIN; 254 } 255 256 rc = smb_lgrp_getbyname("Administrators", &grp); 257 if (rc == SMB_LGRP_SUCCESS) { 258 if (smb_lgrp_is_member(&grp, user_info->user_sid)) 259 user_info->flags |= SMB_UINFO_FLAG_LADMIN; 260 smb_lgrp_free(&grp); 261 } 262 } 263 264 /* 265 * mlsvc_string_save 266 * 267 * This is a convenience function to prepare strings for an RPC call. 268 * An ms_string_t is set up with the appropriate lengths and str is 269 * set up to point to a copy of the original string on the heap. The 270 * macro MLRPC_HEAP_STRSAVE is an alias for mlrpc_heap_strsave, which 271 * extends the heap and copies the string into the new area. 272 */ 273 int 274 mlsvc_string_save(ms_string_t *ms, char *str, struct mlrpc_xaction *mxa) 275 { 276 if (str == NULL) 277 return (0); 278 279 ms->length = mts_wcequiv_strlen(str); 280 ms->allosize = ms->length + sizeof (mts_wchar_t); 281 282 if ((ms->str = MLRPC_HEAP_STRSAVE(mxa, str)) == NULL) 283 return (0); 284 285 return (1); 286 } 287 288 /* 289 * mlsvc_sid_save 290 * 291 * Expand the heap and copy the sid into the new area. 292 * Returns a pointer to the copy of the sid on the heap. 293 */ 294 smb_sid_t * 295 mlsvc_sid_save(smb_sid_t *sid, struct mlrpc_xaction *mxa) 296 { 297 smb_sid_t *heap_sid; 298 unsigned size; 299 300 if (sid == NULL) 301 return (NULL); 302 303 size = smb_sid_len(sid); 304 305 if ((heap_sid = (smb_sid_t *)MLRPC_HEAP_MALLOC(mxa, size)) == NULL) 306 return (0); 307 308 bcopy(sid, heap_sid, size); 309 return (heap_sid); 310 } 311 312 /* 313 * mlsvc_is_null_handle 314 * 315 * Check a handle against a null handle. Returns 1 if the handle is 316 * null. Otherwise returns 0. 317 */ 318 int 319 mlsvc_is_null_handle(mlsvc_handle_t *handle) 320 { 321 static ms_handle_t zero_handle; 322 323 if (handle == NULL || handle->context == NULL) 324 return (1); 325 326 if (!memcmp(&handle->handle, &zero_handle, sizeof (ms_handle_t))) 327 return (1); 328 329 return (0); 330 } 331 332 DWORD 333 mlsvc_netlogon(char *server, char *domain) 334 { 335 mlsvc_handle_t netr_handle; 336 DWORD status; 337 338 if (netr_open(server, domain, &netr_handle) == 0) { 339 status = netlogon_auth(server, &netr_handle, 340 NETR_FLG_INIT); 341 (void) netr_close(&netr_handle); 342 } else { 343 status = NT_STATUS_OPEN_FAILED; 344 } 345 346 return (status); 347 } 348 349 /* 350 * mlsvc_join 351 * 352 * Returns NT status codes. 353 */ 354 DWORD 355 mlsvc_join(char *server, char *domain, char *plain_user, char *plain_text) 356 { 357 smb_auth_info_t auth; 358 smb_ntdomain_t *di; 359 int erc; 360 DWORD status; 361 char machine_passwd[MLSVC_MACHINE_ACCT_PASSWD_MAX]; 362 char fqdn[MAXHOSTNAMELEN]; 363 364 machine_passwd[0] = '\0'; 365 366 /* 367 * Ensure that the domain name is uppercase. 368 */ 369 (void) utf8_strupr(domain); 370 371 /* 372 * There is no point continuing if the domain information is 373 * not available. Wait for up to 10 seconds and then give up. 374 */ 375 if ((di = smb_getdomaininfo(10)) == 0) { 376 status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 377 return (status); 378 } 379 380 if (strcasecmp(domain, di->domain) != 0) { 381 status = NT_STATUS_INVALID_PARAMETER; 382 return (status); 383 } 384 385 erc = mlsvc_logon(server, domain, plain_user); 386 387 if (erc == AUTH_USER_GRANT) { 388 if (mlsvc_ntjoin_support == B_FALSE) { 389 if (smb_resolve_fqdn(domain, fqdn, MAXHOSTNAMELEN) != 1) 390 return (NT_STATUS_INVALID_PARAMETER); 391 392 if (smb_ads_join(fqdn, plain_user, plain_text, 393 machine_passwd, sizeof (machine_passwd)) 394 == SMB_ADJOIN_SUCCESS) 395 status = NT_STATUS_SUCCESS; 396 else 397 status = NT_STATUS_UNSUCCESSFUL; 398 } else { 399 if (mlsvc_user_getauth(server, plain_user, &auth) 400 != 0) { 401 status = NT_STATUS_INVALID_PARAMETER; 402 return (status); 403 } 404 405 status = sam_create_trust_account(server, domain, 406 &auth); 407 if (status == NT_STATUS_SUCCESS) { 408 (void) smb_getnetbiosname(machine_passwd, 409 sizeof (machine_passwd)); 410 (void) utf8_strlwr(machine_passwd); 411 } 412 } 413 414 if (status == NT_STATUS_SUCCESS) { 415 erc = smb_setdomainprops(NULL, server, 416 machine_passwd); 417 if (erc != 0) 418 return (NT_STATUS_UNSUCCESSFUL); 419 420 status = mlsvc_netlogon(server, domain); 421 } 422 } else { 423 status = NT_STATUS_LOGON_FAILURE; 424 } 425 426 return (status); 427 } 428