/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) #include #else #include #endif #include void smb_cfg_set_require(const char *value, smb_cfg_val_t *cfg) { if (value == NULL) { *cfg = SMB_CONFIG_DISABLED; return; } if (strcmp(value, "required") == 0) *cfg = SMB_CONFIG_REQUIRED; else if (strcmp(value, "enabled") == 0) *cfg = SMB_CONFIG_ENABLED; else *cfg = SMB_CONFIG_DISABLED; } /* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * Legacy encode/decode routines for door clients and servers. */ #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) #include #include #include #else #include #include #include #endif #include #include #include #include #include smb_dr_ctx_t * smb_dr_decode_start(char *ptr, int size) { smb_dr_ctx_t *ctx = MEM_MALLOC("CommonDoor", sizeof (smb_dr_ctx_t)); if (ctx) { ctx->start_ptr = ctx->ptr = ptr; ctx->end_ptr = ptr + size; ctx->status = 0; } return (ctx); } int smb_dr_decode_finish(smb_dr_ctx_t *ctx) { int status = ctx->status; if (status == 0 && ctx->ptr != ctx->end_ptr) status = ENOTEMPTY; MEM_FREE("CommonDoor", ctx); return (status); } smb_dr_ctx_t * smb_dr_encode_start(char *ptr, int size) { smb_dr_ctx_t *ctx = MEM_MALLOC("CommonDoor", sizeof (smb_dr_ctx_t)); if (ctx) { ctx->start_ptr = ctx->ptr = ptr; ctx->end_ptr = ptr + size; ctx->status = 0; } return (ctx); } int smb_dr_encode_finish(smb_dr_ctx_t *ctx, unsigned int *used) { int status = ctx->status; if (status == 0) { if (ctx->ptr < ctx->end_ptr) { /*LINTED E_PTRDIFF_OVERFLOW*/ *used = ctx->ptr - ctx->start_ptr; } else { status = ENOSPC; } } MEM_FREE("CommonDoor", ctx); return (status); } DWORD smb_dr_get_dword(smb_dr_ctx_t *ctx) { DWORD num = 0; if (ctx->status == 0) { if (ctx->ptr + sizeof (DWORD) <= ctx->end_ptr) { (void) memcpy(&num, ctx->ptr, sizeof (DWORD)); ctx->ptr += sizeof (DWORD); } else { ctx->status = ENOSPC; } } return (num); } int32_t smb_dr_get_int32(smb_dr_ctx_t *ctx) { int32_t num = 0; if (ctx->status == 0) { if (ctx->ptr + sizeof (int32_t) <= ctx->end_ptr) { (void) memcpy(&num, ctx->ptr, sizeof (int32_t)); ctx->ptr += sizeof (int32_t); } else { ctx->status = ENOSPC; } } return (num); } uint32_t smb_dr_get_uint32(smb_dr_ctx_t *ctx) { return ((uint32_t)smb_dr_get_int32(ctx)); } char * smb_dr_get_string(smb_dr_ctx_t *ctx) { char *buf = NULL; int len = smb_dr_get_int32(ctx); if (ctx->status == 0) { if (len == -1) return (buf); if (ctx->ptr + len <= ctx->end_ptr) { buf = MEM_MALLOC("CommonDoor", len +1); if (buf) { if (len == 0) { (void) strcpy(buf, ""); } else { (void) memcpy(buf, ctx->ptr, len); ctx->ptr += len; *(buf + len) = '\0'; } } else { #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) ctx->status = errno; #else ctx->status = ENOMEM; #endif } } else { ctx->status = ENOSPC; } } return (buf); } void smb_dr_put_dword(smb_dr_ctx_t *ctx, DWORD num) { if (ctx->status == 0) { if (ctx->ptr + sizeof (DWORD) <= ctx->end_ptr) { (void) memcpy(ctx->ptr, &num, sizeof (DWORD)); ctx->ptr += sizeof (DWORD); } else { ctx->status = ENOSPC; } } } void smb_dr_put_int32(smb_dr_ctx_t *ctx, int32_t num) { if (ctx->status == 0) { if (ctx->ptr + sizeof (int32_t) <= ctx->end_ptr) { (void) memcpy(ctx->ptr, &num, sizeof (int32_t)); ctx->ptr += sizeof (int32_t); } else { ctx->status = ENOSPC; } } } void smb_dr_put_uint32(smb_dr_ctx_t *ctx, uint32_t num) { smb_dr_put_int32(ctx, (int32_t)num); } void smb_dr_put_string(smb_dr_ctx_t *ctx, const char *buf) { int len; if (!buf) len = -1; else len = strlen(buf); if (ctx->status == 0) { smb_dr_put_int32(ctx, len); if (len <= 0) return; if (ctx->ptr + len <= ctx->end_ptr) { (void) memcpy(ctx->ptr, buf, len); ctx->ptr += len; } else { ctx->status = ENOSPC; } } } void smb_dr_free_string(char *buf) { if (buf) MEM_FREE("CommonDoor", buf); } int64_t smb_dr_get_int64(smb_dr_ctx_t *ctx) { int64_t num = 0; if (ctx->status == 0) { if (ctx->ptr + sizeof (int64_t) <= ctx->end_ptr) { (void) memcpy(&num, ctx->ptr, sizeof (int64_t)); ctx->ptr += sizeof (int64_t); } else { ctx->status = ENOSPC; } } return (num); } uint64_t smb_dr_get_uint64(smb_dr_ctx_t *ctx) { return ((uint64_t)smb_dr_get_int64(ctx)); } void smb_dr_put_int64(smb_dr_ctx_t *ctx, int64_t num) { if (ctx->status == 0) { if (ctx->ptr + sizeof (int64_t) <= ctx->end_ptr) { (void) memcpy(ctx->ptr, &num, sizeof (int64_t)); ctx->ptr += sizeof (int64_t); } else { ctx->status = ENOSPC; } } } void smb_dr_put_uint64(smb_dr_ctx_t *ctx, uint64_t num) { smb_dr_put_int64(ctx, (int64_t)num); } void smb_dr_put_short(smb_dr_ctx_t *ctx, short num) { if (ctx->status == 0) { if (ctx->ptr + sizeof (short) <= ctx->end_ptr) { (void) memcpy(ctx->ptr, &num, sizeof (short)); ctx->ptr += sizeof (short); } else { ctx->status = ENOSPC; } } } short smb_dr_get_short(smb_dr_ctx_t *ctx) { short num = 0; if (ctx->status == 0) { if (ctx->ptr + sizeof (short) <= ctx->end_ptr) { (void) memcpy(&num, ctx->ptr, sizeof (short)); ctx->ptr += sizeof (short); } else { ctx->status = ENOSPC; } } return (num); } void smb_dr_put_ushort(smb_dr_ctx_t *ctx, unsigned short num) { smb_dr_put_short(ctx, (short)num); } unsigned short smb_dr_get_ushort(smb_dr_ctx_t *ctx) { return ((unsigned short)smb_dr_get_short(ctx)); } void smb_dr_put_word(smb_dr_ctx_t *ctx, WORD num) { smb_dr_put_ushort(ctx, num); } WORD smb_dr_get_word(smb_dr_ctx_t *ctx) { return (smb_dr_get_ushort(ctx)); } void smb_dr_put_BYTE(smb_dr_ctx_t *ctx, BYTE byte) { if (ctx->status == 0) { if (ctx->ptr + sizeof (BYTE) <= ctx->end_ptr) { (void) memcpy(ctx->ptr, &byte, sizeof (BYTE)); ctx->ptr += sizeof (BYTE); } else { ctx->status = ENOSPC; } } } BYTE smb_dr_get_BYTE(smb_dr_ctx_t *ctx) { BYTE byte = 0; if (ctx->status == 0) { if (ctx->ptr + sizeof (BYTE) <= ctx->end_ptr) { (void) memcpy(&byte, ctx->ptr, sizeof (BYTE)); ctx->ptr += sizeof (BYTE); } else { ctx->status = ENOSPC; } } return (byte); } void smb_dr_put_buf(smb_dr_ctx_t *ctx, unsigned char *start, int len) { smb_dr_put_int32(ctx, len); if (ctx->status == 0) { if (ctx->ptr + len <= ctx->end_ptr) { (void) memcpy(ctx->ptr, start, len); ctx->ptr += len; } else { ctx->status = ENOSPC; } } } int smb_dr_get_buf(smb_dr_ctx_t *ctx, unsigned char *buf, int bufsize) { int len = -1; if (!buf) return (-1); len = smb_dr_get_int32(ctx); if (ctx->status == 0) { if (bufsize < len) { ctx->status = ENOSPC; return (-2); } if (ctx->ptr + len <= ctx->end_ptr) { (void) memcpy(buf, ctx->ptr, len); ctx->ptr += len; } else { ctx->status = ENOSPC; return (-3); } } return (len); } void smb_dr_get_share(smb_dr_ctx_t *ctx, smb_share_t *si) { if (ctx->status == 0) { if (smb_dr_get_int32(ctx)) { (void) memcpy(si, ctx->ptr, sizeof (smb_share_t)); ctx->ptr += sizeof (smb_share_t); } else { bzero(si, sizeof (smb_share_t)); } } else { bzero(si, sizeof (smb_share_t)); } } void smb_dr_put_share(smb_dr_ctx_t *ctx, smb_share_t *si) { if (si) { smb_dr_put_int32(ctx, 1); if (ctx->ptr + sizeof (smb_share_t) <= ctx->end_ptr) { (void) memcpy(ctx->ptr, si, sizeof (smb_share_t)); ctx->ptr += sizeof (smb_share_t); } else { ctx->status = ENOSPC; } } else { smb_dr_put_int32(ctx, 0); } } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2019 Nexenta by DDN, Inc. All rights reserved. */ /* * Wrappers for inet address manipulation to do what SMB needs. */ #include #include #include #if !defined(_KERNEL) #include #include #include #include #else /* !_KERNEL */ #include #include /* Don't want the rest of what's in inet/ip.h */ #define inet_ntop _inet_ntop // illumos.org/issues/5980 extern char *inet_ntop(int, const void *, char *, int); extern int inet_pton(int, char *, void *); #endif /* !_KERNEL */ #include const struct in6_addr ipv6addr_any = IN6ADDR_ANY_INIT; boolean_t smb_inet_equal(smb_inaddr_t *ip1, smb_inaddr_t *ip2) { if ((ip1->a_family == AF_INET) && (ip2->a_family == AF_INET) && (ip1->a_ipv4 == ip2->a_ipv4)) return (B_TRUE); if ((ip1->a_family == AF_INET6) && (ip2->a_family == AF_INET6) && (!memcmp(&ip1->a_ipv6, &ip2->a_ipv6, sizeof (in6_addr_t)))) return (B_TRUE); else return (B_FALSE); } boolean_t smb_inet_same_subnet(smb_inaddr_t *ip1, smb_inaddr_t *ip2, uint32_t v4mask) { if ((ip1->a_family == AF_INET) && (ip2->a_family == AF_INET) && ((ip1->a_ipv4 & v4mask) == (ip2->a_ipv4 & v4mask))) return (B_TRUE); if ((ip1->a_family == AF_INET6) && (ip2->a_family == AF_INET6) && (!memcmp(&ip1->a_ipv6, &ip2->a_ipv6, sizeof (in6_addr_t)))) return (B_TRUE); else return (B_FALSE); } boolean_t smb_inet_iszero(smb_inaddr_t *ipaddr) { const void *ipsz = (const void *)&ipv6addr_any; if ((ipaddr->a_family == AF_INET) && (ipaddr->a_ipv4 == 0)) return (B_TRUE); if ((ipaddr->a_family == AF_INET6) && !memcmp(&ipaddr->a_ipv6, ipsz, sizeof (in6_addr_t))) return (B_TRUE); else return (B_FALSE); } const char * smb_inet_ntop(smb_inaddr_t *addr, char *buf, int size) { /* Lint avoidance */ #ifdef _KERNEL int sz = size; #else size_t sz = (size_t)size; #endif return ((char *)inet_ntop(addr->a_family, addr, buf, sz)); } /* * 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) #include #include #else #include #include #include #endif #include #include /* * Maximum recursion depth for the wildcard match functions. * These functions may recurse when processing a '*'. */ #define SMB_MATCH_DEPTH_MAX 32 struct match_priv { int depth; boolean_t ci; }; static int smb_match_private(const char *, const char *, struct match_priv *); static const char smb_wildcards[] = "*?<>\""; /* * Return B_TRUE if pattern contains wildcards */ boolean_t smb_contains_wildcards(const char *pattern) { return (strpbrk(pattern, smb_wildcards) != NULL); } /* * NT-compatible file name match function. [MS-FSA 3.1.4.4] * Returns TRUE if there is a match. */ boolean_t smb_match(const char *p, const char *s, boolean_t ci) { struct match_priv priv; int rc; /* * Optimize common patterns that match everything: * ("*", "<\"*") That second one is the converted * form of "*.*" after smb_convert_wildcards() does * its work on it for an old LM client. Note that a * plain "*.*" never gets this far. */ if (p[0] == '*' && p[1] == '\0') return (B_TRUE); if (p[0] == '<' && p[1] == '\"' && p[2] == '*' && p[3] == '\0') return (B_TRUE); /* * Match string ".." as if "." This is Windows behavior * (not mentioned in MS-FSA) that was determined using * the Samba masktest program. */ if (s[0] == '.' && s[1] == '.' && s[2] == '\0') s++; /* * Optimize simple patterns (no wildcards) */ if (NULL == strpbrk(p, smb_wildcards)) { if (ci) rc = smb_strcasecmp(p, s, 0); else rc = strcmp(p, s); return (rc == 0); } /* * Do real wildcard match. */ priv.depth = 0; priv.ci = ci; rc = smb_match_private(p, s, &priv); return (rc == 1); } /* * Internal file name match function. [MS-FSA 3.1.4.4] * This does the full expression evaluation. * * '*' matches zero of more of any characters. * '?' matches exactly one of any character. * '<' matches any string up through the last dot or EOS. * '>' matches any one char not a dot, dot at EOS, or EOS. * '"' matches a dot, or EOS. * * Returns: * 1 match * 0 no-match * -1 no-match, error (illseq, too many wildcards in pattern, ...) * * Note that both the pattern and the string are in multi-byte form. * * The implementation of this is quite tricky. First note that it * can call itself recursively, though it limits the recursion depth. * Each switch case in the while loop can basically do one of three * things: (a) return "Yes, match", (b) return "not a match", or * continue processing the match pattern. The cases for wildcards * that may match a variable number of characters ('*' and '<') do * recursive calls, looking for a match of the remaining pattern, * starting at the current and later positions in the string. */ static int smb_match_private(const char *pat, const char *str, struct match_priv *priv) { const char *limit; char pc; /* current pattern char */ int rc; uint32_t wcpat, wcstr; /* current wchar in pat, str */ int nbpat, nbstr; /* multi-byte length of it */ if (priv->depth >= SMB_MATCH_DEPTH_MAX) return (-1); /* * Advance over one multi-byte char, used in cases like * '?' or '>' where "match one character" needs to be * interpreted as "match one multi-byte sequence". * * This macro needs to consume the semicolon following * each place it appears, so this is carefully written * as an if/else with a missing semicolon at the end. */ #define ADVANCE(str) \ if ((nbstr = smb_mbtowc(NULL, str, MTS_MB_CHAR_MAX)) < 1) \ return (-1); \ else \ str += nbstr /* no ; */ /* * We move pat forward in each switch case so that the * default case can move it by a whole multi-byte seq. */ while ((pc = *pat) != '\0') { switch (pc) { case '?': /* exactly one of any character */ pat++; if (*str != '\0') { ADVANCE(str); continue; } /* EOS: no-match */ return (0); case '*': /* zero or more of any characters */ pat++; /* Optimize '*' at end of pattern. */ if (*pat == '\0') return (1); /* match */ while (*str != '\0') { priv->depth++; rc = smb_match_private(pat, str, priv); priv->depth--; if (rc != 0) return (rc); /* match */ ADVANCE(str); } continue; case '<': /* any string up through the last dot or EOS */ pat++; if ((limit = strrchr(str, '.')) != NULL) limit++; while (*str != '\0' && str != limit) { priv->depth++; rc = smb_match_private(pat, str, priv); priv->depth--; if (rc != 0) return (rc); /* match */ ADVANCE(str); } continue; case '>': /* anything not a dot, dot at EOS, or EOS */ pat++; if (*str == '.') { if (str[1] == '\0') { /* dot at EOS */ str++; /* ADVANCE over '.' */ continue; } /* dot NOT at EOS: no-match */ return (0); } if (*str != '\0') { /* something not a dot */ ADVANCE(str); continue; } continue; case '\"': /* dot, or EOS */ pat++; if (*str == '.') { str++; /* ADVANCE over '.' */ continue; } if (*str == '\0') { continue; } /* something else: no-match */ return (0); default: /* not a wildcard */ nbpat = smb_mbtowc(&wcpat, pat, MTS_MB_CHAR_MAX); nbstr = smb_mbtowc(&wcstr, str, MTS_MB_CHAR_MAX); /* make sure we advance */ if (nbpat < 1 || nbstr < 1) return (-1); if (wcpat == wcstr) { pat += nbpat; str += nbstr; continue; } if (priv->ci) { wcpat = smb_tolower(wcpat); wcstr = smb_tolower(wcstr); if (wcpat == wcstr) { pat += nbpat; str += nbstr; continue; } } return (0); /* no-match */ } } return (*str == '\0'); } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2019 Nexenta by DDN, Inc. All rights reserved. */ /* * Msgbuf buffer management implementation. The smb_msgbuf interface is * typically used to encode or decode SMB data using sprintf/scanf * style operations. It contains special handling for the SMB header. * It can also be used for general purpose encoding and decoding. */ #include #include #include #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) #include #include #include #include #else #include #include #endif #include #include #include static int buf_decode(smb_msgbuf_t *, char *, va_list ap); static int buf_encode(smb_msgbuf_t *, char *, va_list ap); static void *smb_msgbuf_malloc(smb_msgbuf_t *, size_t); static int smb_msgbuf_chkerc(char *text, int erc); static int msgbuf_get_oem_string(smb_msgbuf_t *, char **, int); static int msgbuf_get_unicode_string(smb_msgbuf_t *, char **, int); static int msgbuf_put_oem_string(smb_msgbuf_t *, char *, int); static int msgbuf_put_unicode_string(smb_msgbuf_t *, char *, int); /* * Returns the offset or number of bytes used within the buffer. */ size_t smb_msgbuf_used(smb_msgbuf_t *mb) { /*LINTED E_PTRDIFF_OVERFLOW*/ return (mb->scan - mb->base); } /* * Returns the actual buffer size. */ size_t smb_msgbuf_size(smb_msgbuf_t *mb) { return (mb->max); } uint8_t * smb_msgbuf_base(smb_msgbuf_t *mb) { return (mb->base); } /* * Ensure that the scan is aligned on a word (16-bit) boundary. */ void smb_msgbuf_word_align(smb_msgbuf_t *mb) { mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 1) & ~1); } /* * Ensure that the scan is aligned on a dword (32-bit) boundary. */ void smb_msgbuf_dword_align(smb_msgbuf_t *mb) { mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 3) & ~3); } /* * Checks whether or not the buffer has space for the amount of data * specified. Returns 1 if there is space, otherwise returns 0. */ int smb_msgbuf_has_space(smb_msgbuf_t *mb, size_t size) { if (size > mb->max || (mb->scan + size) > mb->end) return (0); return (1); } /* * Set flags the smb_msgbuf. */ void smb_msgbuf_fset(smb_msgbuf_t *mb, uint32_t flags) { mb->flags |= flags; } /* * Clear flags the smb_msgbuf. */ void smb_msgbuf_fclear(smb_msgbuf_t *mb, uint32_t flags) { mb->flags &= ~flags; } /* * smb_msgbuf_init * * Initialize a smb_msgbuf_t structure based on the buffer and size * specified. Both scan and base initially point to the beginning * of the buffer and end points to the limit of the buffer. As * data is added scan should be incremented to point to the next * offset at which data will be written. Max and count are set * to the actual buffer size. */ void smb_msgbuf_init(smb_msgbuf_t *mb, uint8_t *buf, size_t size, uint32_t flags) { mb->scan = mb->base = buf; mb->max = mb->count = size; mb->end = &buf[size]; mb->flags = flags; mb->mlist.next = 0; } /* * smb_msgbuf_term * * Destruct a smb_msgbuf_t. Free any memory hanging off the mlist. */ void smb_msgbuf_term(smb_msgbuf_t *mb) { smb_msgbuf_mlist_t *item = mb->mlist.next; smb_msgbuf_mlist_t *tmp; while (item) { tmp = item; item = item->next; #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) free(tmp); #else kmem_free(tmp, tmp->size); #endif } } /* * smb_msgbuf_decode * * Decode a smb_msgbuf buffer as indicated by the format string into * the variable arg list. This is similar to a scanf operation. * * On success, returns the number of bytes decoded. Otherwise * returns a -ve error code. */ int smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...) { int rc; uint8_t *orig_scan; va_list ap; va_start(ap, fmt); orig_scan = mb->scan; rc = buf_decode(mb, fmt, ap); va_end(ap); if (rc != SMB_MSGBUF_SUCCESS) { (void) smb_msgbuf_chkerc("smb_msgbuf_decode", rc); mb->scan = orig_scan; return (rc); } /*LINTED E_PTRDIFF_OVERFLOW*/ return (mb->scan - orig_scan); } /* * buf_decode * * Private decode function, where the real work of decoding the smb_msgbuf * is done. This function should only be called via smb_msgbuf_decode to * ensure correct behaviour and error handling. */ static int buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap) { uint8_t c; uint8_t *bvalp; uint16_t *wvalp; uint32_t *lvalp; uint64_t *llvalp; char **cvalpp; boolean_t repc_specified; int repc; int rc; while ((c = *fmt++) != 0) { repc_specified = B_FALSE; repc = 1; if (c == ' ' || c == '\t') continue; if (c == '(') { while (((c = *fmt++) != 0) && c != ')') ; if (!c) return (SMB_MSGBUF_SUCCESS); continue; } if ('0' <= c && c <= '9') { repc = 0; do { repc = repc * 10 + c - '0'; c = *fmt++; } while ('0' <= c && c <= '9'); repc_specified = B_TRUE; } else if (c == '#') { repc = va_arg(ap, int); c = *fmt++; repc_specified = B_TRUE; } switch (c) { case '.': if (smb_msgbuf_has_space(mb, repc) == 0) return (SMB_MSGBUF_UNDERFLOW); mb->scan += repc; break; case 'c': /* get char */ if (smb_msgbuf_has_space(mb, repc) == 0) return (SMB_MSGBUF_UNDERFLOW); bvalp = va_arg(ap, uint8_t *); bcopy(mb->scan, bvalp, repc); mb->scan += repc; break; case 'b': /* get byte */ if (smb_msgbuf_has_space(mb, repc) == 0) return (SMB_MSGBUF_UNDERFLOW); bvalp = va_arg(ap, uint8_t *); while (repc-- > 0) { *bvalp++ = *mb->scan++; } break; case 'w': /* get word */ rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t)); if (rc == 0) return (SMB_MSGBUF_UNDERFLOW); wvalp = va_arg(ap, uint16_t *); while (repc-- > 0) { *wvalp++ = LE_IN16(mb->scan); mb->scan += sizeof (uint16_t); } break; case 'l': /* get long */ rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t)); if (rc == 0) return (SMB_MSGBUF_UNDERFLOW); lvalp = va_arg(ap, uint32_t *); while (repc-- > 0) { *lvalp++ = LE_IN32(mb->scan); mb->scan += sizeof (int32_t); } break; case 'q': /* get quad */ rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t)); if (rc == 0) return (SMB_MSGBUF_UNDERFLOW); llvalp = va_arg(ap, uint64_t *); while (repc-- > 0) { *llvalp++ = LE_IN64(mb->scan); mb->scan += sizeof (int64_t); } break; case 'u': /* Convert from unicode if flags are set */ if (mb->flags & SMB_MSGBUF_UNICODE) goto unicode_translation; /*FALLTHROUGH*/ case 's': /* get OEM string */ cvalpp = va_arg(ap, char **); if (!repc_specified) repc = 0; rc = msgbuf_get_oem_string(mb, cvalpp, repc); if (rc != 0) return (rc); break; case 'U': /* get UTF-16 string */ unicode_translation: cvalpp = va_arg(ap, char **); if (!repc_specified) repc = 0; rc = msgbuf_get_unicode_string(mb, cvalpp, repc); if (rc != 0) return (rc); break; case 'M': if (smb_msgbuf_has_space(mb, 4) == 0) return (SMB_MSGBUF_UNDERFLOW); if (mb->scan[0] != 0xFF || mb->scan[1] != 'S' || mb->scan[2] != 'M' || mb->scan[3] != 'B') { return (SMB_MSGBUF_INVALID_HEADER); } mb->scan += 4; break; default: return (SMB_MSGBUF_INVALID_FORMAT); } } return (SMB_MSGBUF_SUCCESS); } /* * msgbuf_get_oem_string * * Decode an OEM string, returning its UTF-8 form in strpp, * allocated using smb_msgbuf_malloc (automatically freed). * If max_bytes != 0, consume at most max_bytes of the mb. * See also: mbc_marshal_get_oem_string */ static int msgbuf_get_oem_string(smb_msgbuf_t *mb, char **strpp, int max_bytes) { char *mbs; uint8_t *oembuf = NULL; int oemlen; // len of OEM string, w/o null int datalen; // OtW data len int mbsmax; // max len of ret str int rlen; if (max_bytes == 0) max_bytes = 0xffff; /* * Determine the OtW data length and OEM string length * Note: oemlen is the string length (w/o null) and * datalen is how much we move mb->scan */ datalen = 0; oemlen = 0; for (;;) { if (datalen >= max_bytes) break; /* in-line smb_msgbuf_has_space */ if ((mb->scan + datalen) >= mb->end) return (SMB_MSGBUF_UNDERFLOW); datalen++; if (mb->scan[datalen - 1] == 0) break; oemlen++; } /* * Get datalen bytes into a temp buffer * sized with room to add a null. * Free oembuf in smb_msgbuf_term */ oembuf = smb_msgbuf_malloc(mb, datalen + 1); if (oembuf == NULL) return (SMB_MSGBUF_UNDERFLOW); bcopy(mb->scan, oembuf, datalen); mb->scan += datalen; oembuf[oemlen] = '\0'; /* * Get the buffer we'll return and convert to UTF-8. * May take as much as double the space. */ mbsmax = oemlen * 2; mbs = smb_msgbuf_malloc(mb, mbsmax + 1); if (mbs == NULL) return (SMB_MSGBUF_UNDERFLOW); rlen = smb_oemtombs(mbs, oembuf, mbsmax); if (rlen < 0) return (SMB_MSGBUF_UNDERFLOW); if (rlen > mbsmax) rlen = mbsmax; mbs[rlen] = '\0'; *strpp = mbs; return (0); } /* * msgbuf_get_unicode_string * * Decode a UTF-16 string, returning its UTF-8 form in strpp, * allocated using smb_msgbuf_malloc (automatically freed). * If max_bytes != 0, consume at most max_bytes of the mb. * See also: mbc_marshal_get_unicode_string */ static int msgbuf_get_unicode_string(smb_msgbuf_t *mb, char **strpp, int max_bytes) { char *mbs; uint16_t *wcsbuf = NULL; int wcslen; // wchar count int datalen; // OtW data len size_t mbsmax; // max len of ret str size_t rlen; if (max_bytes == 0) max_bytes = 0xffff; /* * Unicode strings are always word aligned. */ smb_msgbuf_word_align(mb); /* * Determine the OtW data length and (WC) string length * Note: wcslen counts 16-bit wide_chars (w/o null), * and datalen is how much we move mb->scan */ datalen = 0; wcslen = 0; for (;;) { if (datalen >= max_bytes) break; /* in-line smb_msgbuf_has_space */ if ((mb->scan + datalen) >= mb->end) return (SMB_MSGBUF_UNDERFLOW); datalen += 2; if (mb->scan[datalen - 2] == 0 && mb->scan[datalen - 1] == 0) break; wcslen++; } /* * Get datalen bytes into a temp buffer * sized with room to add a (WC) null. * Note: wcsbuf has little-endian order */ wcsbuf = smb_msgbuf_malloc(mb, datalen + 2); if (wcsbuf == NULL) return (SMB_MSGBUF_UNDERFLOW); bcopy(mb->scan, wcsbuf, datalen); mb->scan += datalen; wcsbuf[wcslen] = 0; /* * Get the buffer we'll return and convert to UTF-8. * May take as much 4X number of wide chars. */ mbsmax = wcslen * MTS_MB_CUR_MAX; mbs = smb_msgbuf_malloc(mb, mbsmax + 1); if (mbs == NULL) return (SMB_MSGBUF_UNDERFLOW); rlen = smb_wcstombs(mbs, wcsbuf, mbsmax); if (rlen == (size_t)-1) return (SMB_MSGBUF_UNDERFLOW); if (rlen > mbsmax) rlen = mbsmax; mbs[rlen] = '\0'; *strpp = mbs; return (0); } /* * smb_msgbuf_encode * * Encode a smb_msgbuf buffer as indicated by the format string using * the variable arg list. This is similar to a sprintf operation. * * On success, returns the number of bytes encoded. Otherwise * returns a -ve error code. */ int smb_msgbuf_encode(smb_msgbuf_t *mb, char *fmt, ...) { int rc; uint8_t *orig_scan; va_list ap; va_start(ap, fmt); orig_scan = mb->scan; rc = buf_encode(mb, fmt, ap); va_end(ap); if (rc != SMB_MSGBUF_SUCCESS) { (void) smb_msgbuf_chkerc("smb_msgbuf_encode", rc); mb->scan = orig_scan; return (rc); } /*LINTED E_PTRDIFF_OVERFLOW*/ return (mb->scan - orig_scan); } /* * buf_encode * * Private encode function, where the real work of encoding the smb_msgbuf * is done. This function should only be called via smb_msgbuf_encode to * ensure correct behaviour and error handling. */ static int buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap) { uint8_t cval; uint16_t wval; uint32_t lval; uint64_t llval; uint8_t *bvalp; char *cvalp; uint8_t c; boolean_t repc_specified; int repc; int rc; while ((c = *fmt++) != 0) { repc_specified = B_FALSE; repc = 1; if (c == ' ' || c == '\t') continue; if (c == '(') { while (((c = *fmt++) != 0) && c != ')') ; if (!c) return (SMB_MSGBUF_SUCCESS); continue; } if ('0' <= c && c <= '9') { repc = 0; do { repc = repc * 10 + c - '0'; c = *fmt++; } while ('0' <= c && c <= '9'); repc_specified = B_TRUE; } else if (c == '#') { repc = va_arg(ap, int); c = *fmt++; repc_specified = B_TRUE; } switch (c) { case '.': if (smb_msgbuf_has_space(mb, repc) == 0) return (SMB_MSGBUF_OVERFLOW); while (repc-- > 0) *mb->scan++ = 0; break; case 'c': /* put char */ if (smb_msgbuf_has_space(mb, repc) == 0) return (SMB_MSGBUF_OVERFLOW); bvalp = va_arg(ap, uint8_t *); bcopy(bvalp, mb->scan, repc); mb->scan += repc; break; case 'b': /* put byte */ if (smb_msgbuf_has_space(mb, repc) == 0) return (SMB_MSGBUF_OVERFLOW); while (repc-- > 0) { cval = va_arg(ap, int); *mb->scan++ = cval; } break; case 'w': /* put word */ rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t)); if (rc == 0) return (SMB_MSGBUF_OVERFLOW); while (repc-- > 0) { wval = va_arg(ap, int); LE_OUT16(mb->scan, wval); mb->scan += sizeof (uint16_t); } break; case 'l': /* put long */ rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t)); if (rc == 0) return (SMB_MSGBUF_OVERFLOW); while (repc-- > 0) { lval = va_arg(ap, uint32_t); LE_OUT32(mb->scan, lval); mb->scan += sizeof (int32_t); } break; case 'q': /* put quad */ rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t)); if (rc == 0) return (SMB_MSGBUF_OVERFLOW); while (repc-- > 0) { llval = va_arg(ap, uint64_t); LE_OUT64(mb->scan, llval); mb->scan += sizeof (uint64_t); } break; case 'u': /* conditional unicode */ if (mb->flags & SMB_MSGBUF_UNICODE) goto unicode_translation; /* FALLTHROUGH */ case 's': /* put OEM string */ cvalp = va_arg(ap, char *); if (!repc_specified) repc = 0; rc = msgbuf_put_oem_string(mb, cvalp, repc); if (rc != 0) return (rc); break; case 'U': /* put UTF-16 string */ unicode_translation: cvalp = va_arg(ap, char *); if (!repc_specified) repc = 0; rc = msgbuf_put_unicode_string(mb, cvalp, repc); if (rc != 0) return (rc); break; case 'M': if (smb_msgbuf_has_space(mb, 4) == 0) return (SMB_MSGBUF_OVERFLOW); *mb->scan++ = 0xFF; *mb->scan++ = 'S'; *mb->scan++ = 'M'; *mb->scan++ = 'B'; break; default: return (SMB_MSGBUF_INVALID_FORMAT); } } return (SMB_MSGBUF_SUCCESS); } /* * Marshal a UTF-8 string (str) into mbc, converting to OEM codeset. * Also write a null unless the repc count limits the length we put. * When (repc > 0) the length we marshal must be exactly repc, and * truncate or pad the mb data as necessary. * See also: mbc_marshal_put_oem_string */ static int msgbuf_put_oem_string(smb_msgbuf_t *mb, char *mbs, int repc) { uint8_t *oembuf = NULL; uint8_t *s; int oemlen; int rlen; /* * Compute length of converted OEM string, * NOT including null terminator */ if ((oemlen = smb_sbequiv_strlen(mbs)) == -1) return (SMB_MSGBUF_DATA_ERROR); /* * If repc not specified, put whole string + NULL, * otherwise will truncate or pad as needed. */ if (repc <= 0) { repc = oemlen; if ((mb->flags & SMB_MSGBUF_NOTERM) == 0) repc += sizeof (char); } /* * Convert into a temporary buffer * Free oembuf in smb_msgbuf_term. */ oembuf = smb_msgbuf_malloc(mb, oemlen + 1); if (oembuf == NULL) return (SMB_MSGBUF_UNDERFLOW); rlen = smb_mbstooem(oembuf, mbs, oemlen); if (rlen < 0) return (SMB_MSGBUF_DATA_ERROR); if (rlen > oemlen) rlen = oemlen; oembuf[rlen] = '\0'; /* * Copy the converted string into the message, * truncated or paded as required. */ s = oembuf; while (repc > 0) { if (smb_msgbuf_has_space(mb, 1) == 0) return (SMB_MSGBUF_OVERFLOW); *mb->scan++ = *s; if (*s != '\0') s++; repc--; } return (0); } /* * Marshal a UTF-8 string (str) into mbc, converting to UTF-16. * Also write a null unless the repc count limits the length. * When (repc > 0) the length we marshal must be exactly repc, * and truncate or pad the mb data as necessary. * See also: mbc_marshal_put_unicode_string */ static int msgbuf_put_unicode_string(smb_msgbuf_t *mb, char *mbs, int repc) { smb_wchar_t *wcsbuf = NULL; smb_wchar_t *wp; smb_wchar_t wchar; size_t wcslen, wcsbytes; size_t rlen; /* align to word boundary */ smb_msgbuf_word_align(mb); /* * Compute length of converted UTF-16 string, * NOT including null terminator (in bytes). */ wcsbytes = smb_wcequiv_strlen(mbs); if (wcsbytes == (size_t)-1) return (SMB_MSGBUF_DATA_ERROR); /* * If repc not specified, put whole string + NULL, * otherwise will truncate or pad as needed. */ if (repc <= 0) { repc = (int)wcsbytes; if ((mb->flags & SMB_MSGBUF_NOTERM) == 0) repc += sizeof (smb_wchar_t); } /* * Convert into a temporary buffer * Free wcsbuf in smb_msgbuf_term */ wcslen = wcsbytes / 2; wcsbuf = smb_msgbuf_malloc(mb, wcsbytes + 2); if (wcsbuf == NULL) return (SMB_MSGBUF_UNDERFLOW); rlen = smb_mbstowcs(wcsbuf, mbs, wcslen); if (rlen == (size_t)-1) return (SMB_MSGBUF_DATA_ERROR); if (rlen > wcslen) rlen = wcslen; wcsbuf[rlen] = 0; /* * Copy the converted string into the message, * truncated or paded as required. Preserve * little-endian order while copying. */ wp = wcsbuf; while (repc >= sizeof (smb_wchar_t)) { if (smb_msgbuf_has_space(mb, sizeof (smb_wchar_t)) == 0) return (SMB_MSGBUF_OVERFLOW); wchar = LE_IN16(wp); LE_OUT16(mb->scan, wchar); mb->scan += 2; if (wchar != 0) wp++; repc -= sizeof (smb_wchar_t); } if (repc > 0) { if (smb_msgbuf_has_space(mb, 1) == 0) return (SMB_MSGBUF_OVERFLOW); *mb->scan++ = '\0'; } return (0); } /* * smb_msgbuf_malloc * * Allocate some memory for use with this smb_msgbuf. We increase the * requested size to hold the list pointer and return a pointer * to the area for use by the caller. */ static void * smb_msgbuf_malloc(smb_msgbuf_t *mb, size_t size) { smb_msgbuf_mlist_t *item; size += sizeof (smb_msgbuf_mlist_t); #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) if ((item = malloc(size)) == NULL) return (NULL); #else item = kmem_alloc(size, KM_SLEEP); #endif item->next = mb->mlist.next; item->size = size; mb->mlist.next = item; /* * The caller gets a pointer to the address * immediately after the smb_msgbuf_mlist_t. */ return ((void *)(item + 1)); } /* * smb_msgbuf_chkerc * * Diagnostic function to write an appropriate message to the system log. */ static int smb_msgbuf_chkerc(char *text, int erc) { static struct { int erc; char *name; } etable[] = { { SMB_MSGBUF_SUCCESS, "success" }, { SMB_MSGBUF_UNDERFLOW, "overflow/underflow" }, { SMB_MSGBUF_INVALID_FORMAT, "invalid format" }, { SMB_MSGBUF_INVALID_HEADER, "invalid header" }, { SMB_MSGBUF_DATA_ERROR, "data error" } }; int i; for (i = 0; i < sizeof (etable)/sizeof (etable[0]); ++i) { if (etable[i].erc == erc) { if (text == 0) text = "smb_msgbuf_chkerc"; break; } } return (erc); } /* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* * This module defines generic functions to map Native OS and Native * LanMan names to values. */ #if defined(_KERNEL) || defined(_FAKE_KERNEL) #include #include #else #include #endif #include #include typedef struct smb_native { int sn_value; const char *sn_name; } smb_native_t; /* * smbnative_os_value * * Return the appropriate native OS value for the specified native OS name. * * Example OS values used by Windows: * * Windows 4.0, Windows NT, Windows NT 4.0 * Windows 5.0, Windows 5.1 * Windows 2000, Windows 2000 5.0, Windows 2000 5.1 * Windows 2002 * Windows .NET * Windows Server 2003 * Windows XP * * Windows 2000 server: "Windows 2000 2195" * Windows XP Professional client: "Windows 2002 2543" * Windows XP PDC server: "Windows 5.1" * Windows .Net: "Windows .NET 3621" * Windows .Net: "Windows .NET 3718" * * DAVE (Thursby Software: CIFS for MacOS) uses "MacOS", sometimes with a * version number appended, i.e. "MacOS 8.5.1". We treat DAVE like NT 4.0 * except for the cases that DAVE clients set 'watch tree' flag in notify * change requests. * * Samba reports UNIX as its Native OS, which we can map to NT 4.0. */ int smbnative_os_value(const char *native_os) { static smb_native_t os_table[] = { { NATIVE_OS_WINNT, "Windows NT 4.0" }, { NATIVE_OS_WINNT, "Windows NT" }, { NATIVE_OS_WIN95, "Windows 4.0" }, { NATIVE_OS_WIN2000, "Windows 5.0" }, { NATIVE_OS_WIN2000, "Windows 5.1" }, { NATIVE_OS_WIN2000, "Windows 2000" }, { NATIVE_OS_WIN2000, "Windows 2002" }, { NATIVE_OS_WIN2000, "Windows .NET" }, { NATIVE_OS_WIN2000, "Windows Server" }, { NATIVE_OS_WIN2000, "Windows XP" }, { NATIVE_OS_WINNT, "UNIX" }, { NATIVE_OS_MACOS, "MacOS" } }; int i; int len; const char *name; if (native_os == NULL) return (NATIVE_OS_UNKNOWN); /* * Windows Vista sends an empty native OS string. */ if (*native_os == '\0') return (NATIVE_OS_WIN2000); for (i = 0; i < sizeof (os_table)/sizeof (os_table[0]); ++i) { name = os_table[i].sn_name; len = strlen(name); if (smb_strcasecmp(name, native_os, len) == 0) return (os_table[i].sn_value); } return (NATIVE_OS_UNKNOWN); } /* * smbnative_lm_value * * Return the appropriate native LanMan value for the specified native * LanMan name. There's an alignment problem in some packets from some * clients that means we can miss the first character, so we do an * additional check starting from the second character. * * Example LanMan values: * * NT LAN Manager 4.0 * Windows 4.0 * Windows NT, Windows NT 4.0 * Windows 2000 LAN Manager * Windows 2000, Windows 2000 5.0, Windows 2000 5.1 * Windows 2002, Windows 2002 5.1 * Windows .NET, Windows .NET 5.2 * Windows Server 2003 * Windows XP * NETSMB (Solaris CIFS client) * DAVE (Thursby Software: CIFS for MacOS) * Samba */ int smbnative_lm_value(const char *native_lm) { static smb_native_t lm_table[] = { { NATIVE_LM_NT, "NT LAN Manager 4.0" }, { NATIVE_LM_NT, "Windows NT" }, { NATIVE_LM_NT, "Windows 4.0" }, { NATIVE_LM_NT, "DAVE" } }; int i; int len; const char *name; /* * Windows Vista sends an empty native LM string. */ if (native_lm == NULL || *native_lm == '\0') return (NATIVE_LM_WIN2000); for (i = 0; i < sizeof (lm_table)/sizeof (lm_table[0]); ++i) { name = lm_table[i].sn_name; len = strlen(name); if ((smb_strcasecmp(name, native_lm, len) == 0) || (smb_strcasecmp(&name[1], native_lm, len - 1) == 0)) { return (lm_table[i].sn_value); } } return (NATIVE_LM_WIN2000); } /* * smbnative_pdc_value * * This function is called when libsmbrdr connects to a PDC. * The PDC type is derived from the Native LanMan string. * The PDC value will default to PDC_WIN2000. * * Example strings: * * NT LAN Manager 4.0 * Windows 4.0, Windows NT, Windows NT 4.0 * Windows 2000 LAN Manager * Windows 2000, Windows 2000 5.0, Windows 2000 5.1 * Windows 2002, Windows 2002 5.1 * Windows .NET, Windows .NET 5.2 * Samba * DAVE */ int smbnative_pdc_value(const char *native_lm) { static smb_native_t pdc_table[] = { { PDC_WINNT, "NT LAN Manager 4.0" }, { PDC_WINNT, "Windows NT 4.0" }, { PDC_WINNT, "Windows NT" }, { PDC_WINNT, "Windows 4.0" }, { PDC_WINNT, "DAVE" }, { PDC_SAMBA, "Samba" } }; int i; int len; const char *name; if (native_lm == NULL || *native_lm == '\0') return (PDC_WIN2000); for (i = 0; i < sizeof (pdc_table)/sizeof (pdc_table[0]); ++i) { name = pdc_table[i].sn_name; len = strlen(name); if ((smb_strcasecmp(name, native_lm, len) == 0) || (smb_strcasecmp(&name[1], native_lm, len - 1) == 0)) { return (pdc_table[i].sn_value); } } return (PDC_WIN2000); } /* * Returns the native OS string for the given OS version. * If no match is found the string for Windows 2000 is returned. */ const char * smbnative_os_str(smb_version_t *version) { int i; static smb_native_t osstr_table[] = { { SMB_MAJOR_NT, "Windows NT" }, { SMB_MAJOR_2000, "Windows 2000" }, { SMB_MAJOR_XP, "Windows XP" }, { SMB_MAJOR_2003, "Windows Server 2003" }, { SMB_MAJOR_VISTA, "" }, { SMB_MAJOR_2008, "" }, { SMB_MAJOR_2008R2, "" } }; for (i = 0; i < sizeof (osstr_table)/sizeof (osstr_table[0]); ++i) { if (version->sv_major == osstr_table[i].sn_value) return (osstr_table[i].sn_name); } return (osstr_table[1].sn_name); } /* * Returns the native Lanman string for the given OS version. * If no match is found the string for Windows 2000 is returned. */ const char * smbnative_lm_str(smb_version_t *version) { int i; static smb_native_t lmstr_table[] = { { SMB_MAJOR_NT, "NT LAN Manager 4.0" }, { SMB_MAJOR_2000, "Windows 2000 LAN Manager" }, { SMB_MAJOR_XP, "Windows 2002 5.1" }, { SMB_MAJOR_2003, "Windows Server 2003 5.2" }, { SMB_MAJOR_VISTA, "" }, { SMB_MAJOR_2008, "" }, { SMB_MAJOR_2008R2, "" } }; for (i = 0; i < sizeof (lmstr_table)/sizeof (lmstr_table[0]); ++i) { if (version->sv_major == lmstr_table[i].sn_value) return (lmstr_table[i].sn_name); } return (lmstr_table[1].sn_name); } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #if defined(_KERNEL) || defined(_FAKE_KERNEL) #include #include #else #include #endif #include #include /* * Routines than support name compression. * * The NetBIOS name representation in all NetBIOS packets (for NAME, * SESSION, and DATAGRAM services) is defined in the Domain Name * Service RFC 883[3] as "compressed" name messages. This format is * called "second-level encoding" in the section entitled * "Representation of NetBIOS Names" in the Concepts and Methods * document. * * For ease of description, the first two paragraphs from page 31, * the section titled "Domain name representation and compression", * of RFC 883 are replicated here: * * Domain names messages are expressed in terms of a sequence * of labels. Each label is represented as a one octet length * field followed by that number of octets. Since every domain * name ends with the null label of the root, a compressed * domain name is terminated by a length byte of zero. The * high order two bits of the length field must be zero, and * the remaining six bits of the length field limit the label * to 63 octets or less. * * To simplify implementations, the total length of label * octets and label length octets that make up a domain name is * restricted to 255 octets or less. * * The following is the uncompressed representation of the NetBIOS name * "FRED ", which is the 4 ASCII characters, F, R, E, D, followed by 12 * space characters (0x20). This name has the SCOPE_ID: "NETBIOS.COM" * * EGFCEFEECACACACACACACACACACACACA.NETBIOS.COM * * This uncompressed representation of names is called "first-level * encoding" in the section entitled "Representation of NetBIOS Names" * in the Concepts and Methods document. * * The following is a pictographic representation of the compressed * representation of the previous uncompressed Domain Name * representation. * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x20 | E (0x45) | G (0x47) | F (0x46) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | C (0x43) | E (0x45) | F (0x46) | E (0x45) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | E (0x45) | C (0x43) | A (0x41) | C (0x43) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | A (0x41) | C (0x43) | A (0x41) | C (0x43) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | A (0x41) | C (0x43) | A (0x41) | C (0x43) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | A (0x41) | C (0x43) | A (0x41) | C (0x43) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | A (0x41) | C (0x43) | A (0x41) | C (0x43) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | A (0x41) | C (0x43) | A (0x41) | C (0x43) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | A (0X41) | 0x07 | N (0x4E) | E (0x45) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | T (0x54) | B (0x42) | I (0x49) | O (0x4F) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | S (0x53) | 0x03 | C (0x43) | O (0x4F) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | M (0x4D) | 0x00 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Each section of a domain name is called a label [7 (page 31)]. A * label can be a maximum of 63 bytes. The first byte of a label in * compressed representation is the number of bytes in the label. For * the above example, the first 0x20 is the number of bytes in the * left-most label, EGFCEFEECACACACACACACACACACACACA, of the domain * name. The bytes following the label length count are the characters * of the label. The following labels are in sequence after the first * label, which is the encoded NetBIOS name, until a zero (0x00) length * count. The zero length count represents the root label, which is * always null. * * A label length count is actually a 6-bit field in the label length * field. The most significant 2 bits of the field, bits 7 and 6, are * flags allowing an escape from the above compressed representation. * If bits 7 and 6 are both set (11), the following 14 bits are an * offset pointer into the full message to the actual label string from * another domain name that belongs in this name. This label pointer * allows for a further compression of a domain name in a packet. * * NetBIOS implementations can only use label string pointers in Name * Service packets. They cannot be used in Session or Datagram Service * packets. * * The other two possible values for bits 7 and 6 (01 and 10) of a label * length field are reserved for future use by RFC 883[2 (page 32)]. * * Note that the first octet of a compressed name must contain one of * the following bit patterns. (An "x" indicates a bit whose value may * be either 0 or 1.): * * 00100000 - Netbios name, length must be 32 (decimal) * 11xxxxxx - Label string pointer * 10xxxxxx - Reserved * 01xxxxxx - Reserved */ /* * netbios_first_level_name_encode * * Put test description here. * * Inputs: * char * in -> Name to encode * char * out -> Buffer to encode into. * int length -> # of bytes to encode. * * Returns: * Nothing */ int netbios_first_level_name_encode(unsigned char *name, unsigned char *scope, unsigned char *out, int max_out) { unsigned char ch, len; unsigned char *in; unsigned char *lp; unsigned char *op = out; if (max_out < 0x21) return (-1); in = name; *op++ = 0x20; for (len = 0; len < NETBIOS_NAME_SZ; len++) { ch = *in++; *op++ = 'A' + ((ch >> 4) & 0xF); *op++ = 'A' + ((ch) & 0xF); } max_out -= 0x21; in = scope; len = 0; lp = op++; while (((ch = *in++) != 0) && (max_out-- > 1)) { if (ch == 0) { if ((*lp = len) != 0) *op++ = 0; break; } if (ch == '.') { *lp = len; lp = op++; len = 0; } else { *op++ = ch; len++; } } *lp = len; if (len != 0) *op = 0; /*LINTED E_PTRDIFF_OVERFLOW*/ return (op - out); } /* * smb_first_level_name_decode * * The null terminated string "in" is the name to decode. The output * is placed in the name_entry structure "name". * * The scope field is a series of length designated labels as described * in the "Domain name representation and compression" section of RFC883. * The two high order two bits of the length field must be zero, the * remaining six bits contain the field length. The total length of the * domain name is restricted to 255 octets but note that the trailing * root label and its dot are not printed. When converting the labels, * the length fields are replaced by dots. * * Returns the number of bytes scanned or -1 to indicate an error. */ int netbios_first_level_name_decode(char *in, char *name, char *scope) { unsigned int length; char c1, c2; char *cp; char *out; cp = in; if ((length = *cp++) != 0x20) { return (-1); } out = name; while (length > 0) { c1 = *cp++; c2 = *cp++; if ('A' <= c1 && c1 <= 'P' && 'A' <= c2 && c2 <= 'P') { c1 -= 'A'; c2 -= 'A'; *out++ = (c1 << 4) | (c2); } else { return (-1); /* conversion error */ } length -= 2; } /* * Don't bother decoding the scope. Not supported. */ if ((length = *cp++) != 0) return (-1); scope[0] = '\0'; /*LINTED E_PTRDIFF_OVERFLOW*/ return (cp - in); } /* * smb_netbios_name_isvalid * * This function is provided to be used by session service * which runs in kernel in order to hide name_entry definition. * * It returns the decoded name in the provided buffer as 'out' * if it's not null. * * Returns 0 if decode fails, 1 if it succeeds. */ int netbios_name_isvalid(char *in, char *out) { char name[NETBIOS_NAME_SZ]; char scope[NETBIOS_DOMAIN_NAME_MAX]; if (netbios_first_level_name_decode(in, name, scope) < 0) return (0); if (out) (void) strlcpy(out, name, NETBIOS_NAME_SZ); return (1); } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* * Support for oem <-> unicode translations. */ #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) #include #include #include #include #else #include #endif /* _KERNEL */ #include #include #include /* * cpid The oemcpg_table index for this oempage. * value The conversion values. */ typedef struct oempage { uint32_t cpid; smb_wchar_t *value; } oempage_t; /* * filename The actual filename contains the codepage. * bytesperchar The codepage uses double or single bytes per char. * oempage The oempage is used to convert Unicode characters to * OEM characters. Memory needs to be allocated for * the value field of oempage to store the table. * ucspage The unicode page is used to convert OEM characters * to Unicode characters. Memory needs to be allocated * for the value field of ucspage to store the table. * valid True if the codepage has been initialized. */ typedef struct oem_codepage { char *filename; uint32_t bytesperchar; oempage_t oempage; oempage_t ucspage; boolean_t valid; } oem_codepage_t; static oem_codepage_t oemcpg_table[] = { {"850.cpg", 1, {0, 0}, {0, 0}, 0}, /* Multilingual Latin1 */ {"950.cpg", 2, {1, 0}, {1, 0}, 0}, /* Chinese Traditional */ {"1252.cpg", 1, {2, 0}, {2, 0}, 0}, /* MS Latin1 */ {"949.cpg", 2, {3, 0}, {3, 0}, 0}, /* Korean */ {"936.cpg", 2, {4, 0}, {4, 0}, 0}, /* Chinese Simplified */ {"932.cpg", 2, {5, 0}, {5, 0}, 0}, /* Japanese */ {"852.cpg", 1, {6, 0}, {6, 0}, 0}, /* Multilingual Latin2 */ {"1250.cpg", 1, {7, 0}, {7, 0}, 0}, /* MS Latin2 */ {"1253.cpg", 1, {8, 0}, {8, 0}, 0}, /* MS Greek */ {"737.cpg", 1, {9, 0}, {9, 0}, 0}, /* Greek */ {"1254.cpg", 1, {10, 0}, {10, 0}, 0}, /* MS Turkish */ {"857.cpg", 1, {11, 0}, {11, 0}, 0}, /* Multilingual Latin5 */ {"1251.cpg", 1, {12, 0}, {12, 0}, 0}, /* MS Cyrillic */ {"866.cpg", 1, {13, 0}, {13, 0}, 0}, /* Cyrillic II */ {"1255.cpg", 1, {14, 0}, {14, 0}, 0}, /* MS Hebrew */ {"862.cpg", 1, {15, 0}, {15, 0}, 0}, /* Hebrew */ {"1256.cpg", 1, {16, 0}, {16, 0}, 0}, /* MS Arabic */ {"720.cpg", 1, {17, 0}, {17, 0}, 0} /* Arabic */ }; #define MAX_OEMPAGES (sizeof (oemcpg_table) / sizeof (oemcpg_table[0])) #define MAX_UNICODE_IDX 65536 /* * The default SMB OEM codepage for English is codepage 850. */ const smb_wchar_t oem_codepage_850[256] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 }; /* * The default telnet OEM codepage for English is codepage 1252. */ const smb_wchar_t oem_codepage_1252[256] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, 0x20AC, 0x81, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x160, 0x2039, 0x152, 0x8D, 0x017D, 0x8F, 0x90, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x161, 0x203A, 0x153, 0x9D, 0x017E, 0x178, 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; static oempage_t *oem_get_oempage(uint32_t); static oempage_t *oem_get_ucspage(uint32_t); static void oem_codepage_init(uint32_t); static void oem_codepage_setup(uint32_t); /* * Convert a unicode string to an oem string. * * The conversion will stop at the end of the unicode string * or when (nbytes - 1) oem characters have been stored. * * The number of converted unicode characters is returned, * or 0 on error. */ size_t ucstooem(char *oem, const smb_wchar_t *ucs, size_t nbytes, uint32_t cpid) { oempage_t *ucspage; uint32_t count = 0; smb_wchar_t oemchar; if (ucs == NULL || oem == NULL) return (0); if ((ucspage = oem_get_ucspage(cpid)) == NULL) return (0); while (nbytes != 0 && (oemchar = ucspage->value[*ucs]) != 0) { if (oemchar & 0xff00 && nbytes >= MTS_MB_CHAR_MAX) { *oem++ = oemchar >> 8; *oem++ = (char)oemchar; nbytes -= 2; } else if (nbytes > 1) { *oem++ = (char)oemchar; nbytes--; } else { break; } count++; ucs++; } *oem = '\0'; return (count); } /* * Convert an oem string to a unicode string. * * The conversion will stop at the end of the oem string or * when nwchars - 1 have been converted. * * The number of converted oem chars is returned, or 0 on error. * An oem char may be either 1 or 2 bytes. */ size_t oemtoucs(smb_wchar_t *ucs, const char *oem, size_t nwchars, uint32_t cpid) { oempage_t *oempage; size_t count = nwchars; smb_wchar_t oemchar; if (ucs == NULL || oem == NULL) return (0); if ((oempage = oem_get_oempage(cpid)) == NULL) return (0); while ((oemchar = (smb_wchar_t)*oem++ & 0xff) != 0) { /* * Cannot find one byte oemchar in table. * Must be a lead byte. Try two bytes. */ if ((oempage->value[oemchar] == 0) && (oemchar != 0)) { oemchar = oemchar << 8 | (*oem++ & 0xff); if (oempage->value[oemchar] == 0) { *ucs = 0; break; } } #ifdef _BIG_ENDIAN *ucs = LE_IN16(&oempage->value[oemchar]); #else *ucs = oempage->value[oemchar]; #endif count--; ucs++; } *ucs = 0; return (nwchars - count); } /* * Get a pointer to the oem page for the specific codepage id. */ static oempage_t * oem_get_oempage(uint32_t cpid) { if (cpid >= MAX_OEMPAGES) return (NULL); if (!oemcpg_table[cpid].valid) { oem_codepage_init(cpid); if (!oemcpg_table[cpid].valid) return (NULL); } return (&oemcpg_table[cpid].oempage); } /* * Get a pointer to the ucs page for the specific codepage id. */ static oempage_t * oem_get_ucspage(uint32_t cpid) { if (cpid >= MAX_OEMPAGES) return (NULL); if (!oemcpg_table[cpid].valid) { oem_codepage_init(cpid); if (!oemcpg_table[cpid].valid) return (NULL); } return (&oemcpg_table[cpid].ucspage); } /* * Initialize the oem page in the oem table. */ static void oem_codepage_init(uint32_t cpid) { #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) static mutex_t mutex; (void) mutex_lock(&mutex); oem_codepage_setup(cpid); (void) mutex_unlock(&mutex); #else static kmutex_t mutex; mutex_enter(&mutex); oem_codepage_setup(cpid); mutex_exit(&mutex); #endif /* _KERNEL */ } static void oem_codepage_setup(uint32_t cpid) { const smb_wchar_t *default_oem_cp; oem_codepage_t *oemcpg; uint32_t bytesperchar; uint32_t max_oem_index; int i; switch (cpid) { case OEM_CPG_850: default_oem_cp = oem_codepage_850; break; case OEM_CPG_1252: default_oem_cp = oem_codepage_1252; default: return; } oemcpg = &oemcpg_table[cpid]; if (oemcpg->valid) return; /* * max_oem_index will be 256 or 65536 dependent * on the OEM codepage. */ bytesperchar = oemcpg_table[cpid].bytesperchar; max_oem_index = 1 << (bytesperchar * 8); oemcpg->oempage.value = MEM_ZALLOC("oem", max_oem_index * sizeof (smb_wchar_t)); if (oemcpg->oempage.value == NULL) return; oemcpg->ucspage.value = MEM_ZALLOC("oem", MAX_UNICODE_IDX * sizeof (smb_wchar_t)); if (oemcpg->ucspage.value == NULL) { MEM_FREE("oem", oemcpg->oempage.value); oemcpg->oempage.value = NULL; return; } for (i = 0; i < max_oem_index; i++) { oemcpg->oempage.value[i] = default_oem_cp[i]; oemcpg->ucspage.value[default_oem_cp[i]] = (smb_wchar_t)i; } oemcpg->valid = B_TRUE; } /* * 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2022 Tintri by DDN, Inc. All rights reserved. */ #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) #include #include #include #include #else /* !_KERNEL && !_FAKE_KERNEL */ #include /* Needed for _FAKE_KERNEL */ #include #include #include #endif /* !_KERNEL && !_FAKE_KERNEL */ #include static smb_sid_t *smb_sid_alloc(size_t); /* * smb_sid_isvalid * * Performs a minimal SID validation. */ boolean_t smb_sid_isvalid(smb_sid_t *sid) { if (sid == NULL) return (B_FALSE); return ((sid->sid_revision == NT_SID_REVISION) && (sid->sid_subauthcnt <= NT_SID_SUBAUTH_MAX)); } /* * smb_sid_len * * Returns the number of bytes required to hold the sid. */ int smb_sid_len(smb_sid_t *sid) { if (sid == NULL) return (0); return (sizeof (smb_sid_t) - sizeof (uint32_t) + (sid->sid_subauthcnt * sizeof (uint32_t))); } /* * smb_sid_dup * * Make a duplicate of the specified sid. The memory for the new sid * should be freed by calling smb_sid_free(). * A pointer to the new sid is returned. */ smb_sid_t * smb_sid_dup(smb_sid_t *sid) { smb_sid_t *new_sid; int size; if (sid == NULL) return (NULL); size = smb_sid_len(sid); if ((new_sid = smb_sid_alloc(size)) == NULL) return (NULL); bcopy(sid, new_sid, size); return (new_sid); } /* * smb_sid_splice * * Make a full sid from a domain sid and a relative id (rid). * The memory for the result sid should be freed by calling * smb_sid_free(). A pointer to the new sid is returned. */ smb_sid_t * smb_sid_splice(smb_sid_t *domain_sid, uint32_t rid) { smb_sid_t *sid; int size; if (domain_sid == NULL) return (NULL); size = smb_sid_len(domain_sid); if ((sid = smb_sid_alloc(size + sizeof (rid))) == NULL) return (NULL); bcopy(domain_sid, sid, size); sid->sid_subauth[domain_sid->sid_subauthcnt] = rid; ++sid->sid_subauthcnt; return (sid); } /* * smb_sid_getrid * * Return the Relative Id (RID) of the specified SID. It is the * caller's responsibility to ensure that this is an appropriate SID. * All we do here is return the last sub-authority from the SID. */ int smb_sid_getrid(smb_sid_t *sid, uint32_t *rid) { if (!smb_sid_isvalid(sid) || (rid == NULL) || (sid->sid_subauthcnt == 0)) return (-1); *rid = sid->sid_subauth[sid->sid_subauthcnt - 1]; return (0); } /* * smb_sid_split * * Take a full sid and split it into a domain sid and a relative id (rid). * The domain SID is allocated and a pointer to it will be returned. The * RID value is passed back in 'rid' arg if it's not NULL. The allocated * memory for the domain SID must be freed by caller. */ smb_sid_t * smb_sid_split(smb_sid_t *sid, uint32_t *rid) { smb_sid_t *domsid; int size; if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0)) return (NULL); /* * We will reduce sid_subauthcnt by one, because * the domain SID does not include the RID. */ size = smb_sid_len(sid) - sizeof (uint32_t); if ((domsid = smb_sid_alloc(size)) == NULL) return (NULL); bcopy(sid, domsid, size); domsid->sid_subauthcnt = sid->sid_subauthcnt - 1; if (rid) *rid = sid->sid_subauth[sid->sid_subauthcnt - 1]; return (domsid); } /* * smb_sid_splitstr * * Takes a full sid in string form and split it into a domain sid and a * relative id (rid). * * IMPORTANT: The original sid is modified in place. This function assumes * given SID is in valid string format. */ int smb_sid_splitstr(char *strsid, uint32_t *rid) { char *p; if ((p = strrchr(strsid, '-')) == NULL) return (-1); *p++ = '\0'; if (rid) { #if defined(_KERNEL) || defined(_FAKE_KERNEL) unsigned long sua = 0; (void) ddi_strtoul(p, NULL, 10, &sua); *rid = (uint32_t)sua; #else *rid = strtoul(p, NULL, 10); #endif } return (0); } /* * smb_sid_cmp * * Compare two SIDs and return a boolean result. The checks are ordered * such that components that are more likely to differ are checked * first. For example, after checking that the SIDs contain the same * sid_subauthcnt, we check the sub-authorities in reverse order because * the RID is the most likely differentiator between two SIDs, i.e. * they are probably going to be in the same domain. */ boolean_t smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2) { int i; if (sid1 == NULL || sid2 == NULL) return (B_FALSE); if (sid1->sid_subauthcnt != sid2->sid_subauthcnt || sid1->sid_revision != sid2->sid_revision) return (B_FALSE); for (i = sid1->sid_subauthcnt - 1; i >= 0; --i) if (sid1->sid_subauth[i] != sid2->sid_subauth[i]) return (B_FALSE); if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX)) return (B_FALSE); return (B_TRUE); } /* * smb_sid_indomain * * Check if given SID is in given domain. */ boolean_t smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid) { int i; if (sid == NULL || domain_sid == NULL) return (B_FALSE); if (domain_sid->sid_revision != sid->sid_revision || sid->sid_subauthcnt < domain_sid->sid_subauthcnt) return (B_FALSE); for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i) if (domain_sid->sid_subauth[i] != sid->sid_subauth[i]) return (B_FALSE); if (bcmp(&domain_sid->sid_authority, &sid->sid_authority, NT_SID_AUTH_MAX)) return (B_FALSE); return (B_TRUE); } /* * smb_sid_tostr * * Fill in the passed buffer with the string form of the given * binary sid. */ void smb_sid_tostr(const smb_sid_t *sid, char *strsid) { char *p = strsid; int i; if (sid == NULL || strsid == NULL) return; (void) sprintf(p, "S-%d-", sid->sid_revision); while (*p) p++; for (i = 0; i < NT_SID_AUTH_MAX; ++i) { if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) { (void) sprintf(p, "%d", sid->sid_authority[i]); while (*p) p++; } } for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) { (void) sprintf(p, "-%u", sid->sid_subauth[i]); while (*p) p++; } } /* * smb_sid_fromstr * * Converts a SID in string form to a SID structure. There are lots of * simplifying assumptions in here. The memory for the SID is allocated * as if it was the largest possible SID; the caller is responsible for * freeing the memory when it is no longer required. We assume that the * string starts with "S-1-" and that the authority is held in the last * byte, which should be okay for most situations. It also assumes the * sub-authorities are in decimal format. * * On success, a pointer to a SID is returned. Otherwise a null pointer * is returned. */ #if defined(_KERNEL) || defined(_FAKE_KERNEL) smb_sid_t * smb_sid_fromstr(const char *sidstr) { smb_sid_t *sid; smb_sid_t *retsid = NULL; const char *p; int size; uint8_t i; unsigned long sua; if (sidstr == NULL) return (NULL); if (strncmp(sidstr, "S-1-", 4) != 0) return (NULL); sua = 0; (void) ddi_strtoul(&sidstr[4], (char **)&p, 10, &sua); /* * If ddi_strtoul() did the right thing, *p will point at the first '-' * after the identifier authority. * The IdentifierAuthority can be up to 2^48, but all known ones * currently fit into a uint8_t. * TODO: support IdentifierAuthorities > 255 (those over UINT32_MAX are * hex-formatted). */ if (sua > UINT8_MAX || (*p != '-' && *p != '\0')) return (NULL); size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); sid = kmem_zalloc(size, KM_SLEEP); sid->sid_revision = NT_SID_REVISION; sid->sid_authority[5] = (uint8_t)sua; for (i = 0; i < NT_SID_SUBAUTH_MAX && *p; ++i) { while (*p == '-') ++p; if (*p < '0' || *p > '9') goto out; sua = 0; (void) ddi_strtoul(p, (char **)&p, 10, &sua); if (sua > UINT32_MAX) goto out; sid->sid_subauth[i] = (uint32_t)sua; if (*p != '\0' && *p != '-') goto out; } sid->sid_subauthcnt = i; retsid = smb_sid_dup(sid); out: kmem_free(sid, size); return (retsid); } #else /* _KERNEL */ smb_sid_t * smb_sid_fromstr(const char *sidstr) { smb_sid_t *sid; const char *p; int size; uint8_t i; unsigned long sua; if (sidstr == NULL) return (NULL); if (strncmp(sidstr, "S-1-", 4) != 0) return (NULL); sua = strtoul(&sidstr[4], (char **)&p, 10); /* * If strtoul() did the right thing, *p will point at the first '-' * after the identifier authority. * The IdentifierAuthority can be up to 2^48, but all known ones * currently fit into a uint8_t. * TODO: support IdentifierAuthorities > 255 (those over UINT32_MAX are * hex-formatted). */ if (sua > UINT8_MAX || (*p != '-' && *p != '\0')) return (NULL); size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); if ((sid = calloc(size, 1)) == NULL) return (NULL); sid->sid_revision = NT_SID_REVISION; sid->sid_authority[5] = (uint8_t)sua; for (i = 0; i < NT_SID_SUBAUTH_MAX && *p; ++i) { while (*p == '-') ++p; if (*p < '0' || *p > '9') { free(sid); return (NULL); } sid->sid_subauth[i] = strtoul(p, (char **)&p, 10); if (*p != '\0' && *p != '-') { free(sid); return (NULL); } } sid->sid_subauthcnt = i; return (sid); } #endif /* _KERNEL */ /* * smb_sid_type2str * * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE * provides the context for a SID, i.e. the type of resource to which * it refers. */ char * smb_sid_type2str(uint16_t snu_id) { static char *snu_name[] = { "SidTypeSidPrefix", "SidTypeUser", "SidTypeGroup", "SidTypeDomain", "SidTypeAlias", "SidTypeWellKnownGroup", "SidTypeDeletedAccount", "SidTypeInvalid", "SidTypeUnknown", "SidTypeComputer", "SidTypeLabel" }; if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0])))) return (snu_name[snu_id]); return (snu_name[SidTypeUnknown]); } static smb_sid_t * smb_sid_alloc(size_t size) { smb_sid_t *sid; #if defined(_KERNEL) || defined(_FAKE_KERNEL) sid = kmem_alloc(size, KM_SLEEP); #else sid = malloc(size); #endif return (sid); } void smb_sid_free(smb_sid_t *sid) { #if defined(_KERNEL) || defined(_FAKE_KERNEL) if (sid == NULL) return; kmem_free(sid, smb_sid_len(sid)); #else free(sid); #endif } /* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2017 by Delphix. All rights reserved. */ #if defined(_KERNEL) || defined(_FAKE_KERNEL) #include #include #else #include #include #include #include #endif #include #include #include #include #include #include #define UNICODE_N_ENTRIES (sizeof (a_unicode) / sizeof (a_unicode[0])) /* * Global pointer to the current codepage: defaults to ASCII, * and a flag indicating whether the codepage is Unicode or ASCII. */ static const smb_codepage_t *current_codepage = usascii_codepage; static boolean_t is_unicode = B_FALSE; static smb_codepage_t *unicode_codepage = NULL; static smb_codepage_t *smb_unicode_init(void); /* * strsubst * * Scan a string replacing all occurrences of orgchar with newchar. * Returns a pointer to s, or null of s is null. */ char * strsubst(char *s, char orgchar, char newchar) { char *p = s; if (p == 0) return (0); while (*p) { if (*p == orgchar) *p = newchar; ++p; } return (s); } /* * strcanon * * Normalize a string by reducing all the repeated characters in * buf as defined by class. For example; * * char *buf = strdup("/d1//d2//d3\\\\d4\\\\f1.txt"); * strcanon(buf, "/\\"); * * Would result in buf containing the following string: * * /d1/d2/d3\d4\f1.txt * * This function modifies the contents of buf in place and returns * a pointer to buf. */ char * strcanon(char *buf, const char *class) { char *p = buf; char *q = buf; char *r; while (*p) { *q++ = *p; if ((r = strchr(class, *p)) != 0) { while (*p == *r) ++p; } else ++p; } *q = '\0'; return (buf); } void smb_codepage_init(void) { smb_codepage_t *cp; if (is_unicode) return; if ((cp = smb_unicode_init()) != NULL) { current_codepage = cp; unicode_codepage = cp; is_unicode = B_TRUE; } else { current_codepage = usascii_codepage; is_unicode = B_FALSE; } } void smb_codepage_fini(void) { if (unicode_codepage != NULL) { MEM_FREE("unicode", unicode_codepage); unicode_codepage = NULL; current_codepage = NULL; } } /* * Determine whether or not a character is an uppercase character. * This function operates on the current codepage table. Returns * non-zero if the character is uppercase. Otherwise returns zero. */ int smb_isupper(int c) { uint16_t mask = is_unicode ? 0xffff : 0xff; return (current_codepage[c & mask].ctype & CODEPAGE_ISUPPER); } /* * Determine whether or not a character is an lowercase character. * This function operates on the current codepage table. Returns * non-zero if the character is lowercase. Otherwise returns zero. */ int smb_islower(int c) { uint16_t mask = is_unicode ? 0xffff : 0xff; return (current_codepage[c & mask].ctype & CODEPAGE_ISLOWER); } /* * Convert individual characters to their uppercase equivalent value. * If the specified character is lowercase, the uppercase value will * be returned. Otherwise the original value will be returned. */ uint32_t smb_toupper(uint32_t c) { uint16_t mask = is_unicode ? 0xffff : 0xff; return (current_codepage[c & mask].upper); } /* * Convert individual characters to their lowercase equivalent value. * If the specified character is uppercase, the lowercase value will * be returned. Otherwise the original value will be returned. */ uint32_t smb_tolower(uint32_t c) { uint16_t mask = is_unicode ? 0xffff : 0xff; return (current_codepage[c & mask].lower); } /* * Convert a string to uppercase using the appropriate codepage. The * string is converted in place. A pointer to the string is returned. * There is an assumption here that uppercase and lowercase values * always result encode to the same length. */ char * smb_strupr(char *s) { uint32_t c; char *p = s; while (*p) { if (smb_isascii(*p)) { *p = smb_toupper(*p); p++; } else { if (smb_mbtowc(&c, p, MTS_MB_CHAR_MAX) < 0) return (0); if (c == 0) break; c = smb_toupper(c); p += smb_wctomb(p, c); } } return (s); } /* * Convert a string to lowercase using the appropriate codepage. The * string is converted in place. A pointer to the string is returned. * There is an assumption here that uppercase and lowercase values * always result encode to the same length. */ char * smb_strlwr(char *s) { uint32_t c; char *p = s; while (*p) { if (smb_isascii(*p)) { *p = smb_tolower(*p); p++; } else { if (smb_mbtowc(&c, p, MTS_MB_CHAR_MAX) < 0) return (0); if (c == 0) break; c = smb_tolower(c); p += smb_wctomb(p, c); } } return (s); } /* * Returns 1 if string contains NO uppercase chars 0 otherwise. However, * -1 is returned if "s" is not a valid multi-byte string. */ int smb_isstrlwr(const char *s) { uint32_t c; int n; const char *p = s; while (*p) { if (smb_isascii(*p) && smb_isupper(*p)) return (0); else { if ((n = smb_mbtowc(&c, p, MTS_MB_CHAR_MAX)) < 0) return (-1); if (c == 0) break; if (smb_isupper(c)) return (0); p += n; } } return (1); } /* * Returns 1 if string contains NO lowercase chars 0 otherwise. However, * -1 is returned if "s" is not a valid multi-byte string. */ int smb_isstrupr(const char *s) { uint32_t c; int n; const char *p = s; while (*p) { if (smb_isascii(*p) && smb_islower(*p)) return (0); else { if ((n = smb_mbtowc(&c, p, MTS_MB_CHAR_MAX)) < 0) return (-1); if (c == 0) break; if (smb_islower(c)) return (0); p += n; } } return (1); } /* * Compare the null-terminated strings s1 and s2 and return an integer * greater than, equal to or less than 0 dependent on whether s1 is * lexicographically greater than, equal to or less than s2 after * translation of each character to lowercase. The original strings * are not modified. * * If n is non-zero, at most n bytes are compared. Otherwise, the strings * are compared until a null terminator is encountered. * * Out: 0 if strings are equal * < 0 if first string < second string * > 0 if first string > second string */ int smb_strcasecmp(const char *s1, const char *s2, size_t n) { int err = 0; int rc; rc = u8_strcmp(s1, s2, n, U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err); if (err != 0) return (-1); return (rc); } /* * First build a codepage based on cp_unicode.h. Then build the unicode * codepage from this interim codepage by copying the entries over while * fixing them and filling in the gaps. */ static smb_codepage_t * smb_unicode_init(void) { smb_codepage_t *unicode; uint32_t a = 0; uint32_t b = 0; unicode = MEM_ZALLOC("unicode", sizeof (smb_codepage_t) << 16); if (unicode == NULL) return (NULL); while (b != 0xffff) { /* * If there is a gap in the standard, * fill in the gap with no-case entries. */ if (UNICODE_N_ENTRIES <= a || a_unicode[a].val > b) { unicode[b].ctype = CODEPAGE_ISNONE; unicode[b].upper = (smb_wchar_t)b; unicode[b].lower = (smb_wchar_t)b; b++; continue; } /* * Copy the entry and fixup as required. */ switch (a_unicode[a].ctype) { case CODEPAGE_ISNONE: /* * Replace 0xffff in upper/lower fields with its val. */ unicode[b].ctype = CODEPAGE_ISNONE; unicode[b].upper = (smb_wchar_t)b; unicode[b].lower = (smb_wchar_t)b; break; case CODEPAGE_ISUPPER: /* * Some characters may have case yet not have * case conversion. Treat them as no-case. */ if (a_unicode[a].lower == 0xffff) { unicode[b].ctype = CODEPAGE_ISNONE; unicode[b].upper = (smb_wchar_t)b; unicode[b].lower = (smb_wchar_t)b; } else { unicode[b].ctype = CODEPAGE_ISUPPER; unicode[b].upper = (smb_wchar_t)b; unicode[b].lower = a_unicode[a].lower; } break; case CODEPAGE_ISLOWER: /* * Some characters may have case yet not have * case conversion. Treat them as no-case. */ if (a_unicode[a].upper == 0xffff) { unicode[b].ctype = CODEPAGE_ISNONE; unicode[b].upper = (smb_wchar_t)b; unicode[b].lower = (smb_wchar_t)b; } else { unicode[b].ctype = CODEPAGE_ISLOWER; unicode[b].upper = a_unicode[a].upper; unicode[b].lower = (smb_wchar_t)b; } break; default: MEM_FREE("unicode", unicode); return (NULL); } a++; b++; }; return (unicode); } /* * Parse a UNC path (\\server\share\path) into its components. * Although a standard UNC path starts with two '\', in DFS * all UNC paths start with one '\'. So, this function only * checks for one. * * A valid UNC must at least contain two components i.e. server * and share. The path is parsed to: * * unc_server server or domain name with no leading/trailing '\' * unc_share share name with no leading/trailing '\' * unc_path relative path to the share with no leading/trailing '\' * it is valid for unc_path to be NULL. * * Upon successful return of this function, smb_unc_free() * MUST be called when returned 'unc' is no longer needed. * * Returns 0 on success, otherwise returns an errno code. */ int smb_unc_init(const char *path, smb_unc_t *unc) { char *p; if (path == NULL || unc == NULL || (*path != '\\' && *path != '/')) return (EINVAL); bzero(unc, sizeof (smb_unc_t)); #if defined(_KERNEL) || defined(_FAKE_KERNEL) unc->unc_buf = smb_mem_strdup(path); #else if ((unc->unc_buf = strdup(path)) == NULL) return (ENOMEM); #endif (void) strsubst(unc->unc_buf, '\\', '/'); (void) strcanon(unc->unc_buf, "/"); unc->unc_server = unc->unc_buf + 1; if (*unc->unc_server == '\0') { smb_unc_free(unc); return (EINVAL); } if ((p = strchr(unc->unc_server, '/')) == NULL) { smb_unc_free(unc); return (EINVAL); } *p++ = '\0'; unc->unc_share = p; if (*unc->unc_share == '\0') { smb_unc_free(unc); return (EINVAL); } unc->unc_path = strchr(unc->unc_share, '/'); if ((p = unc->unc_path) == NULL) return (0); unc->unc_path++; *p = '\0'; /* remove the last '/' if any */ if ((p = strchr(unc->unc_path, '\0')) != NULL) { if (*(--p) == '/') *p = '\0'; } return (0); } void smb_unc_free(smb_unc_t *unc) { if (unc == NULL) return; #if defined(_KERNEL) || defined(_FAKE_KERNEL) smb_mem_free(unc->unc_buf); #else free(unc->unc_buf); #endif unc->unc_buf = NULL; } /* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * NT Token library (kernel/user) */ #if defined(_KERNEL) || defined(_FAKE_KERNEL) #include #include #include #else /* _KERNEL */ #include #include #include #endif /* _KERNEL */ #include #include #include /* * smb_token_query_privilege * * Find out if the specified privilege is enable in the given * access token. */ int smb_token_query_privilege(smb_token_t *token, int priv_id) { smb_privset_t *privset; int i; if ((token == NULL) || (token->tkn_privileges == NULL)) return (0); privset = token->tkn_privileges; for (i = 0; privset->priv_cnt; i++) { if (privset->priv[i].luid.lo_part == priv_id) { if (privset->priv[i].attrs == SE_PRIVILEGE_ENABLED) return (1); else return (0); } } return (0); } /* * Basic sanity check on a token. */ boolean_t smb_token_valid(smb_token_t *token) { if (token == NULL) return (B_FALSE); if ((token->tkn_user.i_sid == NULL) || (token->tkn_owner.i_sid == NULL) || (token->tkn_primary_grp.i_sid == NULL) || (token->tkn_account_name == NULL) || (token->tkn_domain_name == NULL) || (token->tkn_posix_grps == NULL)) return (B_FALSE); if ((token->tkn_win_grps.i_cnt != 0) && (token->tkn_win_grps.i_ids == NULL)) return (B_FALSE); return (B_TRUE); } #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) /* * Encode: structure -> flat buffer (buffer size) * Pre-condition: obj is non-null. */ uint8_t * smb_token_encode(smb_token_t *obj, uint32_t *len) { uint8_t *buf; XDR xdrs; if (!obj) { syslog(LOG_ERR, "smb_token_encode: invalid parameter"); return (NULL); } *len = xdr_sizeof(smb_token_xdr, obj); buf = (uint8_t *)malloc(*len); if (!buf) { syslog(LOG_ERR, "smb_token_encode: %m"); return (NULL); } xdrmem_create(&xdrs, (const caddr_t)buf, *len, XDR_ENCODE); if (!smb_token_xdr(&xdrs, obj)) { syslog(LOG_ERR, "smb_token_encode: XDR encode error"); *len = 0; free(buf); buf = NULL; } xdr_destroy(&xdrs); return (buf); } /* * Decode: flat buffer -> structure */ smb_logon_t * smb_logon_decode(uint8_t *buf, uint32_t len) { smb_logon_t *obj; XDR xdrs; xdrmem_create(&xdrs, (const caddr_t)buf, len, XDR_DECODE); if ((obj = malloc(sizeof (smb_logon_t))) == NULL) { syslog(LOG_ERR, "smb_logon_decode: %m"); xdr_destroy(&xdrs); return (NULL); } bzero(obj, sizeof (smb_logon_t)); if (!smb_logon_xdr(&xdrs, obj)) { syslog(LOG_ERR, "smb_logon_decode: XDR decode error"); free(obj); obj = NULL; } xdr_destroy(&xdrs); return (obj); } void smb_logon_free(smb_logon_t *obj) { xdr_free(smb_logon_xdr, (char *)obj); free(obj); } #endif /* _KERNEL */ /* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* * This file was originally generated using rpcgen. */ #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) #include #endif /* !_KERNEL */ #include #include #include #include static bool_t smb_privset_xdr(XDR *, smb_privset_t *); static bool_t smb_sid_xdr(XDR *, smb_sid_t *); static bool_t smb_privset_helper_xdr(XDR *xdrs, char **privs) { uint32_t pos, len; uint32_t cnt; bool_t rc; smb_privset_t *p; if (xdrs->x_op == XDR_DECODE) { pos = xdr_getpos(xdrs); if (!xdr_bool(xdrs, &rc)) return (FALSE); if (!xdr_uint32_t(xdrs, &cnt)) return (FALSE); rc = xdr_setpos(xdrs, pos); if (rc == FALSE) return (FALSE); } else { if (*privs == NULL) return (FALSE); p = (smb_privset_t *)(uintptr_t)*privs; cnt = p->priv_cnt; } len = sizeof (smb_privset_t) - sizeof (smb_luid_attrs_t) + (cnt * sizeof (smb_luid_attrs_t)); if (!xdr_pointer(xdrs, privs, len, (xdrproc_t)smb_privset_xdr)) return (FALSE); return (TRUE); } static bool_t smb_id_xdr(XDR *xdrs, smb_id_t *objp) { uint8_t len; if ((xdrs->x_op == XDR_ENCODE) || (xdrs->x_op == XDR_FREE)) len = smb_sid_len(objp->i_sid); if (!xdr_uint32_t(xdrs, &objp->i_attrs)) return (FALSE); if (!xdr_uint8_t(xdrs, &len)) return (FALSE); if (!xdr_pointer(xdrs, (char **)&objp->i_sid, len, (xdrproc_t)smb_sid_xdr)) return (FALSE); if (!xdr_uint32_t(xdrs, (uint32_t *)&objp->i_id)) return (FALSE); return (TRUE); } static bool_t smb_ids_xdr(XDR *xdrs, smb_ids_t *objp) { if (!xdr_array(xdrs, (char **)&objp->i_ids, (uint32_t *)&objp->i_cnt, ~0, sizeof (smb_id_t), (xdrproc_t)smb_id_xdr)) return (FALSE); return (TRUE); } static bool_t smb_posix_grps_xdr(XDR *xdrs, smb_posix_grps_t *objp) { if (!xdr_uint32_t(xdrs, &objp->pg_ngrps)) return (FALSE); if (!xdr_vector(xdrs, (char *)objp->pg_grps, objp->pg_ngrps, sizeof (uint32_t), (xdrproc_t)xdr_uint32_t)) return (FALSE); return (TRUE); } static bool_t smb_posix_grps_helper_xdr(XDR *xdrs, char **identity) { uint32_t pos, len; uint32_t cnt; bool_t rc; if (xdrs->x_op == XDR_DECODE) { pos = xdr_getpos(xdrs); if (!xdr_bool(xdrs, &rc)) return (FALSE); if (!xdr_uint32_t(xdrs, &cnt)) return (FALSE); rc = xdr_setpos(xdrs, pos); if (rc == FALSE) return (FALSE); } else { if (*identity == NULL) return (FALSE); cnt = ((smb_posix_grps_t *)(uintptr_t)*identity)->pg_ngrps; } len = SMB_POSIX_GRPS_SIZE(cnt); if (!xdr_pointer(xdrs, identity, len, (xdrproc_t)smb_posix_grps_xdr)) return (FALSE); return (TRUE); } bool_t smb_logon_xdr(XDR *xdrs, smb_logon_t *objp) { if (!xdr_uint16_t(xdrs, &objp->lg_level)) return (FALSE); if (!xdr_string(xdrs, &objp->lg_username, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->lg_domain, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->lg_e_username, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->lg_e_domain, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->lg_workstation, ~0)) return (FALSE); if (!smb_inaddr_xdr(xdrs, &objp->lg_clnt_ipaddr)) return (FALSE); if (!smb_inaddr_xdr(xdrs, &objp->lg_local_ipaddr)) return (FALSE); if (!xdr_uint16_t(xdrs, &objp->lg_local_port)) return (FALSE); if (!smb_buf32_xdr(xdrs, &objp->lg_challenge_key)) return (FALSE); if (!smb_buf32_xdr(xdrs, &objp->lg_nt_password)) return (FALSE); if (!smb_buf32_xdr(xdrs, &objp->lg_lm_password)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->lg_ntlm_flags)) return (FALSE); if (!xdr_int(xdrs, &objp->lg_native_os)) return (FALSE); if (!xdr_int(xdrs, &objp->lg_native_lm)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->lg_flags)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->lg_logon_id)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->lg_domain_type)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->lg_secmode)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->lg_status)) return (FALSE); return (TRUE); } static bool_t smb_sid_xdr(XDR *xdrs, smb_sid_t *objp) { if (!xdr_uint8_t(xdrs, &objp->sid_revision)) return (FALSE); if (!xdr_uint8_t(xdrs, &objp->sid_subauthcnt)) return (FALSE); if (!xdr_vector(xdrs, (char *)objp->sid_authority, NT_SID_AUTH_MAX, sizeof (uint8_t), (xdrproc_t)xdr_uint8_t)) return (FALSE); if (!xdr_vector(xdrs, (char *)objp->sid_subauth, objp->sid_subauthcnt, sizeof (uint32_t), (xdrproc_t)xdr_uint32_t)) return (FALSE); return (TRUE); } static bool_t smb_luid_xdr(XDR *xdrs, smb_luid_t *objp) { if (!xdr_uint32_t(xdrs, &objp->lo_part)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->hi_part)) return (FALSE); return (TRUE); } static bool_t smb_luid_attrs_xdr(XDR *xdrs, smb_luid_attrs_t *objp) { if (!smb_luid_xdr(xdrs, &objp->luid)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->attrs)) return (FALSE); return (TRUE); } static bool_t smb_privset_xdr(XDR *xdrs, smb_privset_t *objp) { if (!xdr_uint32_t(xdrs, &objp->priv_cnt)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->control)) return (FALSE); if (!xdr_vector(xdrs, (char *)objp->priv, objp->priv_cnt, sizeof (smb_luid_attrs_t), (xdrproc_t)smb_luid_attrs_xdr)) return (FALSE); return (TRUE); } bool_t smb_token_xdr(XDR *xdrs, smb_token_t *objp) { if (!smb_id_xdr(xdrs, &objp->tkn_user)) return (FALSE); if (!smb_id_xdr(xdrs, &objp->tkn_owner)) return (FALSE); if (!smb_id_xdr(xdrs, &objp->tkn_primary_grp)) return (FALSE); if (!smb_ids_xdr(xdrs, &objp->tkn_win_grps)) return (FALSE); if (!smb_privset_helper_xdr(xdrs, (char **)&objp->tkn_privileges)) return (FALSE); if (!xdr_string(xdrs, &objp->tkn_account_name, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->tkn_domain_name, ~0)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->tkn_flags)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->tkn_audit_sid)) return (FALSE); if (!smb_buf32_xdr(xdrs, &objp->tkn_ssnkey)) return (FALSE); if (!smb_posix_grps_helper_xdr(xdrs, (char **)&objp->tkn_posix_grps)) return (FALSE); return (TRUE); } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * Multibyte/wide-char conversion routines. SMB uses UTF-16 on the wire * (smb_wchar_t) and we use UTF-8 internally (our multi-byte, or mbs). */ #if defined(_KERNEL) || defined(_FAKE_KERNEL) #include #include #else /* _KERNEL || _FAKE_KERNEL */ #include #include #include #include #include #endif /* _KERNEL || _FAKE_KERNEL */ #include #include /* * mbstowcs * * The mbstowcs() function converts a multibyte character string * mbstring into a wide character string wcstring. No more than * nwchars wide characters are stored. A terminating null wide * character is appended if there is room. * * Returns the number of wide characters converted, not counting * any terminating null wide character. Returns -1 if an invalid * multibyte character is encountered. */ size_t smb_mbstowcs(smb_wchar_t *wcs, const char *mbs, size_t nwchars) { size_t mbslen, wcslen; int err; /* NULL or empty input is allowed. */ if (mbs == NULL || *mbs == '\0') { if (wcs != NULL && nwchars > 0) *wcs = 0; return (0); } /* * Traditional mbstowcs(3C) allows wcs==NULL to get the length. * SMB never calls it that way, but let's future-proof. */ if (wcs == NULL) { return ((size_t)-1); } mbslen = strlen(mbs); wcslen = nwchars; err = uconv_u8tou16((const uchar_t *)mbs, &mbslen, wcs, &wcslen, UCONV_OUT_LITTLE_ENDIAN); if (err != 0) return ((size_t)-1); if (wcslen < nwchars) wcs[wcslen] = 0; return (wcslen); } /* * mbtowc * * The mbtowc() function converts a multibyte character mbchar into * a wide character and stores the result in the object pointed to * by wcharp. Up to nbytes bytes are examined. * * If mbchar is NULL, mbtowc() returns zero to indicate that shift * states are not supported. Shift states are used to switch between * representation modes using reserved bytes to signal shifting * without them being interpreted as characters. If mbchar is null * mbtowc should return non-zero if the current locale requires shift * states. Otherwise it should be return 0. * * If mbchar is non-null, returns the number of bytes processed in * mbchar. If mbchar is null, convert the null (wcharp=0) but * return length zero. If mbchar is invalid, returns -1. */ int /*ARGSUSED*/ smb_mbtowc(uint32_t *wcharp, const char *mbchar, size_t nbytes) { uint32_t wide_char; int count, err; size_t mblen; size_t wclen; if (mbchar == NULL) return (0); /* no shift states */ /* * How many bytes in this symbol? */ count = u8_validate((char *)mbchar, nbytes, NULL, 0, &err); if (count < 0) return (-1); mblen = count; wclen = 1; err = uconv_u8tou32((const uchar_t *)mbchar, &mblen, &wide_char, &wclen, UCONV_OUT_SYSTEM_ENDIAN); if (err != 0) return (-1); if (wclen == 0) { wide_char = 0; count = 0; } if (wcharp) *wcharp = wide_char; return (count); } /* * wctomb * * The wctomb() function converts a wide character wchar into a multibyte * character and stores the result in mbchar. The object pointed to by * mbchar must be large enough to accommodate the multibyte character. * * Returns the numberof bytes written to mbchar. * Note: handles null like any 1-byte char. */ int smb_wctomb(char *mbchar, uint32_t wchar) { char junk[MTS_MB_CUR_MAX+1]; size_t mblen; size_t wclen; int err; if (mbchar == NULL) mbchar = junk; mblen = MTS_MB_CUR_MAX; wclen = 1; err = uconv_u32tou8(&wchar, &wclen, (uchar_t *)mbchar, &mblen, UCONV_IN_SYSTEM_ENDIAN | UCONV_IGNORE_NULL); if (err != 0) return (-1); return ((int)mblen); } /* * wcstombs * * The wcstombs() function converts a wide character string wcstring * into a multibyte character string mbstring. Up to nbytes bytes are * stored in mbstring. Partial multibyte characters at the end of the * string are not stored. The multibyte character string is null * terminated if there is room. * * Returns the number of bytes converted, not counting the terminating * null byte. Returns -1 if an invalid WC sequence is encountered. */ size_t smb_wcstombs(char *mbs, const smb_wchar_t *wcs, size_t nbytes) { size_t mbslen, wcslen; int err; /* NULL or empty input is allowed. */ if (wcs == NULL || *wcs == 0) { if (mbs != NULL && nbytes > 0) *mbs = '\0'; return (0); } /* * Traditional wcstombs(3C) allows mbs==NULL to get the length. * SMB never calls it that way, but let's future-proof. */ if (mbs == NULL) { return ((size_t)-1); } /* * Compute wcslen */ wcslen = 0; while (wcs[wcslen] != 0) wcslen++; mbslen = nbytes; err = uconv_u16tou8(wcs, &wcslen, (uchar_t *)mbs, &mbslen, UCONV_IN_LITTLE_ENDIAN); if (err != 0) return ((size_t)-1); if (mbslen < nbytes) mbs[mbslen] = '\0'; return (mbslen); } /* * Returns the number of bytes that would be written if the multi- * byte string mbs was converted to a wide character string, not * counting the terminating null wide character. */ size_t smb_wcequiv_strlen(const char *mbs) { uint32_t wide_char; size_t bytes; size_t len = 0; while (*mbs) { bytes = smb_mbtowc(&wide_char, mbs, MTS_MB_CHAR_MAX); if (bytes == ((size_t)-1)) return ((size_t)-1); mbs += bytes; len += sizeof (smb_wchar_t); if (bytes > 3) { /* * Extended unicode, so TWO smb_wchar_t */ len += sizeof (smb_wchar_t); } } return (len); } /* * Returns the number of bytes that would be written if the multi- * byte string mbs was converted to an OEM character string, * (smb_mbstooem) not counting the terminating null character. */ size_t smb_sbequiv_strlen(const char *mbs) { size_t nbytes; size_t len = 0; while (*mbs) { nbytes = smb_mbtowc(NULL, mbs, MTS_MB_CHAR_MAX); if (nbytes == ((size_t)-1)) return ((size_t)-1); if (nbytes == 0) break; if (nbytes == 1) { /* ASCII */ len++; } else if (nbytes < 8) { /* Compute OEM length */ char mbsbuf[8]; uint8_t oembuf[8]; int oemlen; (void) strlcpy(mbsbuf, mbs, nbytes+1); oemlen = smb_mbstooem(oembuf, mbsbuf, 8); if (oemlen < 0) return ((size_t)-1); len += oemlen; } else { return ((size_t)-1); } mbs += nbytes; } return (len); } /* * Convert OEM strings to/from internal (UTF-8) form. * * We rarely encounter these anymore because all modern * SMB clients use Unicode (UTF-16). The few cases where * this IS still called are normally using ASCII, i.e. * tag names etc. so short-cut those cases. If we get * something non-ASCII we have to call iconv. * * If we were to really support OEM code pages, we would * need to have a way to set the OEM code page from some * configuration value. For now it's always CP850. * See also ./smb_oem.c */ static char smb_oem_codepage[32] = "CP850"; /* * smb_oemtombs * * Convert a null terminated OEM string 'string' to a UTF-8 string * no longer than max_mblen (null terminated if space). * * If the input string contains invalid OEM characters, a value * of -1 will be returned. Otherwise returns the length of 'mbs', * excluding the terminating null character. * * If either mbstring or string is a null pointer, -1 is returned. */ int smb_oemtombs(char *mbs, const uint8_t *oems, int max_mblen) { uchar_t *p; int oemlen; int rlen; boolean_t need_iconv = B_FALSE; if (mbs == NULL || oems == NULL) return (-1); /* * Check if the oems is all ASCII (and get the length * while we're at it) so we know if we need to iconv. * We usually can avoid the iconv calls. */ oemlen = 0; p = (uchar_t *)oems; while (*p != '\0') { oemlen++; if (*p & 0x80) need_iconv = B_TRUE; p++; } if (need_iconv) { int rc; char *obuf = mbs; size_t olen = max_mblen; size_t ilen = oemlen; #if defined(_KERNEL) || defined(_FAKE_KERNEL) char *ibuf = (char *)oems; kiconv_t ic; int err; ic = kiconv_open("UTF-8", smb_oem_codepage); if (ic == (kiconv_t)-1) goto just_copy; rc = kiconv(ic, &ibuf, &ilen, &obuf, &olen, &err); (void) kiconv_close(ic); #else /* _KERNEL || _FAKE_KERNEL */ const char *ibuf = (char *)oems; iconv_t ic; ic = iconv_open("UTF-8", smb_oem_codepage); if (ic == (iconv_t)-1) goto just_copy; rc = iconv(ic, &ibuf, &ilen, &obuf, &olen); (void) iconv_close(ic); #endif /* _KERNEL || _FAKE_KERNEL */ if (rc < 0) return (-1); /* Return val. is output bytes. */ rlen = (max_mblen - olen); } else { just_copy: rlen = oemlen; if (rlen > max_mblen) rlen = max_mblen; bcopy(oems, mbs, rlen); } if (rlen < max_mblen) mbs[rlen] = '\0'; return (rlen); } /* * smb_mbstooem * * Convert a null terminated multi-byte string 'mbs' to an OEM string * no longer than max_oemlen (null terminated if space). * * If the input string contains invalid multi-byte characters, a value * of -1 will be returned. Otherwise returns the length of 'oems', * excluding the terminating null character. * * If either mbstring or string is a null pointer, -1 is returned. */ int smb_mbstooem(uint8_t *oems, const char *mbs, int max_oemlen) { uchar_t *p; int mbslen; int rlen; boolean_t need_iconv = B_FALSE; if (oems == NULL || mbs == NULL) return (-1); /* * Check if the mbs is all ASCII (and get the length * while we're at it) so we know if we need to iconv. * We usually can avoid the iconv calls. */ mbslen = 0; p = (uchar_t *)mbs; while (*p != '\0') { mbslen++; if (*p & 0x80) need_iconv = B_TRUE; p++; } if (need_iconv) { int rc; char *obuf = (char *)oems; size_t olen = max_oemlen; size_t ilen = mbslen; #if defined(_KERNEL) || defined(_FAKE_KERNEL) char *ibuf = (char *)mbs; kiconv_t ic; int err; ic = kiconv_open(smb_oem_codepage, "UTF-8"); if (ic == (kiconv_t)-1) goto just_copy; rc = kiconv(ic, &ibuf, &ilen, &obuf, &olen, &err); (void) kiconv_close(ic); #else /* _KERNEL || _FAKE_KERNEL */ const char *ibuf = mbs; iconv_t ic; ic = iconv_open(smb_oem_codepage, "UTF-8"); if (ic == (iconv_t)-1) goto just_copy; rc = iconv(ic, &ibuf, &ilen, &obuf, &olen); (void) iconv_close(ic); #endif /* _KERNEL || _FAKE_KERNEL */ if (rc < 0) return (-1); /* Return val. is output bytes. */ rlen = (max_oemlen - olen); } else { just_copy: rlen = mbslen; if (rlen > max_oemlen) rlen = max_oemlen; bcopy(mbs, oems, rlen); } if (rlen < max_oemlen) oems[rlen] = '\0'; return (rlen); } /* * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright 2022 RackTop Systems, Inc. */ #include #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) #include #include #include #endif /* _KERNEL */ #include #include #include #include #define SMB_XDRMAX32_SZ 0xFFFFFFFF bool_t smb_list_xdr(XDR *, list_t *, const size_t, const size_t, const xdrproc_t); bool_t smb_buf32_xdr(XDR *xdrs, smb_buf32_t *objp) { uint_t maxsize = SMB_XDRMAX32_SZ; uint_t size; if (xdrs->x_op != XDR_DECODE) maxsize = size = (uint_t)objp->len; if (xdr_bytes(xdrs, (char **)&objp->val, &size, maxsize)) { if (xdrs->x_op == XDR_DECODE) objp->len = (uint32_t)size; return (TRUE); } return (FALSE); } /* * When decoding into a string, ensure that objp->buf is NULL or * is pointing at a buffer large enough to receive the string. * Don't leave it as an uninitialized pointer. * * If objp->buf is NULL, xdr_string will allocate memory for the * string. Otherwise it will copy into the available buffer. */ bool_t smb_string_xdr(XDR *xdrs, smb_string_t *objp) { if (!xdr_string(xdrs, &objp->buf, ~0)) return (FALSE); return (TRUE); } const char * smb_doorhdr_opname(uint32_t op) { struct { uint32_t op; const char *name; } ops[] = { { SMB_DR_NULL, "null" }, { SMB_DR_ASYNC_RESPONSE, "async_response" }, { SMB_DR_USER_AUTH_LOGON, "user_auth_logon" }, { SMB_DR_USER_NONAUTH_LOGON, "user_nonauth_logon" }, { SMB_DR_USER_AUTH_LOGOFF, "user_auth_logoff" }, { SMB_DR_LOOKUP_SID, "lookup_sid" }, { SMB_DR_LOOKUP_NAME, "lookup_name" }, { SMB_DR_JOIN, "join" }, { SMB_DR_GET_DCINFO, "get_dcinfo" }, { SMB_DR_VSS_GET_COUNT, "vss_get_count" }, { SMB_DR_VSS_GET_SNAPSHOTS, "vss_get_snapshots" }, { SMB_DR_VSS_MAP_GMTTOKEN, "vss_map_gmttoken" }, { SMB_DR_ADS_FIND_HOST, "ads_find_host" }, { SMB_DR_QUOTA_QUERY, "quota_query" }, { SMB_DR_QUOTA_SET, "quota_set" }, { SMB_DR_DFS_GET_REFERRALS, "dfs_get_referrals" }, { SMB_DR_SHR_HOSTACCESS, "share_hostaccess" }, { SMB_DR_SHR_EXEC, "share_exec" }, { SMB_DR_NOTIFY_DC_CHANGED, "notify_dc_changed" } }; int i; for (i = 0; i < (sizeof (ops) / sizeof (ops[0])); ++i) { if (ops[i].op == op) return (ops[i].name); } return ("unknown"); } /* * Encode a door header structure into an XDR buffer. */ int smb_doorhdr_encode(smb_doorhdr_t *hdr, uint8_t *buf, uint32_t buflen) { XDR xdrs; int rc = 0; xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_ENCODE); if (!smb_doorhdr_xdr(&xdrs, hdr)) rc = -1; xdr_destroy(&xdrs); return (rc); } /* * Decode an XDR buffer into a door header structure. */ int smb_doorhdr_decode(smb_doorhdr_t *hdr, uint8_t *buf, uint32_t buflen) { XDR xdrs; int rc = 0; bzero(hdr, sizeof (smb_doorhdr_t)); xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_DECODE); if (!smb_doorhdr_xdr(&xdrs, hdr)) rc = -1; xdr_destroy(&xdrs); return (rc); } bool_t smb_doorhdr_xdr(XDR *xdrs, smb_doorhdr_t *objp) { if (!xdr_uint32_t(xdrs, &objp->dh_magic)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->dh_flags)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->dh_fid)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->dh_op)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->dh_txid)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->dh_datalen)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->dh_resid)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->dh_door_rc)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->dh_status)) return (FALSE); return (TRUE); } /* * Encode an smb_netuserinfo_t into a buffer. */ int smb_netuserinfo_encode(smb_netuserinfo_t *info, uint8_t *buf, uint32_t buflen, uint_t *nbytes) { XDR xdrs; int rc = 0; xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_ENCODE); if (!smb_netuserinfo_xdr(&xdrs, info)) rc = -1; if (nbytes != NULL) *nbytes = xdr_getpos(&xdrs); xdr_destroy(&xdrs); return (rc); } /* * Decode an XDR buffer into an smb_netuserinfo_t. */ int smb_netuserinfo_decode(smb_netuserinfo_t *info, uint8_t *buf, uint32_t buflen, uint_t *nbytes) { XDR xdrs; int rc = 0; xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_DECODE); bzero(info, sizeof (smb_netuserinfo_t)); if (!smb_netuserinfo_xdr(&xdrs, info)) rc = -1; if (nbytes != NULL) *nbytes = xdr_getpos(&xdrs); xdr_destroy(&xdrs); return (rc); } bool_t smb_inaddr_xdr(XDR *xdrs, smb_inaddr_t *objp) { if (!xdr_int32_t(xdrs, &objp->a_family)) return (FALSE); if (objp->a_family == AF_INET) { if (!xdr_uint32_t(xdrs, (in_addr_t *)&objp->a_ipv4)) return (FALSE); } else { if (!xdr_vector(xdrs, (char *)&objp->a_ipv6, sizeof (objp->a_ipv6), sizeof (char), (xdrproc_t)xdr_char)) return (FALSE); } return (TRUE); } /* * XDR encode/decode for smb_netuserinfo_t. */ bool_t smb_netuserinfo_xdr(XDR *xdrs, smb_netuserinfo_t *objp) { if (!xdr_uint64_t(xdrs, &objp->ui_session_id)) return (FALSE); if (!xdr_uint64_t(xdrs, &objp->ui_user_id)) return (FALSE); if (!xdr_uint16_t(xdrs, &objp->ui_domain_len)) return (FALSE); if (!xdr_string(xdrs, &objp->ui_domain, ~0)) return (FALSE); if (!xdr_uint16_t(xdrs, &objp->ui_account_len)) return (FALSE); if (!xdr_string(xdrs, &objp->ui_account, ~0)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->ui_posix_uid)) return (FALSE); if (!xdr_uint16_t(xdrs, &objp->ui_workstation_len)) return (FALSE); if (!xdr_string(xdrs, &objp->ui_workstation, ~0)) return (FALSE); if (!smb_inaddr_xdr(xdrs, &objp->ui_ipaddr)) return (FALSE); if (!xdr_int32_t(xdrs, &objp->ui_native_os)) return (FALSE); if (!xdr_int64_t(xdrs, &objp->ui_logon_time)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->ui_numopens)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->ui_flags)) return (FALSE); return (TRUE); } /* * Encode an smb_netconnectinfo_t into a buffer. */ int smb_netconnectinfo_encode(smb_netconnectinfo_t *info, uint8_t *buf, uint32_t buflen, uint_t *nbytes) { XDR xdrs; int rc = 0; xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_ENCODE); if (!smb_netconnectinfo_xdr(&xdrs, info)) rc = -1; if (nbytes != NULL) *nbytes = xdr_getpos(&xdrs); xdr_destroy(&xdrs); return (rc); } /* * Decode an XDR buffer into an smb_netconnectinfo_t. */ int smb_netconnectinfo_decode(smb_netconnectinfo_t *info, uint8_t *buf, uint32_t buflen, uint_t *nbytes) { XDR xdrs; int rc = 0; xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_DECODE); bzero(info, sizeof (smb_netconnectinfo_t)); if (!smb_netconnectinfo_xdr(&xdrs, info)) rc = -1; if (nbytes != NULL) *nbytes = xdr_getpos(&xdrs); xdr_destroy(&xdrs); return (rc); } /* * XDR encode/decode for smb_netconnectinfo_t. */ bool_t smb_netconnectinfo_xdr(XDR *xdrs, smb_netconnectinfo_t *objp) { if (!xdr_uint32_t(xdrs, &objp->ci_id)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->ci_type)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->ci_numopens)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->ci_numusers)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->ci_time)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->ci_namelen)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->ci_sharelen)) return (FALSE); if (!xdr_string(xdrs, &objp->ci_username, MAXNAMELEN)) return (FALSE); if (!xdr_string(xdrs, &objp->ci_share, MAXNAMELEN)) return (FALSE); return (TRUE); } /* * Encode an smb_netfileinfo_t into a buffer. */ int smb_netfileinfo_encode(smb_netfileinfo_t *info, uint8_t *buf, uint32_t buflen, uint_t *nbytes) { XDR xdrs; int rc = 0; xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_ENCODE); if (!smb_netfileinfo_xdr(&xdrs, info)) rc = -1; if (nbytes != NULL) *nbytes = xdr_getpos(&xdrs); xdr_destroy(&xdrs); return (rc); } /* * Decode an XDR buffer into an smb_netfileinfo_t. */ int smb_netfileinfo_decode(smb_netfileinfo_t *info, uint8_t *buf, uint32_t buflen, uint_t *nbytes) { XDR xdrs; int rc = 0; xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_DECODE); bzero(info, sizeof (smb_netfileinfo_t)); if (!smb_netfileinfo_xdr(&xdrs, info)) rc = -1; if (nbytes != NULL) *nbytes = xdr_getpos(&xdrs); xdr_destroy(&xdrs); return (rc); } /* * XDR encode/decode for smb_netfileinfo_t. */ bool_t smb_netfileinfo_xdr(XDR *xdrs, smb_netfileinfo_t *objp) { if (!xdr_uint16_t(xdrs, &objp->fi_fid)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->fi_uniqid)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->fi_permissions)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->fi_numlocks)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->fi_pathlen)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->fi_namelen)) return (FALSE); if (!xdr_string(xdrs, &objp->fi_path, MAXPATHLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->fi_username, MAXNAMELEN)) return (FALSE); return (TRUE); } bool_t smb_gmttoken_query_xdr(XDR *xdrs, smb_gmttoken_query_t *objp) { if (!xdr_uint32_t(xdrs, &objp->gtq_count)) { return (FALSE); } if (!xdr_string(xdrs, &objp->gtq_path, ~0)) { return (FALSE); } return (TRUE); } static bool_t smb_gmttoken_xdr(XDR *xdrs, smb_gmttoken_t *objp) { if (!xdr_string(xdrs, objp, SMB_VSS_GMT_SIZE)) { return (FALSE); } return (TRUE); } bool_t smb_gmttoken_response_xdr(XDR *xdrs, smb_gmttoken_response_t *objp) { if (!xdr_uint32_t(xdrs, &objp->gtr_count)) { return (FALSE); } if (!xdr_array(xdrs, (char **)&objp->gtr_gmttokens.gtr_gmttokens_val, (uint_t *)&objp->gtr_gmttokens.gtr_gmttokens_len, ~0, sizeof (smb_gmttoken_t), (xdrproc_t)smb_gmttoken_xdr)) { return (FALSE); } return (TRUE); } bool_t smb_gmttoken_snapname_xdr(XDR *xdrs, smb_gmttoken_snapname_t *objp) { if (!xdr_string(xdrs, &objp->gts_path, MAXPATHLEN)) { return (FALSE); } if (!xdr_string(xdrs, &objp->gts_gmttoken, SMB_VSS_GMT_SIZE)) { return (FALSE); } if (!xdr_uint64_t(xdrs, &objp->gts_toktime)) { return (FALSE); } return (TRUE); } bool_t smb_quota_xdr(XDR *xdrs, smb_quota_t *objp) { if (!xdr_vector(xdrs, (char *)objp->q_sidstr, SMB_SID_STRSZ, sizeof (char), (xdrproc_t)xdr_char)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->q_sidtype)) return (FALSE); if (!xdr_uint64_t(xdrs, &objp->q_used)) return (FALSE); if (!xdr_uint64_t(xdrs, &objp->q_thresh)) return (FALSE); if (!xdr_uint64_t(xdrs, &objp->q_limit)) return (FALSE); return (TRUE); } bool_t smb_quota_sid_xdr(XDR *xdrs, smb_quota_sid_t *objp) { if (!xdr_vector(xdrs, (char *)objp->qs_sidstr, SMB_SID_STRSZ, sizeof (char), (xdrproc_t)xdr_char)) return (FALSE); return (TRUE); } bool_t smb_quota_query_xdr(XDR *xdrs, smb_quota_query_t *objp) { if (!xdr_string(xdrs, &objp->qq_root_path, ~0)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->qq_query_op)) return (FALSE); if (!xdr_bool(xdrs, &objp->qq_single)) return (FALSE); if (!xdr_bool(xdrs, &objp->qq_restart)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->qq_max_quota)) return (FALSE); if (!smb_list_xdr(xdrs, &objp->qq_sid_list, offsetof(smb_quota_sid_t, qs_list_node), sizeof (smb_quota_sid_t), (xdrproc_t)smb_quota_sid_xdr)) return (FALSE); return (TRUE); } bool_t smb_quota_response_xdr(XDR *xdrs, smb_quota_response_t *objp) { if (!xdr_uint32_t(xdrs, &objp->qr_status)) return (FALSE); if (!smb_list_xdr(xdrs, &objp->qr_quota_list, offsetof(smb_quota_t, q_list_node), sizeof (smb_quota_t), (xdrproc_t)smb_quota_xdr)) return (FALSE); return (TRUE); } bool_t smb_quota_set_xdr(XDR *xdrs, smb_quota_set_t *objp) { if (!xdr_string(xdrs, &objp->qs_root_path, ~0)) return (FALSE); if (!smb_list_xdr(xdrs, &objp->qs_quota_list, offsetof(smb_quota_t, q_list_node), sizeof (smb_quota_t), (xdrproc_t)smb_quota_xdr)) return (FALSE); return (TRUE); } /* * XDR a list_t list of elements * offset - offset of list_node_t in list element * elsize - size of list element * elproc - XDR function for the list element */ bool_t smb_list_xdr(XDR *xdrs, list_t *list, const size_t offset, const size_t elsize, const xdrproc_t elproc) { void *node; uint32_t count = 0; switch (xdrs->x_op) { case XDR_ENCODE: node = list_head(list); while (node) { ++count; node = list_next(list, node); } if (!xdr_uint32_t(xdrs, &count)) return (FALSE); node = list_head(list); while (node) { if (!elproc(xdrs, node)) return (FALSE); node = list_next(list, node); } return (TRUE); case XDR_DECODE: if (!xdr_uint32_t(xdrs, &count)) return (FALSE); list_create(list, elsize, offset); while (count) { node = MEM_MALLOC("xdr", elsize); if (node == NULL) return (FALSE); if (!elproc(xdrs, node)) return (FALSE); list_insert_tail(list, node); --count; } return (TRUE); case XDR_FREE: while ((node = list_head(list)) != NULL) { list_remove(list, node); (void) elproc(xdrs, node); MEM_FREE("xdr", node); } list_destroy(list); return (TRUE); } return (FALSE); } bool_t dfs_target_pclass_xdr(XDR *xdrs, dfs_target_pclass_t *objp) { return (xdr_enum(xdrs, (enum_t *)objp)); } bool_t dfs_target_priority_xdr(XDR *xdrs, dfs_target_priority_t *objp) { if (!dfs_target_pclass_xdr(xdrs, &objp->p_class)) return (FALSE); if (!xdr_uint16_t(xdrs, &objp->p_rank)) return (FALSE); return (TRUE); } bool_t dfs_target_xdr(XDR *xdrs, dfs_target_t *objp) { if (!xdr_vector(xdrs, (char *)objp->t_server, DFS_SRVNAME_MAX, sizeof (char), (xdrproc_t)xdr_char)) return (FALSE); if (!xdr_vector(xdrs, (char *)objp->t_share, DFS_NAME_MAX, sizeof (char), (xdrproc_t)xdr_char)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->t_state)) return (FALSE); if (!dfs_target_priority_xdr(xdrs, &objp->t_priority)) return (FALSE); return (TRUE); } bool_t dfs_reftype_xdr(XDR *xdrs, dfs_reftype_t *objp) { return (xdr_enum(xdrs, (enum_t *)objp)); } bool_t dfs_info_xdr(XDR *xdrs, dfs_info_t *objp) { if (!xdr_vector(xdrs, (char *)objp->i_uncpath, DFS_PATH_MAX, sizeof (char), (xdrproc_t)xdr_char)) return (FALSE); if (!xdr_vector(xdrs, (char *)objp->i_comment, DFS_COMMENT_MAX, sizeof (char), (xdrproc_t)xdr_char)) return (FALSE); if (!xdr_vector(xdrs, (char *)objp->i_guid, UUID_PRINTABLE_STRING_LENGTH, sizeof (char), (xdrproc_t)xdr_char)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->i_state)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->i_timeout)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->i_propflags)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->i_type)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->i_targets, (uint32_t *)&objp->i_ntargets, ~0, sizeof (dfs_target_t), (xdrproc_t)dfs_target_xdr)) return (FALSE); return (TRUE); } bool_t dfs_referral_query_xdr(XDR *xdrs, dfs_referral_query_t *objp) { if (!dfs_reftype_xdr(xdrs, &objp->rq_type)) return (FALSE); if (!xdr_string(xdrs, &objp->rq_path, ~0)) return (FALSE); return (TRUE); } bool_t dfs_referral_response_xdr(XDR *xdrs, dfs_referral_response_t *objp) { if (!dfs_info_xdr(xdrs, &objp->rp_referrals)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->rp_status)) return (FALSE); return (TRUE); } bool_t smb_shr_hostaccess_query_xdr(XDR *xdrs, smb_shr_hostaccess_query_t *objp) { if (!xdr_string(xdrs, &objp->shq_none, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->shq_ro, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->shq_rw, ~0)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->shq_flag)) return (FALSE); if (!smb_inaddr_xdr(xdrs, &objp->shq_ipaddr)) return (FALSE); return (TRUE); } bool_t smb_shr_execinfo_xdr(XDR *xdrs, smb_shr_execinfo_t *objp) { if (!xdr_string(xdrs, &objp->e_sharename, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->e_winname, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->e_userdom, ~0)) return (FALSE); if (!smb_inaddr_xdr(xdrs, &objp->e_srv_ipaddr)) return (FALSE); if (!smb_inaddr_xdr(xdrs, &objp->e_cli_ipaddr)) return (FALSE); if (!xdr_string(xdrs, &objp->e_cli_netbiosname, ~0)) return (FALSE); if (!xdr_u_int(xdrs, &objp->e_uid)) return (FALSE); if (!xdr_int(xdrs, &objp->e_type)) return (FALSE); return (TRUE); } /* * The smbsrv ioctl callers include a CRC of the XDR encoded data, * and kmod ioctl handler checks it. Both use this function. This * is not really XDR related, but this is as good a place as any. */ #define SMB_CRC_POLYNOMIAL 0xD8B5D8B5 uint32_t smb_crc_gen(uint8_t *buf, size_t len) { uint32_t crc = SMB_CRC_POLYNOMIAL; uint8_t *p; int i; for (p = buf, i = 0; i < len; ++i, ++p) { crc = (crc ^ (uint32_t)*p) + (crc << 12); if (crc == 0 || crc == 0xFFFFFFFF) crc = SMB_CRC_POLYNOMIAL; } return (crc); }