Mercurial > illumos > illumos-gate
view usr/src/uts/common/fs/smbsrv/smb_util.c @ 7961:4b5e3051f38b
6751647 TRANS2_FIND_NEXT continuation by filename restarts search at beginning of directory
6753904 SVCCTL server side service
6741484 Local users cannot connect to CIFS shares from MacOS 10.5
6746898 win98 can not overwrite *.files
6753310 Incorrect handling of SmbNegotiate request when invalid dialects are negotiated.
6751123 Unable to join domain, core dump generated with IPMP setting
6722437 SMB_TRANS2_FIND returns wrong status code when stream file is passed
6716578 can not delete file in extended attribute name space in cifs client when cifs server is solaris
PSARC 2008/584 Correction in nbmand behavior
6734067 Long delay when viewing MS Word Read-only file properties with nbmand enabled.
PSARC/2007/281 NFS share properties for Montana compatibility
6475452 Need Solaris support for Montana approve file functionality in NFS
6582170 Host-based access control (approve file)
6749075 Unable to join domain if user password exceeds 20 characters
6612716 Join domain fails if hostname is > 15 chars
6753251 server signing: wrong signature is generated for the NetShareEnum reply
6757521 SMB daemon leaks memory after displaying GSS status
6760315 Local user cannot connnect to CIFS shares if CIFS server's hostname is not specified
6757333 Share publisher thread runs into infinite loop of displaying GSS major/minor status
6757132 smbd crashes at smb_idmap_batch_getmappings
6760876 security descriptor decoding function has a glitch
6761491 Cannot open or delete a named stream on a directory file.
6741449 Cleanup list in smbns_ads module
6593958 Users with restore privilege can take ownership of files
author | natalie li - Sun Microsystems - Irvine United States <Natalie.Li@Sun.COM> |
---|---|
date | Tue, 28 Oct 2008 03:34:04 -0700 |
parents | 73b61202d5d6 |
children | 5f1c6a3b0fad |
line wrap: on
line source
/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include <sys/tzfile.h> #include <sys/atomic.h> #include <sys/kidmap.h> #include <sys/time.h> #include <smbsrv/smb_incl.h> #include <smbsrv/smb_fsops.h> #include <smbsrv/string.h> #include <smbsrv/mbuf.h> #include <smbsrv/smbinfo.h> #include <smbsrv/smb_xdr.h> #include <smbsrv/smb_vops.h> #include <smbsrv/smb_idmap.h> #include <sys/sid.h> #include <sys/priv_names.h> #ifdef DEBUG uint_t smb_tsd_key; #endif static boolean_t smb_thread_continue_timedwait_locked(smb_thread_t *thread, int ticks); time_t tzh_leapcnt = 0; struct tm *smb_gmtime_r(time_t *clock, struct tm *result); time_t smb_timegm(struct tm *tm); struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; }; static int days_in_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int smb_ascii_or_unicode_strlen(struct smb_request *sr, char *str) { if (sr->smb_flg2 & SMB_FLAGS2_UNICODE) return (mts_wcequiv_strlen(str)); return (strlen(str)); } int smb_ascii_or_unicode_strlen_null(struct smb_request *sr, char *str) { if (sr->smb_flg2 & SMB_FLAGS2_UNICODE) return (mts_wcequiv_strlen(str) + 2); return (strlen(str) + 1); } int smb_ascii_or_unicode_null_len(struct smb_request *sr) { if (sr->smb_flg2 & SMB_FLAGS2_UNICODE) return (2); return (1); } int smb_component_match( struct smb_request *sr, ino64_t fileid, struct smb_odir *od, smb_odir_context_t *pc) { boolean_t ignore_case = SMB_TREE_IS_CASEINSENSITIVE(sr); return (smb_match_name(fileid, pc->dc_name, pc->dc_shortname, pc->dc_name83, od->d_pattern, ignore_case)); } int smb_convert_unicode_wildcards(char *path) { int wildcards = 0; char *ptr = path; char nch; /* * Special case "<" for "dir *." */ if (strcmp(path, "<") == 0) { return (1); } while (*ptr) { nch = *(ptr + 1); switch (*ptr) { case '*' : /* Count non-unicode wildcards while we're at it */ case '?' : wildcards++; break; case '<' : if (nch == '.') { *(ptr++) = '*'; wildcards++; } break; case '>' : *ptr = '?'; wildcards++; break; case '\"' : *ptr = '.'; break; } ptr++; } /* NT DOS wildcards... */ if (strcmp(path, "????????.???") == 0) { (void) strcpy(path, "*"); } else if (strncmp(path, "????????.", 9) == 0) { *path = '*'; (void) strcpy(path+1, path+8); } return (wildcards); } /* * smb_is_dot_or_dotdot * * Use when checking for the "." and ".." entries in a directory. * Returns B_TRUE if the name is "." or "..". Otherwise returns B_FALSE. */ boolean_t smb_is_dot_or_dotdot(const char *name) { if (*name != '.') return (B_FALSE); if ((name[1] == 0) || (name[1] == '.' && name[2] == 0)) return (B_TRUE); return (B_FALSE); } /* * smb_sattr_check * * Check file attributes against a search attribute (sattr) mask. * * Normal files, which includes READONLY and ARCHIVE, always pass * this check. If the DIRECTORY, HIDDEN or SYSTEM special attributes * are set then they must appear in the search mask. The special * attributes are inclusive, i.e. all special attributes that appear * in sattr must also appear in the file attributes for the check to * pass. * * The following examples show how this works: * * fileA: READONLY * fileB: 0 (no attributes = normal file) * fileC: READONLY, ARCHIVE * fileD: HIDDEN * fileE: READONLY, HIDDEN, SYSTEM * dirA: DIRECTORY * * search attribute: 0 * Returns: fileA, fileB and fileC. * search attribute: HIDDEN * Returns: fileA, fileB, fileC and fileD. * search attribute: SYSTEM * Returns: fileA, fileB and fileC. * search attribute: DIRECTORY * Returns: fileA, fileB, fileC and dirA. * search attribute: HIDDEN and SYSTEM * Returns: fileA, fileB, fileC, fileD and fileE. * * Returns true if the file and sattr match; otherwise, returns false. */ boolean_t smb_sattr_check(smb_attr_t *ap, char *name, unsigned short sattr) { if (name) { if (smb_is_dot_or_dotdot(name) && !(sattr & FILE_ATTRIBUTE_HIDDEN)) return (B_FALSE); } if ((ap->sa_vattr.va_type == VDIR) && !(sattr & FILE_ATTRIBUTE_DIRECTORY)) return (B_FALSE); if ((ap->sa_dosattr & FILE_ATTRIBUTE_HIDDEN) && !(sattr & FILE_ATTRIBUTE_HIDDEN)) return (B_FALSE); if ((ap->sa_dosattr & FILE_ATTRIBUTE_SYSTEM) && !(sattr & FILE_ATTRIBUTE_SYSTEM)) return (B_FALSE); return (B_TRUE); } /* * smb_stream_parse_name * * calling function is responsible for passing valid buffers with * adequate sizes. * * path is a NULL terminated string which could be a * stream path. If it's a stream path it could be * in one of the following formats: * . path:stream * . path:stream:$DATA * unnamed stream is part of the path and there is * exactly one ':' in between the unamed and name * streams * * u_stream_name will contain the unamed stream portion upon * successful return. * this is the portion between last '\' and * the first ':' * * stream_name will contain the named stream portion upon * successful return. * this is the portion between the first ':' and the * end of the 'name' string. * * '::' - is a non-stream and is commonly used by Windows to designate * the unamed stream in the form "::$DATA" * * on return the named stream always has a ":$DATA" appended if there * isn't one already * * Return Codes: * * 0 - given path doesn't contain any streams * 1 - given path had a stream */ int smb_stream_parse_name(char *path, char *u_stream_name, char *stream_name) { char *colonp; char *slashp; if (path == 0) return (0); /* * if there is no colon in the path or it's the last char * then it's not a stream name */ colonp = strchr(path, ':'); if ((colonp == 0) || (*(colonp+1) == 0)) return (0); /* "::" always means the unamed stream */ if (strstr(path, "::")) return (0); if (stream_name) { /* * stream name is the portion between ':' and the * end of 'path' string (including the starting ':') */ (void) strcpy(stream_name, colonp); if (strstr(stream_name, ":$DATA") == 0) (void) strcat(stream_name, ":$DATA"); } if (u_stream_name) { /* * uname stream is the portion between last '\' * and the ':' */ slashp = strrchr(path, '\\'); slashp = (slashp == 0) ? path : slashp + 1; /*LINTED E_PTRDIFF_OVERFLOW*/ (void) strlcpy(u_stream_name, slashp, colonp - slashp + 1); } return (1); } int microtime(timestruc_t *tvp) { tvp->tv_sec = gethrestime_sec(); tvp->tv_nsec = 0; return (0); } int32_t clock_get_milli_uptime() { return (TICK_TO_MSEC(lbolt)); } int /*ARGSUSED*/ smb_noop(void *p, size_t size, int foo) { return (0); } /* * smb_idpool_increment * * This function increments the ID pool by doubling the current size. This * function assumes the caller entered the mutex of the pool. */ static int smb_idpool_increment( smb_idpool_t *pool) { uint8_t *new_pool; uint32_t new_size; ASSERT(pool->id_magic == SMB_IDPOOL_MAGIC); new_size = pool->id_size * 2; if (new_size <= SMB_IDPOOL_MAX_SIZE) { new_pool = kmem_alloc(new_size / 8, KM_NOSLEEP); if (new_pool) { bzero(new_pool, new_size / 8); bcopy(pool->id_pool, new_pool, pool->id_size / 8); kmem_free(pool->id_pool, pool->id_size / 8); pool->id_pool = new_pool; pool->id_free_counter += new_size - pool->id_size; pool->id_max_free_counter += new_size - pool->id_size; pool->id_size = new_size; pool->id_idx_msk = (new_size / 8) - 1; if (new_size >= SMB_IDPOOL_MAX_SIZE) { /* id -1 made unavailable */ pool->id_pool[pool->id_idx_msk] = 0x80; pool->id_free_counter--; pool->id_max_free_counter--; } return (0); } } return (-1); } /* * smb_idpool_constructor * * This function initializes the pool structure provided. */ int smb_idpool_constructor( smb_idpool_t *pool) { ASSERT(pool->id_magic != SMB_IDPOOL_MAGIC); pool->id_size = SMB_IDPOOL_MIN_SIZE; pool->id_idx_msk = (SMB_IDPOOL_MIN_SIZE / 8) - 1; pool->id_free_counter = SMB_IDPOOL_MIN_SIZE - 1; pool->id_max_free_counter = SMB_IDPOOL_MIN_SIZE - 1; pool->id_bit = 0x02; pool->id_bit_idx = 1; pool->id_idx = 0; pool->id_pool = (uint8_t *)kmem_alloc((SMB_IDPOOL_MIN_SIZE / 8), KM_SLEEP); bzero(pool->id_pool, (SMB_IDPOOL_MIN_SIZE / 8)); /* -1 id made unavailable */ pool->id_pool[0] = 0x01; /* id 0 made unavailable */ mutex_init(&pool->id_mutex, NULL, MUTEX_DEFAULT, NULL); pool->id_magic = SMB_IDPOOL_MAGIC; return (0); } /* * smb_idpool_destructor * * This function tears down and frees the resources associated with the * pool provided. */ void smb_idpool_destructor( smb_idpool_t *pool) { ASSERT(pool->id_magic == SMB_IDPOOL_MAGIC); ASSERT(pool->id_free_counter == pool->id_max_free_counter); pool->id_magic = (uint32_t)~SMB_IDPOOL_MAGIC; mutex_destroy(&pool->id_mutex); kmem_free(pool->id_pool, (size_t)(pool->id_size / 8)); } /* * smb_idpool_alloc * * This function allocates an ID from the pool provided. */ int smb_idpool_alloc( smb_idpool_t *pool, uint16_t *id) { uint32_t i; uint8_t bit; uint8_t bit_idx; uint8_t byte; ASSERT(pool->id_magic == SMB_IDPOOL_MAGIC); mutex_enter(&pool->id_mutex); if ((pool->id_free_counter == 0) && smb_idpool_increment(pool)) { mutex_exit(&pool->id_mutex); return (-1); } i = pool->id_size; while (i) { bit = pool->id_bit; bit_idx = pool->id_bit_idx; byte = pool->id_pool[pool->id_idx]; while (bit) { if (byte & bit) { bit = bit << 1; bit_idx++; continue; } pool->id_pool[pool->id_idx] |= bit; *id = (uint16_t)(pool->id_idx * 8 + (uint32_t)bit_idx); pool->id_free_counter--; pool->id_bit = bit; pool->id_bit_idx = bit_idx; mutex_exit(&pool->id_mutex); return (0); } pool->id_bit = 1; pool->id_bit_idx = 0; pool->id_idx++; pool->id_idx &= pool->id_idx_msk; --i; } /* * This section of code shouldn't be reached. If there are IDs * available and none could be found there's a problem. */ ASSERT(0); mutex_exit(&pool->id_mutex); return (-1); } /* * smb_idpool_free * * This function frees the ID provided. */ void smb_idpool_free( smb_idpool_t *pool, uint16_t id) { ASSERT(pool->id_magic == SMB_IDPOOL_MAGIC); ASSERT(id != 0); ASSERT(id != 0xFFFF); mutex_enter(&pool->id_mutex); if (pool->id_pool[id >> 3] & (1 << (id & 7))) { pool->id_pool[id >> 3] &= ~(1 << (id & 7)); pool->id_free_counter++; ASSERT(pool->id_free_counter <= pool->id_max_free_counter); mutex_exit(&pool->id_mutex); return; } /* Freeing a free ID. */ ASSERT(0); mutex_exit(&pool->id_mutex); } /* * smb_llist_constructor * * This function initializes a locked list. */ void smb_llist_constructor( smb_llist_t *ll, size_t size, size_t offset) { rw_init(&ll->ll_lock, NULL, RW_DEFAULT, NULL); list_create(&ll->ll_list, size, offset); ll->ll_count = 0; ll->ll_wrop = 0; } /* * smb_llist_destructor * * This function destroys a locked list. */ void smb_llist_destructor( smb_llist_t *ll) { ASSERT(ll->ll_count == 0); rw_destroy(&ll->ll_lock); list_destroy(&ll->ll_list); } /* * smb_llist_upgrade * * This function tries to upgrade the lock of the locked list. It assumes the * locked has already been entered in RW_READER mode. It first tries using the * Solaris function rw_tryupgrade(). If that call fails the lock is released * and reentered in RW_WRITER mode. In that last case a window is opened during * which the contents of the list may have changed. The return code indicates * whether or not the list was modified when the lock was exited. */ int smb_llist_upgrade( smb_llist_t *ll) { uint64_t wrop; if (rw_tryupgrade(&ll->ll_lock) != 0) { return (0); } wrop = ll->ll_wrop; rw_exit(&ll->ll_lock); rw_enter(&ll->ll_lock, RW_WRITER); return (wrop != ll->ll_wrop); } /* * smb_llist_insert_head * * This function inserts the object passed a the beginning of the list. This * function assumes the lock of the list has already been entered. */ void smb_llist_insert_head( smb_llist_t *ll, void *obj) { list_insert_head(&ll->ll_list, obj); ++ll->ll_wrop; ++ll->ll_count; } /* * smb_llist_insert_tail * * This function appends to the object passed to the list. This function assumes * the lock of the list has already been entered. * */ void smb_llist_insert_tail( smb_llist_t *ll, void *obj) { list_insert_tail(&ll->ll_list, obj); ++ll->ll_wrop; ++ll->ll_count; } /* * smb_llist_remove * * This function removes the object passed from the list. This function assumes * the lock of the list has already been entered. */ void smb_llist_remove( smb_llist_t *ll, void *obj) { list_remove(&ll->ll_list, obj); ++ll->ll_wrop; --ll->ll_count; } /* * smb_llist_get_count * * This function returns the number of elements in the specified list. */ uint32_t smb_llist_get_count( smb_llist_t *ll) { return (ll->ll_count); } /* * smb_slist_constructor * * Synchronized list constructor. */ void smb_slist_constructor( smb_slist_t *sl, size_t size, size_t offset) { mutex_init(&sl->sl_mutex, NULL, MUTEX_DEFAULT, NULL); cv_init(&sl->sl_cv, NULL, CV_DEFAULT, NULL); list_create(&sl->sl_list, size, offset); sl->sl_count = 0; sl->sl_waiting = B_FALSE; } /* * smb_slist_destructor * * Synchronized list destructor. */ void smb_slist_destructor( smb_slist_t *sl) { ASSERT(sl->sl_count == 0); mutex_destroy(&sl->sl_mutex); cv_destroy(&sl->sl_cv); list_destroy(&sl->sl_list); } /* * smb_slist_insert_head * * This function inserts the object passed a the beginning of the list. */ void smb_slist_insert_head( smb_slist_t *sl, void *obj) { mutex_enter(&sl->sl_mutex); list_insert_head(&sl->sl_list, obj); ++sl->sl_count; mutex_exit(&sl->sl_mutex); } /* * smb_slist_insert_tail * * This function appends the object passed to the list. */ void smb_slist_insert_tail( smb_slist_t *sl, void *obj) { mutex_enter(&sl->sl_mutex); list_insert_tail(&sl->sl_list, obj); ++sl->sl_count; mutex_exit(&sl->sl_mutex); } /* * smb_llist_remove * * This function removes the object passed by the caller from the list. */ void smb_slist_remove( smb_slist_t *sl, void *obj) { mutex_enter(&sl->sl_mutex); list_remove(&sl->sl_list, obj); if ((--sl->sl_count == 0) && (sl->sl_waiting)) { sl->sl_waiting = B_FALSE; cv_broadcast(&sl->sl_cv); } mutex_exit(&sl->sl_mutex); } /* * smb_slist_move_tail * * This function transfers all the contents of the synchronized list to the * list_t provided. It returns the number of objects transferred. */ uint32_t smb_slist_move_tail( list_t *lst, smb_slist_t *sl) { uint32_t rv; mutex_enter(&sl->sl_mutex); rv = sl->sl_count; if (sl->sl_count) { list_move_tail(lst, &sl->sl_list); sl->sl_count = 0; if (sl->sl_waiting) { sl->sl_waiting = B_FALSE; cv_broadcast(&sl->sl_cv); } } mutex_exit(&sl->sl_mutex); return (rv); } /* * smb_slist_obj_move * * This function moves an object from one list to the end of the other list. It * assumes the mutex of each list has been entered. */ void smb_slist_obj_move( smb_slist_t *dst, smb_slist_t *src, void *obj) { ASSERT(dst->sl_list.list_offset == src->sl_list.list_offset); ASSERT(dst->sl_list.list_size == src->sl_list.list_size); list_remove(&src->sl_list, obj); list_insert_tail(&dst->sl_list, obj); dst->sl_count++; src->sl_count--; if ((src->sl_count == 0) && (src->sl_waiting)) { src->sl_waiting = B_FALSE; cv_broadcast(&src->sl_cv); } } /* * smb_slist_wait_for_empty * * This function waits for a list to be emptied. */ void smb_slist_wait_for_empty( smb_slist_t *sl) { mutex_enter(&sl->sl_mutex); while (sl->sl_count) { sl->sl_waiting = B_TRUE; cv_wait(&sl->sl_cv, &sl->sl_mutex); } mutex_exit(&sl->sl_mutex); } /* * smb_slist_exit * * This function exits the muetx of the list and signal the condition variable * if the list is empty. */ void smb_slist_exit(smb_slist_t *sl) { if ((sl->sl_count == 0) && (sl->sl_waiting)) { sl->sl_waiting = B_FALSE; cv_broadcast(&sl->sl_cv); } mutex_exit(&sl->sl_mutex); } /* * smb_thread_entry_point * * Common entry point for all the threads created through smb_thread_start. The * state of teh thread is set to "running" at the beginning and moved to * "exiting" just before calling thread_exit(). The condition variable is * also signaled. */ static void smb_thread_entry_point( smb_thread_t *thread) { ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); ASSERT(thread->sth_state == SMB_THREAD_STATE_STARTING); thread->sth_th = curthread; thread->sth_did = thread->sth_th->t_did; if (!thread->sth_kill) { thread->sth_state = SMB_THREAD_STATE_RUNNING; cv_signal(&thread->sth_cv); mutex_exit(&thread->sth_mtx); thread->sth_ep(thread, thread->sth_ep_arg); mutex_enter(&thread->sth_mtx); } thread->sth_th = NULL; thread->sth_state = SMB_THREAD_STATE_EXITING; cv_broadcast(&thread->sth_cv); mutex_exit(&thread->sth_mtx); thread_exit(); } /* * smb_thread_init */ void smb_thread_init( smb_thread_t *thread, char *name, smb_thread_ep_t ep, void *ep_arg, smb_thread_aw_t aw, void *aw_arg) { ASSERT(thread->sth_magic != SMB_THREAD_MAGIC); bzero(thread, sizeof (*thread)); (void) strlcpy(thread->sth_name, name, sizeof (thread->sth_name)); thread->sth_ep = ep; thread->sth_ep_arg = ep_arg; thread->sth_aw = aw; thread->sth_aw_arg = aw_arg; thread->sth_state = SMB_THREAD_STATE_EXITED; mutex_init(&thread->sth_mtx, NULL, MUTEX_DEFAULT, NULL); cv_init(&thread->sth_cv, NULL, CV_DEFAULT, NULL); thread->sth_magic = SMB_THREAD_MAGIC; } /* * smb_thread_destroy */ void smb_thread_destroy( smb_thread_t *thread) { ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); ASSERT(thread->sth_state == SMB_THREAD_STATE_EXITED); thread->sth_magic = 0; mutex_destroy(&thread->sth_mtx); cv_destroy(&thread->sth_cv); } /* * smb_thread_start * * This function starts a thread with the parameters provided. It waits until * the state of the thread has been moved to running. */ /*ARGSUSED*/ int smb_thread_start( smb_thread_t *thread) { int rc = 0; kthread_t *tmpthread; ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); switch (thread->sth_state) { case SMB_THREAD_STATE_EXITED: thread->sth_state = SMB_THREAD_STATE_STARTING; mutex_exit(&thread->sth_mtx); tmpthread = thread_create(NULL, 0, smb_thread_entry_point, thread, 0, &p0, TS_RUN, minclsyspri); ASSERT(tmpthread != NULL); mutex_enter(&thread->sth_mtx); while (thread->sth_state == SMB_THREAD_STATE_STARTING) cv_wait(&thread->sth_cv, &thread->sth_mtx); if (thread->sth_state != SMB_THREAD_STATE_RUNNING) rc = -1; break; default: ASSERT(0); rc = -1; break; } mutex_exit(&thread->sth_mtx); return (rc); } /* * smb_thread_stop * * This function signals a thread to kill itself and waits until the "exiting" * state has been reached. */ void smb_thread_stop( smb_thread_t *thread) { ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); switch (thread->sth_state) { case SMB_THREAD_STATE_RUNNING: case SMB_THREAD_STATE_STARTING: if (!thread->sth_kill) { thread->sth_kill = B_TRUE; if (thread->sth_aw) thread->sth_aw(thread, thread->sth_aw_arg); cv_broadcast(&thread->sth_cv); while (thread->sth_state != SMB_THREAD_STATE_EXITING) cv_wait(&thread->sth_cv, &thread->sth_mtx); mutex_exit(&thread->sth_mtx); thread_join(thread->sth_did); mutex_enter(&thread->sth_mtx); thread->sth_state = SMB_THREAD_STATE_EXITED; thread->sth_did = 0; thread->sth_kill = B_FALSE; cv_broadcast(&thread->sth_cv); break; } /*FALLTHRU*/ case SMB_THREAD_STATE_EXITING: if (thread->sth_kill) { while (thread->sth_state != SMB_THREAD_STATE_EXITED) cv_wait(&thread->sth_cv, &thread->sth_mtx); } else { thread->sth_state = SMB_THREAD_STATE_EXITED; thread->sth_did = 0; } break; case SMB_THREAD_STATE_EXITED: break; default: ASSERT(0); break; } mutex_exit(&thread->sth_mtx); } /* * smb_thread_signal * * This function signals a thread. */ void smb_thread_signal( smb_thread_t *thread) { ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); switch (thread->sth_state) { case SMB_THREAD_STATE_RUNNING: if (thread->sth_aw) thread->sth_aw(thread, thread->sth_aw_arg); cv_signal(&thread->sth_cv); break; default: break; } mutex_exit(&thread->sth_mtx); } boolean_t smb_thread_continue(smb_thread_t *thread) { boolean_t result; ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); result = smb_thread_continue_timedwait_locked(thread, 0); mutex_exit(&thread->sth_mtx); return (result); } boolean_t smb_thread_continue_nowait(smb_thread_t *thread) { boolean_t result; ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); /* * Setting ticks=-1 requests a non-blocking check. We will * still block if the thread is in "suspend" state. */ result = smb_thread_continue_timedwait_locked(thread, -1); mutex_exit(&thread->sth_mtx); return (result); } boolean_t smb_thread_continue_timedwait(smb_thread_t *thread, int seconds) { boolean_t result; ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); result = smb_thread_continue_timedwait_locked(thread, SEC_TO_TICK(seconds)); mutex_exit(&thread->sth_mtx); return (result); } /* * smb_thread_continue_timedwait_locked * * Internal only. Ticks==-1 means don't block, Ticks == 0 means wait * indefinitely */ static boolean_t smb_thread_continue_timedwait_locked(smb_thread_t *thread, int ticks) { boolean_t result; clock_t finish_time = lbolt + ticks; /* -1 means don't block */ if (ticks != -1 && !thread->sth_kill) { if (ticks == 0) { cv_wait(&thread->sth_cv, &thread->sth_mtx); } else { (void) cv_timedwait(&thread->sth_cv, &thread->sth_mtx, finish_time); } } result = (thread->sth_kill == 0); return (result); } void smb_thread_set_awaken(smb_thread_t *thread, smb_thread_aw_t new_aw_fn, void *new_aw_arg) { ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); thread->sth_aw = new_aw_fn; thread->sth_aw_arg = new_aw_arg; mutex_exit(&thread->sth_mtx); } /* * smb_rwx_init */ void smb_rwx_init( smb_rwx_t *rwx) { bzero(rwx, sizeof (smb_rwx_t)); cv_init(&rwx->rwx_cv, NULL, CV_DEFAULT, NULL); mutex_init(&rwx->rwx_mutex, NULL, MUTEX_DEFAULT, NULL); rw_init(&rwx->rwx_lock, NULL, RW_DEFAULT, NULL); } /* * smb_rwx_destroy */ void smb_rwx_destroy( smb_rwx_t *rwx) { mutex_destroy(&rwx->rwx_mutex); cv_destroy(&rwx->rwx_cv); rw_destroy(&rwx->rwx_lock); } /* * smb_rwx_rwexit */ void smb_rwx_rwexit( smb_rwx_t *rwx) { if (rw_write_held(&rwx->rwx_lock)) { ASSERT(rw_owner(&rwx->rwx_lock) == curthread); mutex_enter(&rwx->rwx_mutex); if (rwx->rwx_waiting) { rwx->rwx_waiting = B_FALSE; cv_broadcast(&rwx->rwx_cv); } mutex_exit(&rwx->rwx_mutex); } rw_exit(&rwx->rwx_lock); } /* * smb_rwx_rwupgrade */ krw_t smb_rwx_rwupgrade( smb_rwx_t *rwx) { if (rw_write_held(&rwx->rwx_lock)) { ASSERT(rw_owner(&rwx->rwx_lock) == curthread); return (RW_WRITER); } if (!rw_tryupgrade(&rwx->rwx_lock)) { rw_exit(&rwx->rwx_lock); rw_enter(&rwx->rwx_lock, RW_WRITER); } return (RW_READER); } /* * smb_rwx_rwrestore */ void smb_rwx_rwdowngrade( smb_rwx_t *rwx, krw_t mode) { ASSERT(rw_write_held(&rwx->rwx_lock)); ASSERT(rw_owner(&rwx->rwx_lock) == curthread); if (mode == RW_WRITER) { return; } ASSERT(mode == RW_READER); mutex_enter(&rwx->rwx_mutex); if (rwx->rwx_waiting) { rwx->rwx_waiting = B_FALSE; cv_broadcast(&rwx->rwx_cv); } mutex_exit(&rwx->rwx_mutex); rw_downgrade(&rwx->rwx_lock); } /* * smb_rwx_wait * * This function assumes the smb_rwx lock was enter in RW_READER or RW_WRITER * mode. It will: * * 1) release the lock and save its current mode. * 2) wait until the condition variable is signaled. This can happen for * 2 reasons: When a writer releases the lock or when the time out (if * provided) expires. * 3) re-acquire the lock in the mode saved in (1). */ int smb_rwx_rwwait( smb_rwx_t *rwx, clock_t timeout) { int rc; krw_t mode; mutex_enter(&rwx->rwx_mutex); rwx->rwx_waiting = B_TRUE; mutex_exit(&rwx->rwx_mutex); if (rw_write_held(&rwx->rwx_lock)) { ASSERT(rw_owner(&rwx->rwx_lock) == curthread); mode = RW_WRITER; } else { ASSERT(rw_read_held(&rwx->rwx_lock)); mode = RW_READER; } rw_exit(&rwx->rwx_lock); mutex_enter(&rwx->rwx_mutex); if (rwx->rwx_waiting) { if (timeout == -1) { rc = 1; cv_wait(&rwx->rwx_cv, &rwx->rwx_mutex); } else { rc = cv_timedwait(&rwx->rwx_cv, &rwx->rwx_mutex, lbolt + timeout); } } mutex_exit(&rwx->rwx_mutex); rw_enter(&rwx->rwx_lock, mode); return (rc); } /* * SMB ID mapping * * Solaris ID mapping service (aka Winchester) works with domain SIDs * and RIDs where domain SIDs are in string format. CIFS service works * with binary SIDs understandable by CIFS clients. A layer of SMB ID * mapping functions are implemeted to hide the SID conversion details * and also hide the handling of array of batch mapping requests. * * IMPORTANT NOTE The Winchester API requires a zone. Because CIFS server * currently only runs in the global zone the global zone is specified. * This needs to be fixed when the CIFS server supports zones. */ static int smb_idmap_batch_binsid(smb_idmap_batch_t *sib); /* * smb_idmap_getid * * Maps the given Windows SID to a Solaris ID using the * simple mapping API. */ idmap_stat smb_idmap_getid(smb_sid_t *sid, uid_t *id, int *idtype) { smb_idmap_t sim; char sidstr[SMB_SID_STRSZ]; smb_sid_tostr(sid, sidstr); if (smb_sid_splitstr(sidstr, &sim.sim_rid) != 0) return (IDMAP_ERR_SID); sim.sim_domsid = sidstr; sim.sim_id = id; switch (*idtype) { case SMB_IDMAP_USER: sim.sim_stat = kidmap_getuidbysid(global_zone, sim.sim_domsid, sim.sim_rid, sim.sim_id); break; case SMB_IDMAP_GROUP: sim.sim_stat = kidmap_getgidbysid(global_zone, sim.sim_domsid, sim.sim_rid, sim.sim_id); break; case SMB_IDMAP_UNKNOWN: sim.sim_stat = kidmap_getpidbysid(global_zone, sim.sim_domsid, sim.sim_rid, sim.sim_id, &sim.sim_idtype); break; default: ASSERT(0); return (IDMAP_ERR_ARG); } *idtype = sim.sim_idtype; return (sim.sim_stat); } /* * smb_idmap_getsid * * Maps the given Solaris ID to a Windows SID using the * simple mapping API. */ idmap_stat smb_idmap_getsid(uid_t id, int idtype, smb_sid_t **sid) { smb_idmap_t sim; switch (idtype) { case SMB_IDMAP_USER: sim.sim_stat = kidmap_getsidbyuid(global_zone, id, (const char **)&sim.sim_domsid, &sim.sim_rid); break; case SMB_IDMAP_GROUP: sim.sim_stat = kidmap_getsidbygid(global_zone, id, (const char **)&sim.sim_domsid, &sim.sim_rid); break; case SMB_IDMAP_EVERYONE: /* Everyone S-1-1-0 */ sim.sim_domsid = "S-1-1"; sim.sim_rid = 0; sim.sim_stat = IDMAP_SUCCESS; break; default: ASSERT(0); return (IDMAP_ERR_ARG); } if (sim.sim_stat != IDMAP_SUCCESS) return (sim.sim_stat); if (sim.sim_domsid == NULL) return (IDMAP_ERR_NOMAPPING); sim.sim_sid = smb_sid_fromstr(sim.sim_domsid); if (sim.sim_sid == NULL) return (IDMAP_ERR_INTERNAL); *sid = smb_sid_splice(sim.sim_sid, sim.sim_rid); smb_sid_free(sim.sim_sid); if (*sid == NULL) sim.sim_stat = IDMAP_ERR_INTERNAL; return (sim.sim_stat); } /* * smb_idmap_batch_create * * Creates and initializes the context for batch ID mapping. */ idmap_stat smb_idmap_batch_create(smb_idmap_batch_t *sib, uint16_t nmap, int flags) { ASSERT(sib); bzero(sib, sizeof (smb_idmap_batch_t)); sib->sib_idmaph = kidmap_get_create(global_zone); sib->sib_flags = flags; sib->sib_nmap = nmap; sib->sib_size = nmap * sizeof (smb_idmap_t); sib->sib_maps = kmem_zalloc(sib->sib_size, KM_SLEEP); return (IDMAP_SUCCESS); } /* * smb_idmap_batch_destroy * * Frees the batch ID mapping context. * If ID mapping is Solaris -> Windows it frees memories * allocated for binary SIDs. */ void smb_idmap_batch_destroy(smb_idmap_batch_t *sib) { char *domsid; int i; ASSERT(sib); ASSERT(sib->sib_maps); if (sib->sib_idmaph) kidmap_get_destroy(sib->sib_idmaph); if (sib->sib_flags & SMB_IDMAP_ID2SID) { /* * SIDs are allocated only when mapping * UID/GID to SIDs */ for (i = 0; i < sib->sib_nmap; i++) smb_sid_free(sib->sib_maps[i].sim_sid); } else if (sib->sib_flags & SMB_IDMAP_SID2ID) { /* * SID prefixes are allocated only when mapping * SIDs to UID/GID */ for (i = 0; i < sib->sib_nmap; i++) { domsid = sib->sib_maps[i].sim_domsid; if (domsid) kmem_free(domsid, strlen(domsid) + 1); } } if (sib->sib_size && sib->sib_maps) kmem_free(sib->sib_maps, sib->sib_size); } /* * smb_idmap_batch_getid * * Queue a request to map the given SID to a UID or GID. * * sim->sim_id should point to variable that's supposed to * hold the returned UID/GID. This needs to be setup by caller * of this function. * * If requested ID type is known, it's passed as 'idtype', * if it's unknown it'll be returned in sim->sim_idtype. */ idmap_stat smb_idmap_batch_getid(idmap_get_handle_t *idmaph, smb_idmap_t *sim, smb_sid_t *sid, int idtype) { char strsid[SMB_SID_STRSZ]; idmap_stat idm_stat; ASSERT(idmaph); ASSERT(sim); ASSERT(sid); smb_sid_tostr(sid, strsid); if (smb_sid_splitstr(strsid, &sim->sim_rid) != 0) return (IDMAP_ERR_SID); sim->sim_domsid = smb_kstrdup(strsid, strlen(strsid) + 1); switch (idtype) { case SMB_IDMAP_USER: idm_stat = kidmap_batch_getuidbysid(idmaph, sim->sim_domsid, sim->sim_rid, sim->sim_id, &sim->sim_stat); break; case SMB_IDMAP_GROUP: idm_stat = kidmap_batch_getgidbysid(idmaph, sim->sim_domsid, sim->sim_rid, sim->sim_id, &sim->sim_stat); break; case SMB_IDMAP_UNKNOWN: idm_stat = kidmap_batch_getpidbysid(idmaph, sim->sim_domsid, sim->sim_rid, sim->sim_id, &sim->sim_idtype, &sim->sim_stat); break; default: ASSERT(0); return (IDMAP_ERR_ARG); } return (idm_stat); } /* * smb_idmap_batch_getsid * * Queue a request to map the given UID/GID to a SID. * * sim->sim_domsid and sim->sim_rid will contain the mapping * result upon successful process of the batched request. */ idmap_stat smb_idmap_batch_getsid(idmap_get_handle_t *idmaph, smb_idmap_t *sim, uid_t id, int idtype) { idmap_stat idm_stat; switch (idtype) { case SMB_IDMAP_USER: idm_stat = kidmap_batch_getsidbyuid(idmaph, id, (const char **)&sim->sim_domsid, &sim->sim_rid, &sim->sim_stat); break; case SMB_IDMAP_GROUP: idm_stat = kidmap_batch_getsidbygid(idmaph, id, (const char **)&sim->sim_domsid, &sim->sim_rid, &sim->sim_stat); break; case SMB_IDMAP_EVERYONE: /* Everyone S-1-1-0 */ sim->sim_domsid = "S-1-1"; sim->sim_rid = 0; sim->sim_stat = IDMAP_SUCCESS; idm_stat = IDMAP_SUCCESS; break; default: ASSERT(0); return (IDMAP_ERR_ARG); } return (idm_stat); } /* * smb_idmap_batch_binsid * * Convert sidrids to binary sids * * Returns 0 if successful and non-zero upon failure. */ static int smb_idmap_batch_binsid(smb_idmap_batch_t *sib) { smb_sid_t *sid; smb_idmap_t *sim; int i; if (sib->sib_flags & SMB_IDMAP_SID2ID) /* This operation is not required */ return (0); sim = sib->sib_maps; for (i = 0; i < sib->sib_nmap; sim++, i++) { ASSERT(sim->sim_domsid); if (sim->sim_domsid == NULL) return (1); if ((sid = smb_sid_fromstr(sim->sim_domsid)) == NULL) return (1); sim->sim_sid = smb_sid_splice(sid, sim->sim_rid); smb_sid_free(sid); } return (0); } /* * smb_idmap_batch_getmappings * * trigger ID mapping service to get the mappings for queued * requests. * * Checks the result of all the queued requests. * If this is a Solaris -> Windows mapping it generates * binary SIDs from returned (domsid, rid) pairs. */ idmap_stat smb_idmap_batch_getmappings(smb_idmap_batch_t *sib) { idmap_stat idm_stat = IDMAP_SUCCESS; int i; idm_stat = kidmap_get_mappings(sib->sib_idmaph); if (idm_stat != IDMAP_SUCCESS) return (idm_stat); /* * Check the status for all the queued requests */ for (i = 0; i < sib->sib_nmap; i++) { if (sib->sib_maps[i].sim_stat != IDMAP_SUCCESS) return (sib->sib_maps[i].sim_stat); } if (smb_idmap_batch_binsid(sib) != 0) idm_stat = IDMAP_ERR_OTHER; return (idm_stat); } uint64_t unix_to_nt_time(timestruc_t *unix_time) { uint64_t nt_time; nt_time = unix_time->tv_sec; nt_time *= 10000000; /* seconds to 100ns */ nt_time += unix_time->tv_nsec / 100; return (nt_time + NT_TIME_BIAS); } uint32_t nt_to_unix_time(uint64_t nt_time, timestruc_t *unix_time) { uint32_t seconds; nt_time -= NT_TIME_BIAS; seconds = nt_time / 10000000; if (unix_time) { unix_time->tv_sec = seconds; unix_time->tv_nsec = (nt_time % 10000000) * 100; } return (seconds); } /* * smb_dos_to_ux_time * * Convert SMB_DATE & SMB_TIME values to a unix timestamp. * * A date/time field of 0 means that that server file system * assigned value need not be changed. The behaviour when the * date/time field is set to -1 is not documented but is * generally treated like 0. * If date or time is 0 or -1 the unix time is returned as 0 * so that the caller can identify and handle this special case. */ int32_t smb_dos_to_ux_time(int16_t date, int16_t time) { struct tm atm; if (((date == 0) || (time == 0)) || ((date == -1) || (time == -1))) { return (0); } atm.tm_year = ((date >> 9) & 0x3F) + 80; atm.tm_mon = ((date >> 5) & 0x0F) - 1; atm.tm_mday = ((date >> 0) & 0x1F); atm.tm_hour = ((time >> 11) & 0x1F); atm.tm_min = ((time >> 5) & 0x3F); atm.tm_sec = ((time >> 0) & 0x1F) << 1; return (smb_timegm(&atm)); } int32_t smb_ux_to_dos_time(int32_t ux_time, int16_t *date_p, int16_t *time_p) { struct tm atm; int i; time_t tmp_time; tmp_time = (time_t)ux_time; (void) smb_gmtime_r(&tmp_time, &atm); if (date_p) { i = 0; i += atm.tm_year - 80; i <<= 4; i += atm.tm_mon + 1; i <<= 5; i += atm.tm_mday; *date_p = (short)i; } if (time_p) { i = 0; i += atm.tm_hour; i <<= 6; i += atm.tm_min; i <<= 5; i += atm.tm_sec >> 1; *time_p = (short)i; } return (ux_time); } /* * smb_gmtime_r * * Thread-safe version of smb_gmtime. Returns a null pointer if either * input parameter is a null pointer. Otherwise returns a pointer * to result. * * Day of the week calculation: the Epoch was a thursday. * * There are no timezone corrections so tm_isdst and tm_gmtoff are * always zero, and the zone is always WET. */ struct tm * smb_gmtime_r(time_t *clock, struct tm *result) { time_t tsec; int year; int month; int sec_per_month; if (clock == 0 || result == 0) return (0); bzero(result, sizeof (struct tm)); tsec = *clock; tsec -= tzh_leapcnt; result->tm_wday = tsec / SECSPERDAY; result->tm_wday = (result->tm_wday + TM_THURSDAY) % DAYSPERWEEK; year = EPOCH_YEAR; while (tsec >= (isleap(year) ? (SECSPERDAY * DAYSPERLYEAR) : (SECSPERDAY * DAYSPERNYEAR))) { if (isleap(year)) tsec -= SECSPERDAY * DAYSPERLYEAR; else tsec -= SECSPERDAY * DAYSPERNYEAR; ++year; } result->tm_year = year - TM_YEAR_BASE; result->tm_yday = tsec / SECSPERDAY; for (month = TM_JANUARY; month <= TM_DECEMBER; ++month) { sec_per_month = days_in_month[month] * SECSPERDAY; if (month == TM_FEBRUARY && isleap(year)) sec_per_month += SECSPERDAY; if (tsec < sec_per_month) break; tsec -= sec_per_month; } result->tm_mon = month; result->tm_mday = (tsec / SECSPERDAY) + 1; tsec %= SECSPERDAY; result->tm_sec = tsec % 60; tsec /= 60; result->tm_min = tsec % 60; tsec /= 60; result->tm_hour = (int)tsec; return (result); } /* * smb_timegm * * Converts the broken-down time in tm to a time value, i.e. the number * of seconds since the Epoch (00:00:00 UTC, January 1, 1970). This is * not a POSIX or ANSI function. Per the man page, the input values of * tm_wday and tm_yday are ignored and, as the input data is assumed to * represent GMT, we force tm_isdst and tm_gmtoff to 0. * * Before returning the clock time, we use smb_gmtime_r to set up tm_wday * and tm_yday, and bring the other fields within normal range. I don't * think this is really how it should be done but it's convenient for * now. */ time_t smb_timegm(struct tm *tm) { time_t tsec; int dd; int mm; int yy; int year; if (tm == 0) return (-1); year = tm->tm_year + TM_YEAR_BASE; tsec = tzh_leapcnt; for (yy = EPOCH_YEAR; yy < year; ++yy) { if (isleap(yy)) tsec += SECSPERDAY * DAYSPERLYEAR; else tsec += SECSPERDAY * DAYSPERNYEAR; } for (mm = TM_JANUARY; mm < tm->tm_mon; ++mm) { dd = days_in_month[mm] * SECSPERDAY; if (mm == TM_FEBRUARY && isleap(year)) dd += SECSPERDAY; tsec += dd; } tsec += (tm->tm_mday - 1) * SECSPERDAY; tsec += tm->tm_sec; tsec += tm->tm_min * SECSPERMIN; tsec += tm->tm_hour * SECSPERHOUR; tm->tm_isdst = 0; (void) smb_gmtime_r(&tsec, tm); return (tsec); } #ifdef DEBUG uint32_t smb_audit_flags = SMB_AUDIT_NODE; #else uint32_t smb_audit_flags = 0; #endif void smb_audit_buf_node_create(smb_node_t *node) { smb_audit_buf_node_t *abn; if (smb_audit_flags & SMB_AUDIT_NODE) { abn = kmem_zalloc(sizeof (smb_audit_buf_node_t), KM_SLEEP); abn->anb_max_index = SMB_AUDIT_BUF_MAX_REC - 1; node->n_audit_buf = abn; } } void smb_audit_buf_node_destroy(smb_node_t *node) { smb_audit_buf_node_t *abn; abn = node->n_audit_buf; if (abn) { node->n_audit_buf = NULL; kmem_free(abn, sizeof (smb_audit_buf_node_t)); } } /* * smb_cred_set_sid * * Initialize the ksid based on the given smb_id_t. */ static void smb_cred_set_sid(smb_id_t *id, ksid_t *ksid) { char sidstr[SMB_SID_STRSZ]; int rc; ASSERT(id); ASSERT(id->i_sidattr.sid); ksid->ks_id = id->i_id; smb_sid_tostr(id->i_sidattr.sid, sidstr); rc = smb_sid_splitstr(sidstr, &ksid->ks_rid); ASSERT(rc == 0); ksid->ks_attr = id->i_sidattr.attrs; ksid->ks_domain = ksid_lookupdomain(sidstr); } /* * smb_cred_set_sidlist * * Allocate and initialize the ksidlist based on the Windows group list of the * access token. */ static ksidlist_t * smb_cred_set_sidlist(smb_win_grps_t *token_grps) { int i; ksidlist_t *lp; lp = kmem_zalloc(KSIDLIST_MEM(token_grps->wg_count), KM_SLEEP); lp->ksl_ref = 1; lp->ksl_nsid = token_grps->wg_count; lp->ksl_neid = 0; for (i = 0; i < lp->ksl_nsid; i++) { smb_cred_set_sid(&token_grps->wg_groups[i], &lp->ksl_sids[i]); if (lp->ksl_sids[i].ks_id > IDMAP_WK__MAX_GID) lp->ksl_neid++; } return (lp); } /* * smb_cred_create * * The credential of the given SMB user will be allocated and initialized based * on the given access token. */ cred_t * smb_cred_create(smb_token_t *token, uint32_t *privileges) { ksid_t ksid; ksidlist_t *ksidlist = NULL; smb_posix_grps_t *posix_grps; cred_t *cr; ASSERT(token); ASSERT(token->tkn_posix_grps); ASSERT(privileges); cr = crget(); ASSERT(cr != NULL); posix_grps = token->tkn_posix_grps; if (crsetugid(cr, token->tkn_user->i_id, token->tkn_primary_grp->i_id) != 0) { crfree(cr); return (NULL); } if (crsetgroups(cr, posix_grps->pg_ngrps, posix_grps->pg_grps) != 0) { crfree(cr); return (NULL); } smb_cred_set_sid(token->tkn_user, &ksid); crsetsid(cr, &ksid, KSID_USER); smb_cred_set_sid(token->tkn_primary_grp, &ksid); crsetsid(cr, &ksid, KSID_GROUP); smb_cred_set_sid(token->tkn_owner, &ksid); crsetsid(cr, &ksid, KSID_OWNER); ksidlist = smb_cred_set_sidlist(token->tkn_win_grps); crsetsidlist(cr, ksidlist); *privileges = 0; if (smb_token_query_privilege(token, SE_BACKUP_LUID)) { *privileges |= SMB_USER_PRIV_BACKUP; } if (smb_token_query_privilege(token, SE_RESTORE_LUID)) { *privileges |= SMB_USER_PRIV_RESTORE; } if (smb_token_query_privilege(token, SE_TAKE_OWNERSHIP_LUID)) { *privileges |= SMB_USER_PRIV_TAKE_OWNERSHIP; (void) crsetpriv(cr, PRIV_FILE_CHOWN, NULL); } if (smb_token_query_privilege(token, SE_SECURITY_LUID)) { *privileges |= SMB_USER_PRIV_SECURITY; } return (cr); } /* * smb_cred_rele * * The reference count of the user's credential will get decremented if it * is non-zero. Otherwise, the credential will be freed. */ void smb_cred_rele(cred_t *cr) { ASSERT(cr); crfree(cr); } /* * smb_cred_is_member * * Same as smb_token_is_member. The only difference is that * we compare the given SID against user SID and the ksidlist * of the user's cred. */ int smb_cred_is_member(cred_t *cr, smb_sid_t *sid) { ksidlist_t *ksidlist; ksid_t ksid1, *ksid2; smb_id_t id; int i, rc = 0; ASSERT(cr); bzero(&id, sizeof (smb_id_t)); id.i_sidattr.sid = sid; smb_cred_set_sid(&id, &ksid1); ksidlist = crgetsidlist(cr); ASSERT(ksidlist); ASSERT(ksid1.ks_domain); ASSERT(ksid1.ks_domain->kd_name); i = 0; ksid2 = crgetsid(cr, KSID_USER); do { ASSERT(ksid2->ks_domain); ASSERT(ksid2->ks_domain->kd_name); if (strcmp(ksid1.ks_domain->kd_name, ksid2->ks_domain->kd_name) == 0 && ksid1.ks_rid == ksid2->ks_rid) { rc = 1; break; } ksid2 = &ksidlist->ksl_sids[i]; } while (i++ < ksidlist->ksl_nsid); ksid_rele(&ksid1); return (rc); } /* * smb_kstrdup * * Duplicate the given string s. */ char * smb_kstrdup(const char *s, size_t n) { char *s2; ASSERT(s); ASSERT(n); s2 = kmem_alloc(n, KM_SLEEP); (void) strlcpy(s2, s, n); return (s2); } /* * smb_sync_fsattr * * Sync file's attributes with file system. * The sync takes place based on node->what and node->flags * values. */ int smb_sync_fsattr(struct smb_request *sr, cred_t *cr, smb_node_t *node) { uint32_t what; int rc = 0; if (node->flags & NODE_FLAGS_SET_SIZE) { node->flags &= ~NODE_FLAGS_SET_SIZE; node->what |= SMB_AT_SIZE; node->attr.sa_vattr.va_size = node->n_size; } if (node->what) { /* * This is to prevent another thread from starting * a setattr should this one go to sleep */ what = node->what; node->what = 0; node->attr.sa_mask = what; rc = smb_fsop_setattr(sr, cr, node, &node->attr, &node->attr); if (rc) { /* setattr failed, restore the dirty state? */ node->what = what; } else { if (what & SMB_AT_ATIME) node->flags &= ~NODE_FLAGS_SYNCATIME; } } return (rc); } /* * smb_cred_create_privs * * Creates a duplicate credential that contains system privileges for * certain SMB privileges: Backup and Restore. * */ cred_t * smb_cred_create_privs(cred_t *user_cr, uint32_t privileges) { cred_t *cr = NULL; ASSERT(user_cr != NULL); if (privileges & (SMB_USER_PRIV_BACKUP | SMB_USER_PRIV_RESTORE)) cr = crdup(user_cr); if (cr == NULL) return (NULL); if (privileges & SMB_USER_PRIV_BACKUP) { (void) crsetpriv(cr, PRIV_FILE_DAC_READ, PRIV_FILE_DAC_SEARCH, PRIV_SYS_MOUNT, NULL); } if (privileges & SMB_USER_PRIV_RESTORE) { (void) crsetpriv(cr, PRIV_FILE_DAC_WRITE, PRIV_FILE_CHOWN, PRIV_FILE_CHOWN_SELF, PRIV_FILE_DAC_SEARCH, PRIV_FILE_LINK_ANY, PRIV_FILE_OWNER, PRIV_FILE_SETID, PRIV_SYS_LINKDIR, PRIV_SYS_MOUNT, NULL); } return (cr); }