smb_util.c revision b1352070d318187b41b088da3533692976f3f225
/*
* 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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <smbsrv/smb_incl.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/smb_vops.h>
#include <smbsrv/smb_idmap.h>
#include <sys/priv_names.h>
#define SMB_NAME83_BASELEN 8
#define SMB_NAME83_EXTLEN 3
static void smb_replace_wildcards(char *);
static boolean_t
time_t tzh_leapcnt = 0;
struct 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
{
return (mts_wcequiv_strlen(str));
}
int
{
}
int
{
return (2);
return (1);
}
/*
* Substitute wildcards and return the number of wildcards in the post
* conversion pattern.
*/
int
smb_convert_wildcards(char *pattern)
{
char *p = pattern;
int n_wildcard = 0;
while (*p != '\0') {
if (*p == '*' || *p == '?')
++n_wildcard;
++p;
}
return (n_wildcard);
}
/*
* When replacing wildcards a '.' in a name is treated as a base and
* extension separator even if the name is longer than 8.3.
*
* The '*' character matches an entire part of the name. For example,
* "*.abc" matches any name with an extension of "abc".
*
* The '?' character matches a single character.
* If the base contains all ? (8 or more) then it is treated as *.
* If the extension contains all ? (3 or more) then it is treated as *.
*
* Clients convert ASCII wildcards to Unicode wildcards as follows:
*
* ? is converted to >
* . is converted to " if it is followed by ? or *
* * is converted to < if it is followed by .
*
* Note that clients convert "*." to '< and drop the '.' but "*.txt"
* is sent as "<.TXT", i.e.
*
* dir *. -> dir <
* dir *.txt -> dir <.TXT
*
* Since " and < are illegal in Windows file names, we always convert
* these Unicode wildcards without checking the following character.
*/
static void
smb_replace_wildcards(char *pattern)
{
static char *match_all[] = {
"*.",
"*.*"
};
char *extension;
char *p;
int len;
int i;
/*
* Special case "<" for "dir *.", and fast-track for "*".
*/
*pattern = '*';
return;
}
}
for (p = pattern; *p != '\0'; ++p) {
switch (*p) {
case '<':
*p = '*';
break;
case '>':
*p = '?';
break;
case '\"':
*p = '.';
break;
default:
break;
}
}
/*
* Replace "????????.ext" with "*.ext".
*/
p = pattern;
p += strspn(p, "?");
if (*p == '.') {
*p = '\0';
*p = '.';
if (len >= SMB_NAME83_BASELEN) {
*pattern = '*';
}
}
/*
* Replace "base.???" with 'base.*'.
*/
p = ++extension;
p += strspn(p, "?");
if (*p == '\0') {
if (len >= SMB_NAME83_EXTLEN) {
*extension = '\0';
}
}
}
/*
* Replace anything that matches an entry in match_all with "*".
*/
break;
}
}
}
/*
* 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.
*/
{
if ((dosattr & FILE_ATTRIBUTE_DIRECTORY) &&
return (B_FALSE);
if ((dosattr & FILE_ATTRIBUTE_HIDDEN) &&
!(sattr & FILE_ATTRIBUTE_HIDDEN))
return (B_FALSE);
if ((dosattr & FILE_ATTRIBUTE_SYSTEM) &&
!(sattr & FILE_ATTRIBUTE_SYSTEM))
return (B_FALSE);
return (B_TRUE);
}
/*
* smb_stream_parse_name
*
* smb_stream_parse_name should only be called for a path that
* contains a valid named stream. Path validation should have
* been performed before this function is called.
*
* Find the last component of path and split it into filename
* and stream name.
*
* On return the named stream type will be present. The stream
* type defaults to ":$DATA", if it has not been defined
* For exmaple, 'stream' contains :<sname>:$DATA
*/
void
{
*sname = '\0';
else
(void) utf8_strupr(stype);
}
/*
* smb_is_stream_name
*
* Determines if 'path' specifies a named stream.
*
* path is a NULL terminated string which could be a stream path.
* [pathname/]fname[:stream_name[:stream_type]]
*
* - If there is no colon in the path or it's the last char
* then it's not a stream name
*
* - '::' is a non-stream and is commonly used by Windows to designate
* the unamed stream in the form "::$DATA"
*/
smb_is_stream_name(char *path)
{
char *colonp;
return (B_FALSE);
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
}
/*
* smb_validate_stream_name
*
* NT_STATUS_OBJECT_NAME_INVALID will be returned if:
* - the path is not a stream name
* - a path is specified but the fname is ommitted.
* - the stream_type is specified but not valid.
*
* Note: the stream type is case-insensitive.
*/
{
static char *strmtype[] = {
"$DATA",
"$INDEX_ALLOCATION"
};
int i;
return (NT_STATUS_OBJECT_NAME_INVALID);
return (NT_STATUS_OBJECT_NAME_INVALID);
return (NT_STATUS_SUCCESS);
}
return (NT_STATUS_OBJECT_NAME_INVALID);
}
return (NT_STATUS_SUCCESS);
}
int
{
return (0);
}
{
return (TICK_TO_MSEC(lbolt));
}
int /*ARGSUSED*/
{
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
{
if (new_size <= SMB_IDPOOL_MAX_SIZE) {
if (new_pool) {
if (new_size >= SMB_IDPOOL_MAX_SIZE) {
/* id -1 made unavailable */
pool->id_free_counter--;
}
return (0);
}
}
return (-1);
}
/*
* smb_idpool_constructor
*
* This function initializes the pool structure provided.
*/
int
{
KM_SLEEP);
/* -1 id made unavailable */
return (0);
}
/*
* smb_idpool_destructor
*
* This function tears down and frees the resources associated with the
* pool provided.
*/
void
{
}
/*
* smb_idpool_alloc
*
* This function allocates an ID from the pool provided.
*/
int
{
uint32_t i;
return (-1);
}
while (i) {
while (bit) {
bit_idx++;
continue;
}
pool->id_free_counter--;
return (0);
}
pool->id_bit_idx = 0;
--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);
return (-1);
}
/*
* smb_idpool_free
*
* This function frees the ID provided.
*/
void
{
pool->id_free_counter++;
return;
}
/* Freeing a free ID. */
ASSERT(0);
}
/*
* smb_llist_constructor
*
* This function initializes a locked list.
*/
void
{
}
/*
* smb_llist_destructor
*
* This function destroys a locked list.
*/
void
{
}
/*
* 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(
{
return (0);
}
}
/*
* 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
void *obj)
{
}
/*
* 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
void *obj)
{
}
/*
* 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
void *obj)
{
}
/*
* smb_llist_get_count
*
* This function returns the number of elements in the specified list.
*/
{
}
/*
* smb_slist_constructor
*
* Synchronized list constructor.
*/
void
{
}
/*
* smb_slist_destructor
*
* Synchronized list destructor.
*/
void
{
}
/*
* smb_slist_insert_head
*
* This function inserts the object passed a the beginning of the list.
*/
void
void *obj)
{
}
/*
* smb_slist_insert_tail
*
* This function appends the object passed to the list.
*/
void
void *obj)
{
}
/*
* smb_llist_remove
*
* This function removes the object passed by the caller from the list.
*/
void
void *obj)
{
}
}
/*
* 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.
*/
{
if (sl->sl_waiting) {
}
}
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
void *obj)
{
}
}
/*
* smb_slist_wait_for_empty
*
* This function waits for a list to be emptied.
*/
void
{
}
}
/*
* smb_slist_exit
*
* This function exits the muetx of the list and signal the condition variable
* if the list is empty.
*/
void
{
}
}
/*
* 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
{
}
thread_exit();
}
/*
* smb_thread_init
*/
void
char *name,
void *ep_arg,
void *aw_arg)
{
}
/*
* smb_thread_destroy
*/
void
{
}
/*
* 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
{
int rc = 0;
case SMB_THREAD_STATE_EXITED:
rc = -1;
break;
default:
ASSERT(0);
rc = -1;
break;
}
return (rc);
}
/*
* smb_thread_stop
*
* This function signals a thread to kill itself and waits until the "exiting"
* state has been reached.
*/
void
{
case SMB_THREAD_STATE_RUNNING:
break;
}
/*FALLTHRU*/
case SMB_THREAD_STATE_EXITING:
} else {
}
break;
case SMB_THREAD_STATE_EXITED:
break;
default:
ASSERT(0);
break;
}
}
/*
* smb_thread_signal
*
* This function signals a thread.
*/
void
{
case SMB_THREAD_STATE_RUNNING:
break;
default:
break;
}
}
{
return (result);
}
{
/*
* Setting ticks=-1 requests a non-blocking check. We will
* still block if the thread is in "suspend" state.
*/
return (result);
}
{
return (result);
}
/*
* smb_thread_continue_timedwait_locked
*
* Internal only. Ticks==-1 means don't block, Ticks == 0 means wait
* indefinitely
*/
static boolean_t
{
/* -1 means don't block */
if (ticks == 0) {
} else {
}
}
return (result);
}
void
void *new_aw_arg)
{
}
/*
* smb_rwx_init
*/
void
{
}
/*
* smb_rwx_destroy
*/
void
{
}
/*
* smb_rwx_rwexit
*/
void
{
if (rwx->rwx_waiting) {
}
}
}
/*
* smb_rwx_rwupgrade
*/
{
return (RW_WRITER);
}
}
return (RW_READER);
}
/*
* smb_rwx_rwrestore
*/
void
{
return;
}
if (rwx->rwx_waiting) {
}
}
/*
* 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
{
int rc;
} else {
}
if (rwx->rwx_waiting) {
if (timeout == -1) {
rc = 1;
} else {
}
}
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.
*/
/*
* smb_idmap_getid
*
* Maps the given Windows SID to a Solaris ID using the
* simple mapping API.
*/
{
char sidstr[SMB_SID_STRSZ];
return (IDMAP_ERR_SID);
switch (*idtype) {
case SMB_IDMAP_USER:
break;
case SMB_IDMAP_GROUP:
break;
case SMB_IDMAP_UNKNOWN:
break;
default:
ASSERT(0);
return (IDMAP_ERR_ARG);
}
}
/*
* smb_idmap_getsid
*
* Maps the given Solaris ID to a Windows SID using the
* simple mapping API.
*/
{
switch (idtype) {
case SMB_IDMAP_USER:
break;
case SMB_IDMAP_GROUP:
break;
case SMB_IDMAP_EVERYONE:
/* Everyone S-1-1-0 */
break;
default:
ASSERT(0);
return (IDMAP_ERR_ARG);
}
return (IDMAP_ERR_NOMAPPING);
return (IDMAP_ERR_INTERNAL);
}
/*
* smb_idmap_batch_create
*
* Creates and initializes the context for batch ID mapping.
*/
{
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
{
char *domsid;
int i;
if (sib->sib_idmaph)
/*
* SIDs are allocated only when mapping
*/
/*
* SID prefixes are allocated only when mapping
*/
if (domsid)
}
}
}
/*
* 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
* 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.
*/
{
char strsid[SMB_SID_STRSZ];
return (IDMAP_ERR_SID);
switch (idtype) {
case SMB_IDMAP_USER:
break;
case SMB_IDMAP_GROUP:
break;
case SMB_IDMAP_UNKNOWN:
break;
default:
ASSERT(0);
return (IDMAP_ERR_ARG);
}
return (idm_stat);
}
/*
* smb_idmap_batch_getsid
*
*
* sim->sim_domsid and sim->sim_rid will contain the mapping
* result upon successful process of the batched request.
*/
{
switch (idtype) {
case SMB_IDMAP_USER:
break;
case SMB_IDMAP_GROUP:
break;
case SMB_IDMAP_EVERYONE:
/* Everyone S-1-1-0 */
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
{
int i;
/* This operation is not required */
return (0);
return (1);
return (1);
}
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.
*/
{
int i;
if (idm_stat != IDMAP_SUCCESS)
return (idm_stat);
/*
* Check the status for all the queued requests
*/
}
if (smb_idmap_batch_binsid(sib) != 0)
return (idm_stat);
}
{
return (nt_time + NT_TIME_BIAS);
}
{
nt_time -= NT_TIME_BIAS;
if (unix_time) {
}
return (seconds);
}
/*
* smb_dos_to_ux_time
*
* Convert SMB_DATE & SMB_TIME values to a unix timestamp.
*
* assigned value need not be changed. The behaviour when the
* 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.
*/
{
return (0);
}
return (smb_timegm(&atm));
}
{
int i;
if (date_p) {
i = 0;
i <<= 4;
i <<= 5;
*date_p = (short)i;
}
if (time_p) {
i = 0;
i <<= 6;
i <<= 5;
*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 *
{
int year;
int month;
int sec_per_month;
return (0);
tsec -= tzh_leapcnt;
year = EPOCH_YEAR;
(SECSPERDAY * DAYSPERNYEAR))) {
else
++year;
}
if (tsec < sec_per_month)
break;
tsec -= sec_per_month;
}
tsec %= SECSPERDAY;
tsec /= 60;
tsec /= 60;
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.
*/
{
int dd;
int mm;
int yy;
int year;
if (tm == 0)
return (-1);
tsec = tzh_leapcnt;
else
}
dd += SECSPERDAY;
}
return (tsec);
}
/*
* smb_cred_set_sid
*
* Initialize the ksid based on the given smb_id_t.
*/
static void
{
char sidstr[SMB_SID_STRSZ];
int rc;
}
/*
* smb_cred_set_sidlist
*
* Allocate and initialize the ksidlist based on the Windows group list of the
* access token.
*/
static ksidlist_t *
{
int i;
ksidlist_t *lp;
}
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 *
{
return (NULL);
}
return (NULL);
}
*privileges = 0;
}
}
}
}
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_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
{
int i, rc = 0;
i = 0;
do {
rc = 1;
break;
}
return (rc);
}
/*
* smb_kstrdup
*
* Duplicate the given string s.
*/
char *
smb_kstrdup(const char *s, size_t n)
{
char *s2;
ASSERT(s);
ASSERT(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
{
int rc = 0;
}
/*
* This is to prevent another thread from starting
* a setattr should this one go to sleep
*/
if (rc) {
/* setattr failed, restore the dirty state? */
} else {
if (what & SMB_AT_ATIME)
}
}
return (rc);
}
/*
* smb_cred_create_privs
*
* Creates a duplicate credential that contains system privileges for
* certain SMB privileges: Backup and Restore.
*
*/
cred_t *
{
return (NULL);
if (privileges & SMB_USER_PRIV_BACKUP) {
}
if (privileges & SMB_USER_PRIV_RESTORE) {
}
return (cr);
}
/*
* smb_panic
*
* Logs the file name, function name and line number passed in and panics the
* system.
*/
void
{
}