/* * smb_tonetbiosname * * Creates a NetBIOS name based on the given name and suffix. * NetBIOS name is 15 capital characters, padded with space if needed * and the 16th byte is the suffix. */ void smb_tonetbiosname(char *name, char *nb_name, char suffix) { char tmp_name[NETBIOS_NAME_SZ]; smb_wchar_t wtmp_name[NETBIOS_NAME_SZ]; int len; size_t rc; len = 0; rc = smb_mbstowcs(wtmp_name, (const char *)name, NETBIOS_NAME_SZ); if (rc != (size_t)-1) { wtmp_name[NETBIOS_NAME_SZ - 1] = 0; rc = ucstooem(tmp_name, wtmp_name, NETBIOS_NAME_SZ, OEM_CPG_850); if (rc > 0) len = strlen(tmp_name); } (void) memset(nb_name, ' ', NETBIOS_NAME_SZ - 1); if (len) { (void) smb_strupr(tmp_name); (void) memcpy(nb_name, tmp_name, len); } nb_name[NETBIOS_NAME_SZ - 1] = suffix; }
/* * smb_pathname_preprocess_quota * * There is a special file required by windows so that the quota * tab will be displayed by windows clients. This is created in * a special directory, $EXTEND, at the root of the shared file * system. To hide this directory prepend a '.' (dot). */ static void smb_pathname_preprocess_quota(smb_request_t *sr, smb_pathname_t *pn) { char *name = "$EXTEND"; char *new_name = ".$EXTEND"; char *p, *slash; int len; if (!smb_node_is_vfsroot(sr->tid_tree->t_snode)) return; p = pn->pn_path; /* ignore any initial "\\" */ p += strspn(p, "\\"); if (smb_strcasecmp(p, name, strlen(name)) != 0) return; p += strlen(name); if ((*p != ':') && (*p != '\\') && (*p != '\0')) return; slash = (pn->pn_path[0] == '\\') ? "\\" : ""; len = strlen(pn->pn_path) + 2; pn->pn_path = smb_srm_alloc(sr, len); (void) snprintf(pn->pn_path, len, "%s%s%s", slash, new_name, p); (void) smb_strupr(pn->pn_path); }
uint32_t smb_is_executable(char *path) { char extension[5]; int len = strlen(path); if ((len >= 4) && (path[len - 4] == '.')) { (void) strcpy(extension, &path[len - 3]); (void) smb_strupr(extension); if (strcmp(extension, "EXE") == 0) return (NODE_FLAGS_EXECUTABLE); if (strcmp(extension, "COM") == 0) return (NODE_FLAGS_EXECUTABLE); if (strcmp(extension, "DLL") == 0) return (NODE_FLAGS_EXECUTABLE); if (strcmp(extension, "SYM") == 0) return (NODE_FLAGS_EXECUTABLE); } return (0); }
/* * Get the current system node name. The returned name is guaranteed * to be null-terminated (gethostname may not null terminate the name). * If the hostname has been fully-qualified for some reason, the domain * part will be removed. The returned hostname is converted to the * specified case (lower, upper, or preserved). * * If gethostname fails, the returned buffer will contain an empty * string. */ int smb_gethostname(char *buf, size_t buflen, smb_caseconv_t which) { char *p; if (buf == NULL || buflen == 0) return (-1); if (gethostname(buf, buflen) != 0) { *buf = '\0'; return (-1); } buf[buflen - 1] = '\0'; if ((p = strchr(buf, '.')) != NULL) *p = '\0'; switch (which) { case SMB_CASE_LOWER: (void) smb_strlwr(buf); break; case SMB_CASE_UPPER: (void) smb_strupr(buf); break; case SMB_CASE_PRESERVE: default: break; } return (0); }
static boolean_t smb_lmv2_password_ok( unsigned char *challenge, uint32_t clen, unsigned char *ntlm_hash, unsigned char *passwd, char *domain, char *username) { unsigned char *clnt_challenge; unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ]; unsigned char lmv2_resp[SMBAUTH_LM_RESP_SZ]; boolean_t ok = B_FALSE; char *dest[3]; int i; clnt_challenge = &passwd[SMBAUTH_HASH_SZ]; dest[0] = domain; if ((dest[1] = strdup(domain)) == NULL) return (B_FALSE); (void) smb_strupr(dest[1]); dest[2] = ""; /* * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS" * * The NTLMv2 Hash is created from: * - NTLM hash * - user's username, and * - the name of the logon destination(i.e. the NetBIOS name of either * the SMB server or NT Domain against which the suer is trying to * authenticate. * * Experiments show this is not exactly the case. * For Windows Server 2003, the domain name needs to be included and * converted to uppercase. For Vista, the domain name needs to be * included also, but leave the case alone. And in some cases it needs * to be empty. All three variants are tried here. */ for (i = 0; i < (sizeof (dest) / sizeof (char *)); i++) { if (smb_auth_ntlmv2_hash(ntlm_hash, username, dest[i], ntlmv2_hash) != SMBAUTH_SUCCESS) break; if (smb_auth_v2_response(ntlmv2_hash, challenge, clen, clnt_challenge, SMBAUTH_V2_CLNT_CHALLENGE_SZ, lmv2_resp) < 0) break; ok = (bcmp(passwd, lmv2_resp, SMBAUTH_LM_RESP_SZ) == 0); if (ok) break; } free(dest[1]); return (ok); }
/* * smb_encode_netbios_name * * Set up the name and scope fields in the destination name_entry structure. * The name is padded with spaces to 15 bytes. The suffix is copied into the * last byte, i.e. "netbiosname <suffix>". The scope is copied and folded * to uppercase. */ void smb_encode_netbios_name(unsigned char *name, char suffix, unsigned char *scope, struct name_entry *dest) { smb_tonetbiosname((char *)name, (char *)dest->name, suffix); if (scope) { (void) strlcpy((char *)dest->scope, (const char *)scope, sizeof (dest->scope)); } else { (void) smb_config_getstr(SMB_CI_NBSCOPE, (char *)dest->scope, sizeof (dest->scope)); } (void) smb_strupr((char *)dest->scope); }
/* * smb_query_shortname * * If the node is a named stream, use its associated * unnamed stream name to determine the shortname. * If a shortname is required (smb_needs_mangle()), generate it * using smb_mangle(), otherwise, convert the original name to * upper-case and return it as the alternative name. */ static void smb_query_shortname(smb_node_t *node, smb_queryinfo_t *qinfo) { char *namep; if (SMB_IS_STREAM(node)) namep = node->n_unode->od_name; else namep = node->od_name; if (smb_needs_mangled(namep)) { smb_mangle(namep, qinfo->qi_attr.sa_vattr.va_nodeid, qinfo->qi_shortname, SMB_SHORTNAMELEN); } else { (void) strlcpy(qinfo->qi_shortname, namep, SMB_SHORTNAMELEN); (void) smb_strupr(qinfo->qi_shortname); } }
/* * smb_stream_parse_name * * smb_stream_parse_name should only be called for a path that * contains a valid named stream. Path validation should have * been performed before this function is called. * * Find the last component of path and split it into filename * and stream name. * * On return the named stream type will be present. The stream * type defaults to ":$DATA", if it has not been defined * For exmaple, 'stream' contains :<sname>:$DATA */ void smb_stream_parse_name(char *path, char *filename, char *stream) { char *fname, *sname, *stype; ASSERT(path); ASSERT(filename); ASSERT(stream); fname = strrchr(path, '\\'); fname = (fname == NULL) ? path : fname + 1; (void) strlcpy(filename, fname, MAXNAMELEN); sname = strchr(filename, ':'); (void) strlcpy(stream, sname, MAXNAMELEN); *sname = '\0'; stype = strchr(stream + 1, ':'); if (stype == NULL) (void) strlcat(stream, ":$DATA", MAXNAMELEN); else (void) smb_strupr(stype); }
/* * smb_auth_ntlmv2_hash * * The NTLM v2 hash will be created from the given NTLM hash, username, * and the NETBIOS name of the domain. * * The NTLMv2 hash will be returned via the ntlmv2_hash parameter which * will be used in the calculation of the NTLMv2 and LMv2 responses. */ int smb_auth_ntlmv2_hash(unsigned char *ntlm_hash, char *username, char *ntdomain, unsigned char *ntlmv2_hash) { smb_wchar_t *data; int data_len; unsigned char *buf; int rc; if (username == NULL || ntdomain == NULL) return (SMBAUTH_FAILURE); (void) smb_strupr(username); data_len = strlen(username) + strlen(ntdomain); buf = (unsigned char *)malloc((data_len + 1) * sizeof (char)); if (buf == NULL) return (SMBAUTH_FAILURE); (void) snprintf((char *)buf, data_len + 1, "%s%s", username, ntdomain); data = (smb_wchar_t *)malloc((data_len + 1) * sizeof (smb_wchar_t)); if (data == NULL) { free(buf); return (SMBAUTH_FAILURE); } data_len = smb_auth_qnd_unicode(data, (char *)buf, data_len); rc = SMBAUTH_HMACT64((unsigned char *)data, data_len, ntlm_hash, SMBAUTH_HASH_SZ, ntlmv2_hash); free(buf); free(data); return (rc); }
/* * smb_pathname_init * Parse path: pname\\fname:sname:stype * * Elements of the smb_pathname_t structure are allocated using request * specific storage and will be free'd when the sr is destroyed. * * Populate pn structure elements with the individual elements * of pn->pn_path. pn->pn_sname will contain the whole stream name * including the stream type and preceding colon: :sname:%DATA * pn_stype will point to the stream type within pn_sname. * * If the pname element is missing pn_pname will be set to NULL. * If any other element is missing the pointer in pn will be NULL. */ void smb_pathname_init(smb_request_t *sr, smb_pathname_t *pn, char *path) { char *pname, *fname, *sname; int len; bzero(pn, sizeof (smb_pathname_t)); pn->pn_path = smb_pathname_strdup(sr, path); smb_pathname_preprocess(sr, pn); /* parse pn->pn_path into its constituent parts */ pname = pn->pn_path; fname = strrchr(pn->pn_path, '\\'); if (fname) { if (fname == pname) { pn->pn_pname = NULL; } else { *fname = '\0'; pn->pn_pname = smb_pathname_strdup(sr, pname); *fname = '\\'; } ++fname; } else { fname = pname; pn->pn_pname = NULL; } if (fname[0] == '\0') { pn->pn_fname = NULL; return; } if (!smb_is_stream_name(fname)) { pn->pn_fname = smb_pathname_strdup(sr, fname); return; } /* * find sname and stype in fname. * sname can't be NULL smb_is_stream_name checks this */ sname = strchr(fname, ':'); if (sname == fname) fname = NULL; else { *sname = '\0'; pn->pn_fname = smb_pathname_strdup(sr, fname); *sname = ':'; } pn->pn_sname = smb_pathname_strdup(sr, sname); pn->pn_stype = strchr(pn->pn_sname + 1, ':'); if (pn->pn_stype) { (void) smb_strupr(pn->pn_stype); } else { len = strlen(pn->pn_sname); pn->pn_sname = smb_pathname_strcat(sr, pn->pn_sname, ":$DATA"); pn->pn_stype = pn->pn_sname + len; } ++pn->pn_stype; }
static boolean_t smb_ntlmv2_password_ok( unsigned char *challenge, uint32_t clen, unsigned char *ntlm_hash, unsigned char *passwd, int pwdlen, char *domain, char *username, uchar_t *session_key) { unsigned char *clnt_blob; int clnt_blob_len; unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ]; unsigned char *ntlmv2_resp; boolean_t ok = B_FALSE; char *dest[3]; int i; int rc; clnt_blob_len = pwdlen - SMBAUTH_HASH_SZ; clnt_blob = &passwd[SMBAUTH_HASH_SZ]; dest[0] = domain; if ((dest[1] = strdup(domain)) == NULL) return (B_FALSE); (void) smb_strupr(dest[1]); dest[2] = ""; /* * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS" * * The NTLMv2 Hash is created from: * - NTLM hash * - user's username, and * - the name of the logon destination(i.e. the NetBIOS name of either * the SMB server or NT Domain against which the user is trying to * authenticate. * * Experiments show this is not exactly the case. * For Windows Server 2003, the domain name needs to be included and * converted to uppercase. For Vista, the domain name needs to be * included also, but leave the case alone. And in some cases it needs * to be empty. All three variants are tried here. */ ntlmv2_resp = (unsigned char *)malloc(SMBAUTH_HASH_SZ + clnt_blob_len); if (ntlmv2_resp == NULL) { free(dest[1]); return (B_FALSE); } for (i = 0; i < (sizeof (dest) / sizeof (char *)); i++) { if (smb_auth_ntlmv2_hash(ntlm_hash, username, dest[i], ntlmv2_hash) != SMBAUTH_SUCCESS) break; if (smb_auth_v2_response(ntlmv2_hash, challenge, clen, clnt_blob, clnt_blob_len, ntlmv2_resp) < 0) break; ok = (bcmp(passwd, ntlmv2_resp, pwdlen) == 0); if (ok && session_key) { rc = SMBAUTH_HMACT64(ntlmv2_resp, SMBAUTH_HASH_SZ, ntlmv2_hash, SMBAUTH_SESSION_KEY_SZ, session_key); if (rc != SMBAUTH_SUCCESS) { ok = B_FALSE; } break; } } free(dest[1]); free(ntlmv2_resp); return (ok); }
/* * 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, bytes; 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; } out = scope; bytes = 0; for (length = *cp++; length != 0; length = *cp++) { if ((length & 0xc0) != 0x00) { /* * This is a pointer or a reserved field. If it's * a pointer (16-bits) we have to skip the next byte. */ if ((length & 0xc0) == 0xc0) { cp++; continue; } } /* * Replace the length with a '.', except for the first one. */ if (out != scope) { *out++ = '.'; bytes++; } while (length-- > 0) { if (bytes++ >= (NETBIOS_DOMAIN_NAME_MAX - 1)) { return (-1); } *out++ = *cp++; } } *out = 0; /* * We are supposed to preserve all 8-bits of the domain name * but due to the single byte representation in the name cache * and UTF-8 encoding everywhere else, we restrict domain names * to Appendix 1 - Domain Name Syntax Specification in RFC883. */ if (domainname_is_valid(scope)) { (void) smb_strupr(scope); /*LINTED E_PTRDIFF_OVERFLOW*/ return (cp - in); } scope[0] = '\0'; return (-1); }
/* * smb_query_fileinfo * * Populate smb_queryinfo_t structure for SMB_FTYPE_DISK * (This should become an smb_ofile / smb_node function.) */ int smb_query_fileinfo(smb_request_t *sr, smb_node_t *node, uint16_t infolev, smb_queryinfo_t *qinfo) { int rc; boolean_t include_sharename = B_FALSE; (void) bzero(qinfo, sizeof (smb_queryinfo_t)); if (smb_node_getattr(sr, node, &qinfo->qi_attr) != 0) { smbsr_error(sr, NT_STATUS_INTERNAL_ERROR, ERRDOS, ERROR_INTERNAL_ERROR); return (-1); } qinfo->qi_node = node; qinfo->qi_delete_on_close = (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) != 0; /* * The number of links reported should be the number of * non-deleted links. Thus if delete_on_close is set, * decrement the link count. */ if (qinfo->qi_delete_on_close && qinfo->qi_attr.sa_vattr.va_nlink > 0) { --(qinfo->qi_attr.sa_vattr.va_nlink); } /* populate name, namelen and shortname */ /* ALL_INFO levels include the sharename in the name field */ if ((infolev == SMB_QUERY_FILE_ALL_INFO) || (infolev == SMB_FILE_ALL_INFORMATION)) { include_sharename = B_TRUE; } rc = smb_query_pathname(sr->tid_tree, node, include_sharename, qinfo->qi_name, MAXPATHLEN); if (rc != 0) { smbsr_errno(sr, rc); return (-1); } qinfo->qi_namelen = smb_ascii_or_unicode_strlen(sr, qinfo->qi_name); /* * For some reason NT will not show the security tab in the root * directory of a mapped drive unless the filename length is * greater than one. So we hack the length here to persuade NT * to show the tab. It should be safe because of the null * terminator character. */ if (qinfo->qi_namelen == 1) qinfo->qi_namelen = 2; /* * If the shortname is generated by smb_mangle_name() * it will be returned as the alternative name. * Otherwise, convert the original name to upper-case * and return it as the alternative name. */ (void) smb_mangle_name(qinfo->qi_attr.sa_vattr.va_nodeid, node->od_name, qinfo->qi_shortname, qinfo->qi_name83, 0); if (*qinfo->qi_shortname == 0) { (void) strlcpy(qinfo->qi_shortname, node->od_name, SMB_SHORTNAMELEN); (void) smb_strupr(qinfo->qi_shortname); } return (0); }
/* * Always set ACL support because the VFS will fake ACLs for file systems * that don't support them. * * Some flags are dependent on the typename, which is also set up here. * File system types are hardcoded in uts/common/os/vfs_conf.c. */ static void smb_tree_get_flags(const smb_share_t *si, vfs_t *vfsp, smb_tree_t *tree) { typedef struct smb_mtype { char *mt_name; size_t mt_namelen; uint32_t mt_flags; } smb_mtype_t; static smb_mtype_t smb_mtype[] = { { "zfs", 3, SMB_TREE_UNICODE_ON_DISK }, { "ufs", 3, SMB_TREE_UNICODE_ON_DISK }, { "nfs", 3, SMB_TREE_NFS_MOUNTED }, { "tmpfs", 5, SMB_TREE_NO_EXPORT } }; smb_mtype_t *mtype; char *name; uint32_t flags = SMB_TREE_SUPPORTS_ACLS; int i; if (si->shr_flags & SMB_SHRF_CATIA) flags |= SMB_TREE_CATIA; if (si->shr_flags & SMB_SHRF_ABE) flags |= SMB_TREE_ABE; if (vfsp->vfs_flag & VFS_RDONLY) flags |= SMB_TREE_READONLY; if (vfsp->vfs_flag & VFS_XATTR) flags |= SMB_TREE_STREAMS; if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL)) flags |= SMB_TREE_NO_ATIME; name = vfssw[vfsp->vfs_fstype].vsw_name; for (i = 0; i < sizeof (smb_mtype) / sizeof (smb_mtype[0]); ++i) { mtype = &smb_mtype[i]; if (strncasecmp(name, mtype->mt_name, mtype->mt_namelen) == 0) flags |= mtype->mt_flags; } (void) strlcpy(tree->t_typename, name, SMB_TYPENAMELEN); (void) smb_strupr((char *)tree->t_typename); if (vfs_has_feature(vfsp, VFSFT_XVATTR)) flags |= SMB_TREE_XVATTR; if (vfs_has_feature(vfsp, VFSFT_CASEINSENSITIVE)) flags |= SMB_TREE_CASEINSENSITIVE; if (vfs_has_feature(vfsp, VFSFT_NOCASESENSITIVE)) flags |= SMB_TREE_NO_CASESENSITIVE; if (vfs_has_feature(vfsp, VFSFT_DIRENTFLAGS)) flags |= SMB_TREE_DIRENTFLAGS; if (vfs_has_feature(vfsp, VFSFT_ACLONCREATE)) flags |= SMB_TREE_ACLONCREATE; if (vfs_has_feature(vfsp, VFSFT_ACEMASKONACCESS)) flags |= SMB_TREE_ACEMASKONACCESS; DTRACE_PROBE2(smb__tree__flags, uint32_t, flags, char *, name); tree->t_flags = flags; }