Commit e2f34481 authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French

cifsd: add server-side procedures for SMB3

This adds smb3 engine, NTLM/NTLMv2/Kerberos authentication, oplock/lease
cache mechanism for cifsd.
Signed-off-by: default avatarNamjae Jeon <namjae.jeon@samsung.com>
Signed-off-by: default avatarSergey Senozhatsky <sergey.senozhatsky@gmail.com>
Signed-off-by: default avatarHyunchul Lee <hyc.lee@gmail.com>
Acked-by: default avatarRonnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 0626e664
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in
* turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich
*
* Copyright (c) 2000 RP Internet (www.rpi.net.au).
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include "glob.h"
#include "asn1.h"
#include "connection.h"
#include "auth.h"
/*****************************************************************************
*
* Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse)
*
*****************************************************************************/
/* Class */
#define ASN1_UNI 0 /* Universal */
#define ASN1_APL 1 /* Application */
#define ASN1_CTX 2 /* Context */
#define ASN1_PRV 3 /* Private */
/* Tag */
#define ASN1_EOC 0 /* End Of Contents or N/A */
#define ASN1_BOL 1 /* Boolean */
#define ASN1_INT 2 /* Integer */
#define ASN1_BTS 3 /* Bit String */
#define ASN1_OTS 4 /* Octet String */
#define ASN1_NUL 5 /* Null */
#define ASN1_OJI 6 /* Object Identifier */
#define ASN1_OJD 7 /* Object Description */
#define ASN1_EXT 8 /* External */
#define ASN1_ENUM 10 /* Enumerated */
#define ASN1_SEQ 16 /* Sequence */
#define ASN1_SET 17 /* Set */
#define ASN1_NUMSTR 18 /* Numerical String */
#define ASN1_PRNSTR 19 /* Printable String */
#define ASN1_TEXSTR 20 /* Teletext String */
#define ASN1_VIDSTR 21 /* Video String */
#define ASN1_IA5STR 22 /* IA5 String */
#define ASN1_UNITIM 23 /* Universal Time */
#define ASN1_GENTIM 24 /* General Time */
#define ASN1_GRASTR 25 /* Graphical String */
#define ASN1_VISSTR 26 /* Visible String */
#define ASN1_GENSTR 27 /* General String */
/* Primitive / Constructed methods*/
#define ASN1_PRI 0 /* Primitive */
#define ASN1_CON 1 /* Constructed */
/*
* Error codes.
*/
#define ASN1_ERR_NOERROR 0
#define ASN1_ERR_DEC_EMPTY 2
#define ASN1_ERR_DEC_EOC_MISMATCH 3
#define ASN1_ERR_DEC_LENGTH_MISMATCH 4
#define ASN1_ERR_DEC_BADVALUE 5
#define SPNEGO_OID_LEN 7
#define NTLMSSP_OID_LEN 10
#define KRB5_OID_LEN 7
#define KRB5U2U_OID_LEN 8
#define MSKRB5_OID_LEN 7
static unsigned long SPNEGO_OID[7] = { 1, 3, 6, 1, 5, 5, 2 };
static unsigned long NTLMSSP_OID[10] = { 1, 3, 6, 1, 4, 1, 311, 2, 2, 10 };
static unsigned long KRB5_OID[7] = { 1, 2, 840, 113554, 1, 2, 2 };
static unsigned long KRB5U2U_OID[8] = { 1, 2, 840, 113554, 1, 2, 2, 3 };
static unsigned long MSKRB5_OID[7] = { 1, 2, 840, 48018, 1, 2, 2 };
static char NTLMSSP_OID_STR[NTLMSSP_OID_LEN] = { 0x2b, 0x06, 0x01, 0x04, 0x01,
0x82, 0x37, 0x02, 0x02, 0x0a };
/*
* ASN.1 context.
*/
struct asn1_ctx {
int error; /* Error condition */
unsigned char *pointer; /* Octet just to be decoded */
unsigned char *begin; /* First octet */
unsigned char *end; /* Octet after last octet */
};
/*
* Octet string (not null terminated)
*/
struct asn1_octstr {
unsigned char *data;
unsigned int len;
};
static void
asn1_open(struct asn1_ctx *ctx, unsigned char *buf, unsigned int len)
{
ctx->begin = buf;
ctx->end = buf + len;
ctx->pointer = buf;
ctx->error = ASN1_ERR_NOERROR;
}
static unsigned char
asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch)
{
if (ctx->pointer >= ctx->end) {
ctx->error = ASN1_ERR_DEC_EMPTY;
return 0;
}
*ch = *(ctx->pointer)++;
return 1;
}
static unsigned char
asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag)
{
unsigned char ch;
*tag = 0;
do {
if (!asn1_octet_decode(ctx, &ch))
return 0;
*tag <<= 7;
*tag |= ch & 0x7F;
} while ((ch & 0x80) == 0x80);
return 1;
}
static unsigned char
asn1_id_decode(struct asn1_ctx *ctx,
unsigned int *cls, unsigned int *con, unsigned int *tag)
{
unsigned char ch;
if (!asn1_octet_decode(ctx, &ch))
return 0;
*cls = (ch & 0xC0) >> 6;
*con = (ch & 0x20) >> 5;
*tag = (ch & 0x1F);
if (*tag == 0x1F) {
if (!asn1_tag_decode(ctx, tag))
return 0;
}
return 1;
}
static unsigned char
asn1_length_decode(struct asn1_ctx *ctx, unsigned int *def, unsigned int *len)
{
unsigned char ch, cnt;
if (!asn1_octet_decode(ctx, &ch))
return 0;
if (ch == 0x80)
*def = 0;
else {
*def = 1;
if (ch < 0x80)
*len = ch;
else {
cnt = (unsigned char) (ch & 0x7F);
*len = 0;
while (cnt > 0) {
if (!asn1_octet_decode(ctx, &ch))
return 0;
*len <<= 8;
*len |= ch;
cnt--;
}
}
}
/* don't trust len bigger than ctx buffer */
if (*len > ctx->end - ctx->pointer)
return 0;
return 1;
}
static unsigned char
asn1_header_decode(struct asn1_ctx *ctx,
unsigned char **eoc,
unsigned int *cls, unsigned int *con, unsigned int *tag)
{
unsigned int def = 0;
unsigned int len = 0;
if (!asn1_id_decode(ctx, cls, con, tag))
return 0;
if (!asn1_length_decode(ctx, &def, &len))
return 0;
/* primitive shall be definite, indefinite shall be constructed */
if (*con == ASN1_PRI && !def)
return 0;
if (def)
*eoc = ctx->pointer + len;
else
*eoc = NULL;
return 1;
}
static unsigned char
asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc)
{
unsigned char ch;
if (!eoc) {
if (!asn1_octet_decode(ctx, &ch))
return 0;
if (ch != 0x00) {
ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
return 0;
}
if (!asn1_octet_decode(ctx, &ch))
return 0;
if (ch != 0x00) {
ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
return 0;
}
} else {
if (ctx->pointer != eoc) {
ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH;
return 0;
}
}
return 1;
}
static unsigned char
asn1_subid_decode(struct asn1_ctx *ctx, unsigned long *subid)
{
unsigned char ch;
*subid = 0;
do {
if (!asn1_octet_decode(ctx, &ch))
return 0;
*subid <<= 7;
*subid |= ch & 0x7F;
} while ((ch & 0x80) == 0x80);
return 1;
}
static int
asn1_oid_decode(struct asn1_ctx *ctx,
unsigned char *eoc, unsigned long **oid, unsigned int *len)
{
unsigned long subid;
unsigned int size;
unsigned long *optr;
size = eoc - ctx->pointer + 1;
/* first subid actually encodes first two subids */
if (size < 2 || size > UINT_MAX/sizeof(unsigned long))
return 0;
*oid = kmalloc(size * sizeof(unsigned long), GFP_KERNEL);
if (!*oid)
return 0;
optr = *oid;
if (!asn1_subid_decode(ctx, &subid)) {
kfree(*oid);
*oid = NULL;
return 0;
}
if (subid < 40) {
optr[0] = 0;
optr[1] = subid;
} else if (subid < 80) {
optr[0] = 1;
optr[1] = subid - 40;
} else {
optr[0] = 2;
optr[1] = subid - 80;
}
*len = 2;
optr += 2;
while (ctx->pointer < eoc) {
if (++(*len) > size) {
ctx->error = ASN1_ERR_DEC_BADVALUE;
kfree(*oid);
*oid = NULL;
return 0;
}
if (!asn1_subid_decode(ctx, optr++)) {
kfree(*oid);
*oid = NULL;
return 0;
}
}
return 1;
}
static int
compare_oid(unsigned long *oid1, unsigned int oid1len,
unsigned long *oid2, unsigned int oid2len)
{
unsigned int i;
if (oid1len != oid2len)
return 0;
for (i = 0; i < oid1len; i++) {
if (oid1[i] != oid2[i])
return 0;
}
return 1;
}
/* BB check for endian conversion issues here */
int
ksmbd_decode_negTokenInit(unsigned char *security_blob, int length,
struct ksmbd_conn *conn)
{
struct asn1_ctx ctx;
unsigned char *end;
unsigned char *sequence_end;
unsigned long *oid = NULL;
unsigned int cls, con, tag, oidlen, rc, mechTokenlen;
unsigned int mech_type;
ksmbd_debug(AUTH, "Received SecBlob: length %d\n", length);
asn1_open(&ctx, security_blob, length);
/* GSSAPI header */
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
ksmbd_debug(AUTH, "Error decoding negTokenInit header\n");
return 0;
} else if ((cls != ASN1_APL) || (con != ASN1_CON)
|| (tag != ASN1_EOC)) {
ksmbd_debug(AUTH, "cls = %d con = %d tag = %d\n", cls, con,
tag);
return 0;
}
/* Check for SPNEGO OID -- remember to free obj->oid */
rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag);
if (rc) {
if ((tag == ASN1_OJI) && (con == ASN1_PRI) &&
(cls == ASN1_UNI)) {
rc = asn1_oid_decode(&ctx, end, &oid, &oidlen);
if (rc) {
rc = compare_oid(oid, oidlen, SPNEGO_OID,
SPNEGO_OID_LEN);
kfree(oid);
}
} else
rc = 0;
}
/* SPNEGO OID not present or garbled -- bail out */
if (!rc) {
ksmbd_debug(AUTH, "Error decoding negTokenInit header\n");
return 0;
}
/* SPNEGO */
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
ksmbd_debug(AUTH, "Error decoding negTokenInit\n");
return 0;
} else if ((cls != ASN1_CTX) || (con != ASN1_CON)
|| (tag != ASN1_EOC)) {
ksmbd_debug(AUTH,
"cls = %d con = %d tag = %d end = %p (%d) exit 0\n",
cls, con, tag, end, *end);
return 0;
}
/* negTokenInit */
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
ksmbd_debug(AUTH, "Error decoding negTokenInit\n");
return 0;
} else if ((cls != ASN1_UNI) || (con != ASN1_CON)
|| (tag != ASN1_SEQ)) {
ksmbd_debug(AUTH,
"cls = %d con = %d tag = %d end = %p (%d) exit 1\n",
cls, con, tag, end, *end);
return 0;
}
/* sequence */
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
ksmbd_debug(AUTH, "Error decoding 2nd part of negTokenInit\n");
return 0;
} else if ((cls != ASN1_CTX) || (con != ASN1_CON)
|| (tag != ASN1_EOC)) {
ksmbd_debug(AUTH,
"cls = %d con = %d tag = %d end = %p (%d) exit 0\n",
cls, con, tag, end, *end);
return 0;
}
/* sequence of */
if (asn1_header_decode
(&ctx, &sequence_end, &cls, &con, &tag) == 0) {
ksmbd_debug(AUTH, "Error decoding 2nd part of negTokenInit\n");
return 0;
} else if ((cls != ASN1_UNI) || (con != ASN1_CON)
|| (tag != ASN1_SEQ)) {
ksmbd_debug(AUTH,
"cls = %d con = %d tag = %d end = %p (%d) exit 1\n",
cls, con, tag, end, *end);
return 0;
}
/* list of security mechanisms */
while (!asn1_eoc_decode(&ctx, sequence_end)) {
rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag);
if (!rc) {
ksmbd_debug(AUTH,
"Error decoding negTokenInit hdr exit2\n");
return 0;
}
if ((tag == ASN1_OJI) && (con == ASN1_PRI)) {
if (asn1_oid_decode(&ctx, end, &oid, &oidlen)) {
if (compare_oid(oid, oidlen, MSKRB5_OID,
MSKRB5_OID_LEN))
mech_type = KSMBD_AUTH_MSKRB5;
else if (compare_oid(oid, oidlen, KRB5U2U_OID,
KRB5U2U_OID_LEN))
mech_type = KSMBD_AUTH_KRB5U2U;
else if (compare_oid(oid, oidlen, KRB5_OID,
KRB5_OID_LEN))
mech_type = KSMBD_AUTH_KRB5;
else if (compare_oid(oid, oidlen, NTLMSSP_OID,
NTLMSSP_OID_LEN))
mech_type = KSMBD_AUTH_NTLMSSP;
else {
kfree(oid);
continue;
}
conn->auth_mechs |= mech_type;
if (conn->preferred_auth_mech == 0)
conn->preferred_auth_mech = mech_type;
kfree(oid);
}
} else {
ksmbd_debug(AUTH,
"Should be an oid what is going on?\n");
}
}
/* sequence */
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
ksmbd_debug(AUTH, "Error decoding 2nd part of negTokenInit\n");
return 0;
} else if ((cls != ASN1_CTX) || (con != ASN1_CON)
|| (tag != ASN1_INT)) {
ksmbd_debug(AUTH,
"cls = %d con = %d tag = %d end = %p (%d) exit 0\n",
cls, con, tag, end, *end);
return 0;
}
/* sequence of */
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
ksmbd_debug(AUTH, "Error decoding 2nd part of negTokenInit\n");
return 0;
} else if ((cls != ASN1_UNI) || (con != ASN1_PRI)
|| (tag != ASN1_OTS)) {
ksmbd_debug(AUTH,
"cls = %d con = %d tag = %d end = %p (%d) exit 0\n",
cls, con, tag, end, *end);
return 0;
}
mechTokenlen = ctx.end - ctx.pointer;
conn->mechToken = kmalloc(mechTokenlen + 1, GFP_KERNEL);
if (!conn->mechToken) {
ksmbd_err("memory allocation error\n");
return 0;
}
memcpy(conn->mechToken, ctx.pointer, mechTokenlen);
conn->mechToken[mechTokenlen] = '\0';
return 1;
}
int
ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length,
struct ksmbd_conn *conn)
{
struct asn1_ctx ctx;
unsigned char *end;
unsigned int cls, con, tag, mechTokenlen;
ksmbd_debug(AUTH, "Received Auth SecBlob: length %d\n", length);
asn1_open(&ctx, security_blob, length);
/* GSSAPI header */
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
ksmbd_debug(AUTH, "Error decoding negTokenInit header\n");
return 0;
} else if ((cls != ASN1_CTX) || (con != ASN1_CON)
|| (tag != ASN1_BOL)) {
ksmbd_debug(AUTH, "cls = %d con = %d tag = %d\n", cls, con,
tag);
return 0;
}
/* SPNEGO */
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
ksmbd_debug(AUTH, "Error decoding negTokenInit\n");
return 0;
} else if ((cls != ASN1_UNI) || (con != ASN1_CON)
|| (tag != ASN1_SEQ)) {
ksmbd_debug(AUTH,
"cls = %d con = %d tag = %d end = %p (%d) exit 0\n",
cls, con, tag, end, *end);
return 0;
}
/* negTokenTarg */
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
ksmbd_debug(AUTH, "Error decoding negTokenInit\n");
return 0;
} else if ((cls != ASN1_CTX) || (con != ASN1_CON)
|| (tag != ASN1_INT)) {
ksmbd_debug(AUTH,
"cls = %d con = %d tag = %d end = %p (%d) exit 1\n",
cls, con, tag, end, *end);
return 0;
}
/* negTokenTarg */
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
ksmbd_debug(AUTH, "Error decoding negTokenInit\n");
return 0;
} else if ((cls != ASN1_UNI) || (con != ASN1_PRI)
|| (tag != ASN1_OTS)) {
ksmbd_debug(AUTH,
"cls = %d con = %d tag = %d end = %p (%d) exit 1\n",
cls, con, tag, end, *end);
return 0;
}
mechTokenlen = ctx.end - ctx.pointer;
conn->mechToken = kmalloc(mechTokenlen + 1, GFP_KERNEL);
if (!conn->mechToken) {
ksmbd_err("memory allocation error\n");
return 0;
}
memcpy(conn->mechToken, ctx.pointer, mechTokenlen);
conn->mechToken[mechTokenlen] = '\0';
return 1;
}
static int compute_asn_hdr_len_bytes(int len)
{
if (len > 0xFFFFFF)
return 4;
else if (len > 0xFFFF)
return 3;
else if (len > 0xFF)
return 2;
else if (len > 0x7F)
return 1;
else
return 0;
}
static void encode_asn_tag(char *buf,
unsigned int *ofs,
char tag,
char seq,
int length)
{
int i;
int index = *ofs;
char hdr_len = compute_asn_hdr_len_bytes(length);
int len = length + 2 + hdr_len;
/* insert tag */
buf[index++] = tag;
if (!hdr_len)
buf[index++] = len;
else {
buf[index++] = 0x80 | hdr_len;
for (i = hdr_len - 1; i >= 0; i--)
buf[index++] = (len >> (i * 8)) & 0xFF;
}
/* insert seq */
len = len - (index - *ofs);
buf[index++] = seq;
if (!hdr_len)
buf[index++] = len;
else {
buf[index++] = 0x80 | hdr_len;
for (i = hdr_len - 1; i >= 0; i--)
buf[index++] = (len >> (i * 8)) & 0xFF;
}
*ofs += (index - *ofs);
}
int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen,
char *ntlm_blob, int ntlm_blob_len)
{
char *buf;
unsigned int ofs = 0;
int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1;
int oid_len = 4 + compute_asn_hdr_len_bytes(NTLMSSP_OID_LEN) * 2 +
NTLMSSP_OID_LEN;
int ntlmssp_len = 4 + compute_asn_hdr_len_bytes(ntlm_blob_len) * 2 +
ntlm_blob_len;
int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len +
oid_len + ntlmssp_len) * 2 +
neg_result_len + oid_len + ntlmssp_len;
buf = kmalloc(total_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* insert main gss header */
encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len + oid_len +
ntlmssp_len);
/* insert neg result */
encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1);
buf[ofs++] = 1;
/* insert oid */
encode_asn_tag(buf, &ofs, 0xa1, 0x06, NTLMSSP_OID_LEN);
memcpy(buf + ofs, NTLMSSP_OID_STR, NTLMSSP_OID_LEN);
ofs += NTLMSSP_OID_LEN;
/* insert response token - ntlmssp blob */
encode_asn_tag(buf, &ofs, 0xa2, 0x04, ntlm_blob_len);
memcpy(buf + ofs, ntlm_blob, ntlm_blob_len);
ofs += ntlm_blob_len;
*pbuffer = buf;
*buflen = total_len;
return 0;
}
int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
int neg_result)
{
char *buf;
unsigned int ofs = 0;
int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1;
int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len) * 2 +
neg_result_len;
buf = kmalloc(total_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* insert main gss header */
encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len);
/* insert neg result */
encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1);
if (neg_result)
buf[ofs++] = 2;
else
buf[ofs++] = 0;
*pbuffer = buf;
*buflen = total_len;
return 0;
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in
* turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich
*
* Copyright (c) 2000 RP Internet (www.rpi.net.au).
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#ifndef __ASN1_H__
#define __ASN1_H__
int ksmbd_decode_negTokenInit(unsigned char *security_blob,
int length,
struct ksmbd_conn *conn);
int ksmbd_decode_negTokenTarg(unsigned char *security_blob,
int length,
struct ksmbd_conn *conn);
int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer,
u16 *buflen,
char *ntlm_blob,
int ntlm_blob_len);
int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer,
u16 *buflen,
int neg_result);
#endif /* __ASN1_H__ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/backing-dev.h>
#include <linux/writeback.h>
#include <linux/uio.h>
#include <linux/xattr.h>
#include <crypto/hash.h>
#include <crypto/aead.h>
#include <linux/random.h>
#include <linux/scatterlist.h>
#include "auth.h"
#include "glob.h"
#include <linux/fips.h>
#include <crypto/des.h>
#include "server.h"
#include "smb_common.h"
#include "connection.h"
#include "mgmt/user_session.h"
#include "mgmt/user_config.h"
#include "crypto_ctx.h"
#include "transport_ipc.h"
#include "buffer_pool.h"
/*
* Fixed format data defining GSS header and fixed string
* "not_defined_in_RFC4178@please_ignore".
* So sec blob data in neg phase could be generated statically.
*/
static char NEGOTIATE_GSS_HEADER[AUTH_GSS_LENGTH] = {
#ifdef CONFIG_SMB_SERVER_KERBEROS5
0x60, 0x5e, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05,
0x05, 0x02, 0xa0, 0x54, 0x30, 0x52, 0xa0, 0x24,
0x30, 0x22, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x09, 0x2a,
0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02,
0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82,
0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, 0x30, 0x28,
0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, 0x74, 0x5f,
0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f,
0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, 0x34, 0x31,
0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, 0x61, 0x73,
0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65
#else
0x60, 0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05,
0x05, 0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e,
0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a,
0x30, 0x28, 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f,
0x74, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65,
0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43,
0x34, 0x31, 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65,
0x61, 0x73, 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f,
0x72, 0x65
#endif
};
void ksmbd_copy_gss_neg_header(void *buf)
{
memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH);
}
static void
str_to_key(unsigned char *str, unsigned char *key)
{
int i;
key[0] = str[0] >> 1;
key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2);
key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3);
key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4);
key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5);
key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6);
key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7);
key[7] = str[6] & 0x7F;
for (i = 0; i < 8; i++)
key[i] = (key[i] << 1);
}
static int
smbhash(unsigned char *out, const unsigned char *in, unsigned char *key)
{
unsigned char key2[8];
struct des_ctx ctx;
str_to_key(key, key2);
if (fips_enabled) {
ksmbd_debug(AUTH,
"FIPS compliance enabled: DES not permitted\n");
return -ENOENT;
}
des_expand_key(&ctx, key2, DES_KEY_SIZE);
des_encrypt(&ctx, out, in);
memzero_explicit(&ctx, sizeof(ctx));
return 0;
}
static int ksmbd_enc_p24(unsigned char *p21,
const unsigned char *c8,
unsigned char *p24)
{
int rc;
rc = smbhash(p24, c8, p21);
if (rc)
return rc;
rc = smbhash(p24 + 8, c8, p21 + 7);
if (rc)
return rc;
rc = smbhash(p24 + 16, c8, p21 + 14);
return rc;
}
/* produce a md4 message digest from data of length n bytes */
static int ksmbd_enc_md4(unsigned char *md4_hash,
unsigned char *link_str,
int link_len)
{
int rc;
struct ksmbd_crypto_ctx *ctx;
ctx = ksmbd_crypto_ctx_find_md4();
if (!ctx) {
ksmbd_debug(AUTH, "Crypto md4 allocation error\n");
return -EINVAL;
}
rc = crypto_shash_init(CRYPTO_MD4(ctx));
if (rc) {
ksmbd_debug(AUTH, "Could not init md4 shash\n");
goto out;
}
rc = crypto_shash_update(CRYPTO_MD4(ctx), link_str, link_len);
if (rc) {
ksmbd_debug(AUTH, "Could not update with link_str\n");
goto out;
}
rc = crypto_shash_final(CRYPTO_MD4(ctx), md4_hash);
if (rc)
ksmbd_debug(AUTH, "Could not generate md4 hash\n");
out:
ksmbd_release_crypto_ctx(ctx);
return rc;
}
static int ksmbd_enc_update_sess_key(unsigned char *md5_hash,
char *nonce,
char *server_challenge,
int len)
{
int rc;
struct ksmbd_crypto_ctx *ctx;
ctx = ksmbd_crypto_ctx_find_md5();
if (!ctx) {
ksmbd_debug(AUTH, "Crypto md5 allocation error\n");
return -EINVAL;
}
rc = crypto_shash_init(CRYPTO_MD5(ctx));
if (rc) {
ksmbd_debug(AUTH, "Could not init md5 shash\n");
goto out;
}
rc = crypto_shash_update(CRYPTO_MD5(ctx), server_challenge, len);
if (rc) {
ksmbd_debug(AUTH, "Could not update with challenge\n");
goto out;
}
rc = crypto_shash_update(CRYPTO_MD5(ctx), nonce, len);
if (rc) {
ksmbd_debug(AUTH, "Could not update with nonce\n");
goto out;
}
rc = crypto_shash_final(CRYPTO_MD5(ctx), md5_hash);
if (rc)
ksmbd_debug(AUTH, "Could not generate md5 hash\n");
out:
ksmbd_release_crypto_ctx(ctx);
return rc;
}
/**
* ksmbd_gen_sess_key() - function to generate session key
* @sess: session of connection
* @hash: source hash value to be used for find session key
* @hmac: source hmac value to be used for finding session key
*
*/
static int ksmbd_gen_sess_key(struct ksmbd_session *sess,
char *hash,
char *hmac)
{
struct ksmbd_crypto_ctx *ctx;
int rc = -EINVAL;
ctx = ksmbd_crypto_ctx_find_hmacmd5();
if (!ctx)
goto out;
rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx),
hash,
CIFS_HMAC_MD5_HASH_SIZE);
if (rc) {
ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc);
goto out;
}
rc = crypto_shash_init(CRYPTO_HMACMD5(ctx));
if (rc) {
ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc);
goto out;
}
rc = crypto_shash_update(CRYPTO_HMACMD5(ctx),
hmac,
SMB2_NTLMV2_SESSKEY_SIZE);
if (rc) {
ksmbd_debug(AUTH, "Could not update with response error %d\n",
rc);
goto out;
}
rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key);
if (rc) {
ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n",
rc);
goto out;
}
out:
ksmbd_release_crypto_ctx(ctx);
return rc;
}
static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash,
char *dname)
{
int ret = -EINVAL, len;
wchar_t *domain = NULL;
__le16 *uniname = NULL;
struct ksmbd_crypto_ctx *ctx;
ctx = ksmbd_crypto_ctx_find_hmacmd5();
if (!ctx) {
ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n");
goto out;
}
ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx),
user_passkey(sess->user),
CIFS_ENCPWD_SIZE);
if (ret) {
ksmbd_debug(AUTH, "Could not set NT Hash as a key\n");
goto out;
}
ret = crypto_shash_init(CRYPTO_HMACMD5(ctx));
if (ret) {
ksmbd_debug(AUTH, "could not init hmacmd5\n");
goto out;
}
/* convert user_name to unicode */
len = strlen(user_name(sess->user));
uniname = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL);
if (!uniname) {
ret = -ENOMEM;
goto out;
}
if (len) {
len = smb_strtoUTF16(uniname, user_name(sess->user), len,
sess->conn->local_nls);
UniStrupr(uniname);
}
ret = crypto_shash_update(CRYPTO_HMACMD5(ctx),
(char *)uniname,
UNICODE_LEN(len));
if (ret) {
ksmbd_debug(AUTH, "Could not update with user\n");
goto out;
}
/* Convert domain name or conn name to unicode and uppercase */
len = strlen(dname);
domain = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL);
if (!domain) {
ret = -ENOMEM;
goto out;
}
len = smb_strtoUTF16((__le16 *)domain, dname, len,
sess->conn->local_nls);
ret = crypto_shash_update(CRYPTO_HMACMD5(ctx),
(char *)domain,
UNICODE_LEN(len));
if (ret) {
ksmbd_debug(AUTH, "Could not update with domain\n");
goto out;
}
ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash);
out:
if (ret)
ksmbd_debug(AUTH, "Could not generate md5 hash\n");
kfree(uniname);
kfree(domain);
ksmbd_release_crypto_ctx(ctx);
return ret;
}
/**
* ksmbd_auth_ntlm() - NTLM authentication handler
* @sess: session of connection
* @pw_buf: NTLM challenge response
* @passkey: user password
*
* Return: 0 on success, error number on error
*/
int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf)
{
int rc;
unsigned char p21[21];
char key[CIFS_AUTH_RESP_SIZE];
memset(p21, '\0', 21);
memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE);
rc = ksmbd_enc_p24(p21, sess->ntlmssp.cryptkey, key);
if (rc) {
ksmbd_err("password processing failed\n");
return rc;
}
ksmbd_enc_md4(sess->sess_key,
user_passkey(sess->user),
CIFS_SMB1_SESSKEY_SIZE);
memcpy(sess->sess_key + CIFS_SMB1_SESSKEY_SIZE, key,
CIFS_AUTH_RESP_SIZE);
sess->sequence_number = 1;
if (strncmp(pw_buf, key, CIFS_AUTH_RESP_SIZE) != 0) {
ksmbd_debug(AUTH, "ntlmv1 authentication failed\n");
rc = -EINVAL;
} else
ksmbd_debug(AUTH, "ntlmv1 authentication pass\n");
return rc;
}
/**
* ksmbd_auth_ntlmv2() - NTLMv2 authentication handler
* @sess: session of connection
* @ntlmv2: NTLMv2 challenge response
* @blen: NTLMv2 blob length
* @domain_name: domain name
*
* Return: 0 on success, error number on error
*/
int ksmbd_auth_ntlmv2(struct ksmbd_session *sess,
struct ntlmv2_resp *ntlmv2,
int blen,
char *domain_name)
{
char ntlmv2_hash[CIFS_ENCPWD_SIZE];
char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE];
struct ksmbd_crypto_ctx *ctx;
char *construct = NULL;
int rc = -EINVAL, len;
ctx = ksmbd_crypto_ctx_find_hmacmd5();
if (!ctx) {
ksmbd_debug(AUTH, "could not crypto alloc hmacmd5 rc %d\n", rc);
goto out;
}
rc = calc_ntlmv2_hash(sess, ntlmv2_hash, domain_name);
if (rc) {
ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc);
goto out;
}
rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx),
ntlmv2_hash,
CIFS_HMAC_MD5_HASH_SIZE);
if (rc) {
ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n");
goto out;
}
rc = crypto_shash_init(CRYPTO_HMACMD5(ctx));
if (rc) {
ksmbd_debug(AUTH, "Could not init hmacmd5\n");
goto out;
}
len = CIFS_CRYPTO_KEY_SIZE + blen;
construct = kzalloc(len, GFP_KERNEL);
if (!construct) {
rc = -ENOMEM;
goto out;
}
memcpy(construct, sess->ntlmssp.cryptkey, CIFS_CRYPTO_KEY_SIZE);
memcpy(construct + CIFS_CRYPTO_KEY_SIZE,
(char *)(&ntlmv2->blob_signature), blen);
rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len);
if (rc) {
ksmbd_debug(AUTH, "Could not update with response\n");
goto out;
}
rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp);
if (rc) {
ksmbd_debug(AUTH, "Could not generate md5 hash\n");
goto out;
}
rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp);
if (rc) {
ksmbd_debug(AUTH, "Could not generate sess key\n");
goto out;
}
rc = memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE);
out:
ksmbd_release_crypto_ctx(ctx);
kfree(construct);
return rc;
}
/**
* __ksmbd_auth_ntlmv2() - NTLM2(extended security) authentication handler
* @sess: session of connection
* @client_nonce: client nonce from LM response.
* @ntlm_resp: ntlm response data from client.
*
* Return: 0 on success, error number on error
*/
static int __ksmbd_auth_ntlmv2(struct ksmbd_session *sess,
char *client_nonce,
char *ntlm_resp)
{
char sess_key[CIFS_SMB1_SESSKEY_SIZE] = {0};
int rc;
unsigned char p21[21];
char key[CIFS_AUTH_RESP_SIZE];
rc = ksmbd_enc_update_sess_key(sess_key,
client_nonce,
(char *)sess->ntlmssp.cryptkey, 8);
if (rc) {
ksmbd_err("password processing failed\n");
goto out;
}
memset(p21, '\0', 21);
memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE);
rc = ksmbd_enc_p24(p21, sess_key, key);
if (rc) {
ksmbd_err("password processing failed\n");
goto out;
}
rc = memcmp(ntlm_resp, key, CIFS_AUTH_RESP_SIZE);
out:
return rc;
}
/**
* ksmbd_decode_ntlmssp_auth_blob() - helper function to construct
* authenticate blob
* @authblob: authenticate blob source pointer
* @usr: user details
* @sess: session of connection
*
* Return: 0 on success, error number on error
*/
int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
int blob_len,
struct ksmbd_session *sess)
{
char *domain_name;
unsigned int lm_off, nt_off;
unsigned short nt_len;
int ret;
if (blob_len < sizeof(struct authenticate_message)) {
ksmbd_debug(AUTH, "negotiate blob len %d too small\n",
blob_len);
return -EINVAL;
}
if (memcmp(authblob->Signature, "NTLMSSP", 8)) {
ksmbd_debug(AUTH, "blob signature incorrect %s\n",
authblob->Signature);
return -EINVAL;
}
lm_off = le32_to_cpu(authblob->LmChallengeResponse.BufferOffset);
nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset);
nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length);
/* process NTLM authentication */
if (nt_len == CIFS_AUTH_RESP_SIZE) {
if (le32_to_cpu(authblob->NegotiateFlags)
& NTLMSSP_NEGOTIATE_EXTENDED_SEC)
return __ksmbd_auth_ntlmv2(sess, (char *)authblob +
lm_off, (char *)authblob + nt_off);
else
return ksmbd_auth_ntlm(sess, (char *)authblob +
nt_off);
}
/* TODO : use domain name that imported from configuration file */
domain_name = smb_strndup_from_utf16(
(const char *)authblob +
le32_to_cpu(authblob->DomainName.BufferOffset),
le16_to_cpu(authblob->DomainName.Length), true,
sess->conn->local_nls);
if (IS_ERR(domain_name))
return PTR_ERR(domain_name);
/* process NTLMv2 authentication */
ksmbd_debug(AUTH, "decode_ntlmssp_authenticate_blob dname%s\n",
domain_name);
ret = ksmbd_auth_ntlmv2(sess,
(struct ntlmv2_resp *)((char *)authblob + nt_off),
nt_len - CIFS_ENCPWD_SIZE,
domain_name);
kfree(domain_name);
return ret;
}
/**
* ksmbd_decode_ntlmssp_neg_blob() - helper function to construct
* negotiate blob
* @negblob: negotiate blob source pointer
* @rsp: response header pointer to be updated
* @sess: session of connection
*
*/
int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob,
int blob_len,
struct ksmbd_session *sess)
{
if (blob_len < sizeof(struct negotiate_message)) {
ksmbd_debug(AUTH, "negotiate blob len %d too small\n",
blob_len);
return -EINVAL;
}
if (memcmp(negblob->Signature, "NTLMSSP", 8)) {
ksmbd_debug(AUTH, "blob signature incorrect %s\n",
negblob->Signature);
return -EINVAL;
}
sess->ntlmssp.client_flags = le32_to_cpu(negblob->NegotiateFlags);
return 0;
}
/**
* ksmbd_build_ntlmssp_challenge_blob() - helper function to construct
* challenge blob
* @chgblob: challenge blob source pointer to initialize
* @rsp: response header pointer to be updated
* @sess: session of connection
*
*/
unsigned int
ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob,
struct ksmbd_session *sess)
{
struct target_info *tinfo;
wchar_t *name;
__u8 *target_name;
unsigned int len, flags, blob_off, blob_len, type, target_info_len = 0;
int cflags = sess->ntlmssp.client_flags;
memcpy(chgblob->Signature, NTLMSSP_SIGNATURE, 8);
chgblob->MessageType = NtLmChallenge;
flags = NTLMSSP_NEGOTIATE_UNICODE |
NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_TARGET_TYPE_SERVER |
NTLMSSP_NEGOTIATE_TARGET_INFO;
if (cflags & NTLMSSP_NEGOTIATE_SIGN) {
flags |= NTLMSSP_NEGOTIATE_SIGN;
flags |= cflags & (NTLMSSP_NEGOTIATE_128 |
NTLMSSP_NEGOTIATE_56);
}
if (cflags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
if (cflags & NTLMSSP_REQUEST_TARGET)
flags |= NTLMSSP_REQUEST_TARGET;
if (sess->conn->use_spnego &&
(cflags & NTLMSSP_NEGOTIATE_EXTENDED_SEC))
flags |= NTLMSSP_NEGOTIATE_EXTENDED_SEC;
chgblob->NegotiateFlags = cpu_to_le32(flags);
len = strlen(ksmbd_netbios_name());
name = kmalloc(2 + (len * 2), GFP_KERNEL);
if (!name)
return -ENOMEM;
len = smb_strtoUTF16((__le16 *)name, ksmbd_netbios_name(), len,
sess->conn->local_nls);
len = UNICODE_LEN(len);
blob_off = sizeof(struct challenge_message);
blob_len = blob_off + len;
chgblob->TargetName.Length = cpu_to_le16(len);
chgblob->TargetName.MaximumLength = cpu_to_le16(len);
chgblob->TargetName.BufferOffset = cpu_to_le32(blob_off);
/* Initialize random conn challenge */
get_random_bytes(sess->ntlmssp.cryptkey, sizeof(__u64));
memcpy(chgblob->Challenge, sess->ntlmssp.cryptkey,
CIFS_CRYPTO_KEY_SIZE);
/* Add Target Information to security buffer */
chgblob->TargetInfoArray.BufferOffset = cpu_to_le32(blob_len);
target_name = (__u8 *)chgblob + blob_off;
memcpy(target_name, name, len);
tinfo = (struct target_info *)(target_name + len);
chgblob->TargetInfoArray.Length = 0;
/* Add target info list for NetBIOS/DNS settings */
for (type = NTLMSSP_AV_NB_COMPUTER_NAME;
type <= NTLMSSP_AV_DNS_DOMAIN_NAME; type++) {
tinfo->Type = cpu_to_le16(type);
tinfo->Length = cpu_to_le16(len);
memcpy(tinfo->Content, name, len);
tinfo = (struct target_info *)((char *)tinfo + 4 + len);
target_info_len += 4 + len;
}
/* Add terminator subblock */
tinfo->Type = 0;
tinfo->Length = 0;
target_info_len += 4;
chgblob->TargetInfoArray.Length = cpu_to_le16(target_info_len);
chgblob->TargetInfoArray.MaximumLength = cpu_to_le16(target_info_len);
blob_len += target_info_len;
kfree(name);
ksmbd_debug(AUTH, "NTLMSSP SecurityBufferLength %d\n", blob_len);
return blob_len;
}
#ifdef CONFIG_SMB_SERVER_KERBEROS5
int ksmbd_krb5_authenticate(struct ksmbd_session *sess,
char *in_blob, int in_len,
char *out_blob, int *out_len)
{
struct ksmbd_spnego_authen_response *resp;
struct ksmbd_user *user = NULL;
int retval;
resp = ksmbd_ipc_spnego_authen_request(in_blob, in_len);
if (!resp) {
ksmbd_debug(AUTH, "SPNEGO_AUTHEN_REQUEST failure\n");
return -EINVAL;
}
if (!(resp->login_response.status & KSMBD_USER_FLAG_OK)) {
ksmbd_debug(AUTH, "krb5 authentication failure\n");
retval = -EPERM;
goto out;
}
if (*out_len <= resp->spnego_blob_len) {
ksmbd_debug(AUTH, "buf len %d, but blob len %d\n",
*out_len, resp->spnego_blob_len);
retval = -EINVAL;
goto out;
}
if (resp->session_key_len > sizeof(sess->sess_key)) {
ksmbd_debug(AUTH, "session key is too long\n");
retval = -EINVAL;
goto out;
}
user = ksmbd_alloc_user(&resp->login_response);
if (!user) {
ksmbd_debug(AUTH, "login failure\n");
retval = -ENOMEM;
goto out;
}
sess->user = user;
memcpy(sess->sess_key, resp->payload, resp->session_key_len);
memcpy(out_blob, resp->payload + resp->session_key_len,
resp->spnego_blob_len);
*out_len = resp->spnego_blob_len;
retval = 0;
out:
ksmbd_free(resp);
return retval;
}
#else
int ksmbd_krb5_authenticate(struct ksmbd_session *sess,
char *in_blob, int in_len,
char *out_blob, int *out_len)
{
return -EOPNOTSUPP;
}
#endif
/**
* ksmbd_sign_smb2_pdu() - function to generate packet signing
* @conn: connection
* @key: signing key
* @iov: buffer iov array
* @n_vec: number of iovecs
* @sig: signature value generated for client request packet
*
*/
int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn,
char *key,
struct kvec *iov,
int n_vec,
char *sig)
{
struct ksmbd_crypto_ctx *ctx;
int rc = -EINVAL;
int i;
ctx = ksmbd_crypto_ctx_find_hmacsha256();
if (!ctx) {
ksmbd_debug(AUTH, "could not crypto alloc hmacmd5 rc %d\n", rc);
goto out;
}
rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx),
key,
SMB2_NTLMV2_SESSKEY_SIZE);
if (rc)
goto out;
rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx));
if (rc) {
ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc);
goto out;
}
for (i = 0; i < n_vec; i++) {
rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx),
iov[i].iov_base,
iov[i].iov_len);
if (rc) {
ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc);
goto out;
}
}
rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig);
if (rc)
ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc);
out:
ksmbd_release_crypto_ctx(ctx);
return rc;
}
/**
* ksmbd_sign_smb3_pdu() - function to generate packet signing
* @conn: connection
* @key: signing key
* @iov: buffer iov array
* @n_vec: number of iovecs
* @sig: signature value generated for client request packet
*
*/
int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn,
char *key,
struct kvec *iov,
int n_vec,
char *sig)
{
struct ksmbd_crypto_ctx *ctx;
int rc = -EINVAL;
int i;
ctx = ksmbd_crypto_ctx_find_cmacaes();
if (!ctx) {
ksmbd_debug(AUTH, "could not crypto alloc cmac rc %d\n", rc);
goto out;
}
rc = crypto_shash_setkey(CRYPTO_CMACAES_TFM(ctx),
key,
SMB2_CMACAES_SIZE);
if (rc)
goto out;
rc = crypto_shash_init(CRYPTO_CMACAES(ctx));
if (rc) {
ksmbd_debug(AUTH, "cmaces init error %d\n", rc);
goto out;
}
for (i = 0; i < n_vec; i++) {
rc = crypto_shash_update(CRYPTO_CMACAES(ctx),
iov[i].iov_base,
iov[i].iov_len);
if (rc) {
ksmbd_debug(AUTH, "cmaces update error %d\n", rc);
goto out;
}
}
rc = crypto_shash_final(CRYPTO_CMACAES(ctx), sig);
if (rc)
ksmbd_debug(AUTH, "cmaces generation error %d\n", rc);
out:
ksmbd_release_crypto_ctx(ctx);
return rc;
}
struct derivation {
struct kvec label;
struct kvec context;
bool binding;
};
static int generate_key(struct ksmbd_session *sess, struct kvec label,
struct kvec context, __u8 *key, unsigned int key_size)
{
unsigned char zero = 0x0;
__u8 i[4] = {0, 0, 0, 1};
__u8 L[4] = {0, 0, 0, 128};
int rc = -EINVAL;
unsigned char prfhash[SMB2_HMACSHA256_SIZE];
unsigned char *hashptr = prfhash;
struct ksmbd_crypto_ctx *ctx;
memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
memset(key, 0x0, key_size);
ctx = ksmbd_crypto_ctx_find_hmacsha256();
if (!ctx) {
ksmbd_debug(AUTH, "could not crypto alloc hmacmd5 rc %d\n", rc);
goto smb3signkey_ret;
}
rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx),
sess->sess_key,
SMB2_NTLMV2_SESSKEY_SIZE);
if (rc)
goto smb3signkey_ret;
rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx));
if (rc) {
ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc);
goto smb3signkey_ret;
}
rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4);
if (rc) {
ksmbd_debug(AUTH, "could not update with n\n");
goto smb3signkey_ret;
}
rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx),
label.iov_base,
label.iov_len);
if (rc) {
ksmbd_debug(AUTH, "could not update with label\n");
goto smb3signkey_ret;
}
rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1);
if (rc) {
ksmbd_debug(AUTH, "could not update with zero\n");
goto smb3signkey_ret;
}
rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx),
context.iov_base,
context.iov_len);
if (rc) {
ksmbd_debug(AUTH, "could not update with context\n");
goto smb3signkey_ret;
}
rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L, 4);
if (rc) {
ksmbd_debug(AUTH, "could not update with L\n");
goto smb3signkey_ret;
}
rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr);
if (rc) {
ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n",
rc);
goto smb3signkey_ret;
}
memcpy(key, hashptr, key_size);
smb3signkey_ret:
ksmbd_release_crypto_ctx(ctx);
return rc;
}
static int generate_smb3signingkey(struct ksmbd_session *sess,
const struct derivation *signing)
{
int rc;
struct channel *chann;
char *key;
chann = lookup_chann_list(sess);
if (!chann)
return 0;
if (sess->conn->dialect >= SMB30_PROT_ID && signing->binding)
key = chann->smb3signingkey;
else
key = sess->smb3signingkey;
rc = generate_key(sess, signing->label, signing->context, key,
SMB3_SIGN_KEY_SIZE);
if (rc)
return rc;
if (!(sess->conn->dialect >= SMB30_PROT_ID && signing->binding))
memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE);
ksmbd_debug(AUTH, "dumping generated AES signing keys\n");
ksmbd_debug(AUTH, "Session Id %llu\n", sess->id);
ksmbd_debug(AUTH, "Session Key %*ph\n",
SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key);
ksmbd_debug(AUTH, "Signing Key %*ph\n",
SMB3_SIGN_KEY_SIZE, key);
return rc;
}
int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess)
{
struct derivation d;
d.label.iov_base = "SMB2AESCMAC";
d.label.iov_len = 12;
d.context.iov_base = "SmbSign";
d.context.iov_len = 8;
d.binding = false;
return generate_smb3signingkey(sess, &d);
}
int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess)
{
struct derivation d;
d.label.iov_base = "SMBSigningKey";
d.label.iov_len = 14;
d.context.iov_base = sess->Preauth_HashValue;
d.context.iov_len = 64;
d.binding = false;
return generate_smb3signingkey(sess, &d);
}
struct derivation_twin {
struct derivation encryption;
struct derivation decryption;
};
static int generate_smb3encryptionkey(struct ksmbd_session *sess,
const struct derivation_twin *ptwin)
{
int rc;
rc = generate_key(sess, ptwin->encryption.label,
ptwin->encryption.context, sess->smb3encryptionkey,
SMB3_SIGN_KEY_SIZE);
if (rc)
return rc;
rc = generate_key(sess, ptwin->decryption.label,
ptwin->decryption.context,
sess->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
if (rc)
return rc;
ksmbd_debug(AUTH, "dumping generated AES encryption keys\n");
ksmbd_debug(AUTH, "Session Id %llu\n", sess->id);
ksmbd_debug(AUTH, "Session Key %*ph\n",
SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key);
ksmbd_debug(AUTH, "ServerIn Key %*ph\n",
SMB3_SIGN_KEY_SIZE, sess->smb3encryptionkey);
ksmbd_debug(AUTH, "ServerOut Key %*ph\n",
SMB3_SIGN_KEY_SIZE, sess->smb3decryptionkey);
return rc;
}
int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess)
{
struct derivation_twin twin;
struct derivation *d;
d = &twin.encryption;
d->label.iov_base = "SMB2AESCCM";
d->label.iov_len = 11;
d->context.iov_base = "ServerOut";
d->context.iov_len = 10;
d = &twin.decryption;
d->label.iov_base = "SMB2AESCCM";
d->label.iov_len = 11;
d->context.iov_base = "ServerIn ";
d->context.iov_len = 10;
return generate_smb3encryptionkey(sess, &twin);
}
int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess)
{
struct derivation_twin twin;
struct derivation *d;
d = &twin.encryption;
d->label.iov_base = "SMBS2CCipherKey";
d->label.iov_len = 16;
d->context.iov_base = sess->Preauth_HashValue;
d->context.iov_len = 64;
d = &twin.decryption;
d->label.iov_base = "SMBC2SCipherKey";
d->label.iov_len = 16;
d->context.iov_base = sess->Preauth_HashValue;
d->context.iov_len = 64;
return generate_smb3encryptionkey(sess, &twin);
}
int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn,
char *buf,
__u8 *pi_hash)
{
int rc = -1;
struct smb2_hdr *rcv_hdr = (struct smb2_hdr *)buf;
char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId;
int msg_size = be32_to_cpu(rcv_hdr->smb2_buf_length);
struct ksmbd_crypto_ctx *ctx = NULL;
if (conn->preauth_info->Preauth_HashId ==
SMB2_PREAUTH_INTEGRITY_SHA512) {
ctx = ksmbd_crypto_ctx_find_sha512();
if (!ctx) {
ksmbd_debug(AUTH, "could not alloc sha512 rc %d\n", rc);
goto out;
}
} else
goto out;
rc = crypto_shash_init(CRYPTO_SHA512(ctx));
if (rc) {
ksmbd_debug(AUTH, "could not init shashn");
goto out;
}
rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64);
if (rc) {
ksmbd_debug(AUTH, "could not update with n\n");
goto out;
}
rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size);
if (rc) {
ksmbd_debug(AUTH, "could not update with n\n");
goto out;
}
rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash);
if (rc) {
ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc);
goto out;
}
out:
ksmbd_release_crypto_ctx(ctx);
return rc;
}
int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len,
__u8 *pi_hash)
{
int rc = -1;
struct ksmbd_crypto_ctx *ctx = NULL;
ctx = ksmbd_crypto_ctx_find_sha256();
if (!ctx) {
ksmbd_debug(AUTH, "could not alloc sha256 rc %d\n", rc);
goto out;
}
rc = crypto_shash_init(CRYPTO_SHA256(ctx));
if (rc) {
ksmbd_debug(AUTH, "could not init shashn");
goto out;
}
rc = crypto_shash_update(CRYPTO_SHA256(ctx), sd_buf, len);
if (rc) {
ksmbd_debug(AUTH, "could not update with n\n");
goto out;
}
rc = crypto_shash_final(CRYPTO_SHA256(ctx), pi_hash);
if (rc) {
ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc);
goto out;
}
out:
ksmbd_release_crypto_ctx(ctx);
return rc;
}
static int ksmbd_get_encryption_key(struct ksmbd_conn *conn,
__u64 ses_id,
int enc,
u8 *key)
{
struct ksmbd_session *sess;
u8 *ses_enc_key;
sess = ksmbd_session_lookup(conn, ses_id);
if (!sess)
return 1;
ses_enc_key = enc ? sess->smb3encryptionkey :
sess->smb3decryptionkey;
memcpy(key, ses_enc_key, SMB3_SIGN_KEY_SIZE);
return 0;
}
static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf,
unsigned int buflen)
{
void *addr;
if (is_vmalloc_addr(buf))
addr = vmalloc_to_page(buf);
else
addr = virt_to_page(buf);
sg_set_page(sg, addr, buflen, offset_in_page(buf));
}
static struct scatterlist *ksmbd_init_sg(struct kvec *iov,
unsigned int nvec,
u8 *sign)
{
struct scatterlist *sg;
unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24;
int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0;
for (i = 0; i < nvec - 1; i++) {
unsigned long kaddr = (unsigned long)iov[i + 1].iov_base;
if (is_vmalloc_addr(iov[i + 1].iov_base)) {
nr_entries[i] = ((kaddr + iov[i + 1].iov_len +
PAGE_SIZE - 1) >> PAGE_SHIFT) -
(kaddr >> PAGE_SHIFT);
} else
nr_entries[i]++;
total_entries += nr_entries[i];
}
/* Add two entries for transform header and signature */
total_entries += 2;
sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL);
if (!sg)
return NULL;
sg_init_table(sg, total_entries);
smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len);
for (i = 0; i < nvec - 1; i++) {
void *data = iov[i + 1].iov_base;
int len = iov[i + 1].iov_len;
if (is_vmalloc_addr(data)) {
int j, offset = offset_in_page(data);
for (j = 0; j < nr_entries[i]; j++) {
unsigned int bytes = PAGE_SIZE - offset;
if (len <= 0)
break;
if (bytes > len)
bytes = len;
sg_set_page(&sg[sg_idx++],
vmalloc_to_page(data), bytes,
offset_in_page(data));
data += bytes;
len -= bytes;
offset = 0;
}
} else {
sg_set_page(&sg[sg_idx++], virt_to_page(data), len,
offset_in_page(data));
}
}
smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE);
return sg;
}
int ksmbd_crypt_message(struct ksmbd_conn *conn,
struct kvec *iov,
unsigned int nvec,
int enc)
{
struct smb2_transform_hdr *tr_hdr =
(struct smb2_transform_hdr *)iov[0].iov_base;
unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24;
int rc = 0;
struct scatterlist *sg;
u8 sign[SMB2_SIGNATURE_SIZE] = {};
u8 key[SMB3_SIGN_KEY_SIZE];
struct aead_request *req;
char *iv;
unsigned int iv_len;
struct crypto_aead *tfm;
unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
struct ksmbd_crypto_ctx *ctx;
rc = ksmbd_get_encryption_key(conn,
le64_to_cpu(tr_hdr->SessionId),
enc,
key);
if (rc) {
ksmbd_err("Could not get %scryption key\n", enc ? "en" : "de");
return 0;
}
if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM)
ctx = ksmbd_crypto_ctx_find_gcm();
else
ctx = ksmbd_crypto_ctx_find_ccm();
if (!ctx) {
ksmbd_err("crypto alloc failed\n");
return -EINVAL;
}
if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM)
tfm = CRYPTO_GCM(ctx);
else
tfm = CRYPTO_CCM(ctx);
rc = crypto_aead_setkey(tfm, key, SMB3_SIGN_KEY_SIZE);
if (rc) {
ksmbd_err("Failed to set aead key %d\n", rc);
goto free_ctx;
}
rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE);
if (rc) {
ksmbd_err("Failed to set authsize %d\n", rc);
goto free_ctx;
}
req = aead_request_alloc(tfm, GFP_KERNEL);
if (!req) {
ksmbd_err("Failed to alloc aead request\n");
rc = -ENOMEM;
goto free_ctx;
}
if (!enc) {
memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE);
crypt_len += SMB2_SIGNATURE_SIZE;
}
sg = ksmbd_init_sg(iov, nvec, sign);
if (!sg) {
ksmbd_err("Failed to init sg\n");
rc = -ENOMEM;
goto free_req;
}
iv_len = crypto_aead_ivsize(tfm);
iv = kzalloc(iv_len, GFP_KERNEL);
if (!iv) {
ksmbd_err("Failed to alloc IV\n");
rc = -ENOMEM;
goto free_sg;
}
if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM)
memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES128GCM_NONCE);
else {
iv[0] = 3;
memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES128CCM_NONCE);
}
aead_request_set_crypt(req, sg, sg, crypt_len, iv);
aead_request_set_ad(req, assoc_data_len);
aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
if (enc)
rc = crypto_aead_encrypt(req);
else
rc = crypto_aead_decrypt(req);
if (!rc && enc)
memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE);
kfree(iv);
free_sg:
kfree(sg);
free_req:
kfree(req);
free_ctx:
ksmbd_release_crypto_ctx(ctx);
return rc;
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#ifndef __AUTH_H__
#define __AUTH_H__
#include "ntlmssp.h"
#ifdef CONFIG_SMB_SERVER_KERBEROS5
#define AUTH_GSS_LENGTH 96
#define AUTH_GSS_PADDING 0
#else
#define AUTH_GSS_LENGTH 74
#define AUTH_GSS_PADDING 6
#endif
#define CIFS_HMAC_MD5_HASH_SIZE (16)
#define CIFS_NTHASH_SIZE (16)
/*
* Size of the ntlm client response
*/
#define CIFS_AUTH_RESP_SIZE 24
#define CIFS_SMB1_SIGNATURE_SIZE 8
#define CIFS_SMB1_SESSKEY_SIZE 16
#define KSMBD_AUTH_NTLMSSP 0x0001
#define KSMBD_AUTH_KRB5 0x0002
#define KSMBD_AUTH_MSKRB5 0x0004
#define KSMBD_AUTH_KRB5U2U 0x0008
struct ksmbd_session;
struct ksmbd_conn;
struct kvec;
int ksmbd_crypt_message(struct ksmbd_conn *conn,
struct kvec *iov,
unsigned int nvec,
int enc);
void ksmbd_copy_gss_neg_header(void *buf);
int ksmbd_auth_ntlm(struct ksmbd_session *sess,
char *pw_buf);
int ksmbd_auth_ntlmv2(struct ksmbd_session *sess,
struct ntlmv2_resp *ntlmv2,
int blen,
char *domain_name);
int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
int blob_len,
struct ksmbd_session *sess);
int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob,
int blob_len,
struct ksmbd_session *sess);
unsigned int
ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob,
struct ksmbd_session *sess);
int ksmbd_krb5_authenticate(struct ksmbd_session *sess,
char *in_blob, int in_len,
char *out_blob, int *out_len);
int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn,
char *key,
struct kvec *iov,
int n_vec,
char *sig);
int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn,
char *key,
struct kvec *iov,
int n_vec,
char *sig);
int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess);
int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess);
int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess);
int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess);
int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn,
char *buf,
__u8 *pi_hash);
int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len,
__u8 *pi_hash);
#endif
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/version.h>
#include "glob.h"
#include "crypto_ctx.h"
#include "buffer_pool.h"
struct crypto_ctx_list {
spinlock_t ctx_lock;
int avail_ctx;
struct list_head idle_ctx;
wait_queue_head_t ctx_wait;
};
static struct crypto_ctx_list ctx_list;
static inline void free_aead(struct crypto_aead *aead)
{
if (aead)
crypto_free_aead(aead);
}
static void free_shash(struct shash_desc *shash)
{
if (shash) {
crypto_free_shash(shash->tfm);
kfree(shash);
}
}
static struct crypto_aead *alloc_aead(int id)
{
struct crypto_aead *tfm = NULL;
switch (id) {
case CRYPTO_AEAD_AES128_GCM:
tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
break;
case CRYPTO_AEAD_AES128_CCM:
tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
break;
default:
ksmbd_err("Does not support encrypt ahead(id : %d)\n", id);
return NULL;
}
if (IS_ERR(tfm)) {
ksmbd_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm));
return NULL;
}
return tfm;
}
static struct shash_desc *alloc_shash_desc(int id)
{
struct crypto_shash *tfm = NULL;
struct shash_desc *shash;
switch (id) {
case CRYPTO_SHASH_HMACMD5:
tfm = crypto_alloc_shash("hmac(md5)", 0, 0);
break;
case CRYPTO_SHASH_HMACSHA256:
tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
break;
case CRYPTO_SHASH_CMACAES:
tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
break;
case CRYPTO_SHASH_SHA256:
tfm = crypto_alloc_shash("sha256", 0, 0);
break;
case CRYPTO_SHASH_SHA512:
tfm = crypto_alloc_shash("sha512", 0, 0);
break;
case CRYPTO_SHASH_MD4:
tfm = crypto_alloc_shash("md4", 0, 0);
break;
case CRYPTO_SHASH_MD5:
tfm = crypto_alloc_shash("md5", 0, 0);
break;
}
if (IS_ERR(tfm))
return NULL;
shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
GFP_KERNEL);
if (!shash)
crypto_free_shash(tfm);
else
shash->tfm = tfm;
return shash;
}
static struct ksmbd_crypto_ctx *ctx_alloc(void)
{
return ksmbd_alloc(sizeof(struct ksmbd_crypto_ctx));
}
static void ctx_free(struct ksmbd_crypto_ctx *ctx)
{
int i;
for (i = 0; i < CRYPTO_SHASH_MAX; i++)
free_shash(ctx->desc[i]);
for (i = 0; i < CRYPTO_AEAD_MAX; i++)
free_aead(ctx->ccmaes[i]);
ksmbd_free(ctx);
}
static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void)
{
struct ksmbd_crypto_ctx *ctx;
while (1) {
spin_lock(&ctx_list.ctx_lock);
if (!list_empty(&ctx_list.idle_ctx)) {
ctx = list_entry(ctx_list.idle_ctx.next,
struct ksmbd_crypto_ctx,
list);
list_del(&ctx->list);
spin_unlock(&ctx_list.ctx_lock);
return ctx;
}
if (ctx_list.avail_ctx > num_online_cpus()) {
spin_unlock(&ctx_list.ctx_lock);
wait_event(ctx_list.ctx_wait,
!list_empty(&ctx_list.idle_ctx));
continue;
}
ctx_list.avail_ctx++;
spin_unlock(&ctx_list.ctx_lock);
ctx = ctx_alloc();
if (!ctx) {
spin_lock(&ctx_list.ctx_lock);
ctx_list.avail_ctx--;
spin_unlock(&ctx_list.ctx_lock);
wait_event(ctx_list.ctx_wait,
!list_empty(&ctx_list.idle_ctx));
continue;
}
break;
}
return ctx;
}
void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx)
{
if (!ctx)
return;
spin_lock(&ctx_list.ctx_lock);
if (ctx_list.avail_ctx <= num_online_cpus()) {
list_add(&ctx->list, &ctx_list.idle_ctx);
spin_unlock(&ctx_list.ctx_lock);
wake_up(&ctx_list.ctx_wait);
return;
}
ctx_list.avail_ctx--;
spin_unlock(&ctx_list.ctx_lock);
ctx_free(ctx);
}
static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id)
{
struct ksmbd_crypto_ctx *ctx;
if (id >= CRYPTO_SHASH_MAX)
return NULL;
ctx = ksmbd_find_crypto_ctx();
if (ctx->desc[id])
return ctx;
ctx->desc[id] = alloc_shash_desc(id);
if (ctx->desc[id])
return ctx;
ksmbd_release_crypto_ctx(ctx);
return NULL;
}
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void)
{
return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5);
}
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void)
{
return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256);
}
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void)
{
return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES);
}
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void)
{
return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA256);
}
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void)
{
return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512);
}
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void)
{
return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD4);
}
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void)
{
return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD5);
}
static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id)
{
struct ksmbd_crypto_ctx *ctx;
if (id >= CRYPTO_AEAD_MAX)
return NULL;
ctx = ksmbd_find_crypto_ctx();
if (ctx->ccmaes[id])
return ctx;
ctx->ccmaes[id] = alloc_aead(id);
if (ctx->ccmaes[id])
return ctx;
ksmbd_release_crypto_ctx(ctx);
return NULL;
}
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void)
{
return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES128_GCM);
}
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void)
{
return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES128_CCM);
}
void ksmbd_crypto_destroy(void)
{
struct ksmbd_crypto_ctx *ctx;
while (!list_empty(&ctx_list.idle_ctx)) {
ctx = list_entry(ctx_list.idle_ctx.next,
struct ksmbd_crypto_ctx,
list);
list_del(&ctx->list);
ctx_free(ctx);
}
}
int ksmbd_crypto_create(void)
{
struct ksmbd_crypto_ctx *ctx;
spin_lock_init(&ctx_list.ctx_lock);
INIT_LIST_HEAD(&ctx_list.idle_ctx);
init_waitqueue_head(&ctx_list.ctx_wait);
ctx_list.avail_ctx = 1;
ctx = ctx_alloc();
if (!ctx)
return -ENOMEM;
list_add(&ctx->list, &ctx_list.idle_ctx);
return 0;
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
*/
#ifndef __CRYPTO_CTX_H__
#define __CRYPTO_CTX_H__
#include <crypto/hash.h>
#include <crypto/aead.h>
enum {
CRYPTO_SHASH_HMACMD5 = 0,
CRYPTO_SHASH_HMACSHA256,
CRYPTO_SHASH_CMACAES,
CRYPTO_SHASH_SHA256,
CRYPTO_SHASH_SHA512,
CRYPTO_SHASH_MD4,
CRYPTO_SHASH_MD5,
CRYPTO_SHASH_MAX,
};
enum {
CRYPTO_AEAD_AES128_GCM = 16,
CRYPTO_AEAD_AES128_CCM,
CRYPTO_AEAD_MAX,
};
enum {
CRYPTO_BLK_ECBDES = 32,
CRYPTO_BLK_MAX,
};
struct ksmbd_crypto_ctx {
struct list_head list;
struct shash_desc *desc[CRYPTO_SHASH_MAX];
struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX];
};
#define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5])
#define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256])
#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES])
#define CRYPTO_SHA256(c) ((c)->desc[CRYPTO_SHASH_SHA256])
#define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512])
#define CRYPTO_MD4(c) ((c)->desc[CRYPTO_SHASH_MD4])
#define CRYPTO_MD5(c) ((c)->desc[CRYPTO_SHASH_MD5])
#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm)
#define CRYPTO_HMACSHA256_TFM(c)\
((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm)
#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm)
#define CRYPTO_SHA256_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA256]->tfm)
#define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm)
#define CRYPTO_MD4_TFM(c) ((c)->desc[CRYPTO_SHASH_MD4]->tfm)
#define CRYPTO_MD5_TFM(c) ((c)->desc[CRYPTO_SHASH_MD5]->tfm)
#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES128_GCM])
#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES128_CCM])
void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void);
void ksmbd_crypto_destroy(void);
int ksmbd_crypto_create(void);
#endif /* __CRYPTO_CTX_H__ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#include "ksmbd_ida.h"
struct ksmbd_ida *ksmbd_ida_alloc(void)
{
struct ksmbd_ida *ida;
ida = kmalloc(sizeof(struct ksmbd_ida), GFP_KERNEL);
if (!ida)
return NULL;
ida_init(&ida->map);
return ida;
}
void ksmbd_ida_free(struct ksmbd_ida *ida)
{
if (!ida)
return;
ida_destroy(&ida->map);
kfree(ida);
}
static inline int __acquire_id(struct ksmbd_ida *ida, int from, int to)
{
return ida_simple_get(&ida->map, from, to, GFP_KERNEL);
}
int ksmbd_acquire_smb2_tid(struct ksmbd_ida *ida)
{
int id;
do {
id = __acquire_id(ida, 0, 0);
} while (id == 0xFFFF);
return id;
}
int ksmbd_acquire_smb2_uid(struct ksmbd_ida *ida)
{
int id;
do {
id = __acquire_id(ida, 1, 0);
} while (id == 0xFFFE);
return id;
}
int ksmbd_acquire_async_msg_id(struct ksmbd_ida *ida)
{
return __acquire_id(ida, 1, 0);
}
int ksmbd_acquire_id(struct ksmbd_ida *ida)
{
return __acquire_id(ida, 0, 0);
}
void ksmbd_release_id(struct ksmbd_ida *ida, int id)
{
ida_simple_remove(&ida->map, id);
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#ifndef __KSMBD_IDA_MANAGEMENT_H__
#define __KSMBD_IDA_MANAGEMENT_H__
#include <linux/slab.h>
#include <linux/idr.h>
struct ksmbd_ida {
struct ida map;
};
struct ksmbd_ida *ksmbd_ida_alloc(void);
void ksmbd_ida_free(struct ksmbd_ida *ida);
/*
* 2.2.1.6.7 TID Generation
* The value 0xFFFF MUST NOT be used as a valid TID. All other
* possible values for TID, including zero (0x0000), are valid.
* The value 0xFFFF is used to specify all TIDs or no TID,
* depending upon the context in which it is used.
*/
int ksmbd_acquire_smb2_tid(struct ksmbd_ida *ida);
/*
* 2.2.1.6.8 UID Generation
* The value 0xFFFE was declared reserved in the LAN Manager 1.0
* documentation, so a value of 0xFFFE SHOULD NOT be used as a
* valid UID.<21> All other possible values for a UID, excluding
* zero (0x0000), are valid.
*/
int ksmbd_acquire_smb2_uid(struct ksmbd_ida *ida);
int ksmbd_acquire_async_msg_id(struct ksmbd_ida *ida);
int ksmbd_acquire_id(struct ksmbd_ida *ida);
void ksmbd_release_id(struct ksmbd_ida *ida, int id);
#endif /* __KSMBD_IDA_MANAGEMENT_H__ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#include <linux/list.h>
#include <linux/jhash.h>
#include <linux/slab.h>
#include <linux/rwsem.h>
#include <linux/parser.h>
#include <linux/namei.h>
#include <linux/sched.h>
#include "share_config.h"
#include "user_config.h"
#include "user_session.h"
#include "../buffer_pool.h"
#include "../transport_ipc.h"
#include "../ksmbd_server.h" /* FIXME */
#define SHARE_HASH_BITS 3
static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS);
static DECLARE_RWSEM(shares_table_lock);
struct ksmbd_veto_pattern {
char *pattern;
struct list_head list;
};
static unsigned int share_name_hash(char *name)
{
return jhash(name, strlen(name), 0);
}
static void kill_share(struct ksmbd_share_config *share)
{
while (!list_empty(&share->veto_list)) {
struct ksmbd_veto_pattern *p;
p = list_entry(share->veto_list.next,
struct ksmbd_veto_pattern,
list);
list_del(&p->list);
kfree(p->pattern);
kfree(p);
}
if (share->path)
path_put(&share->vfs_path);
kfree(share->name);
kfree(share->path);
kfree(share);
}
void __ksmbd_share_config_put(struct ksmbd_share_config *share)
{
down_write(&shares_table_lock);
hash_del(&share->hlist);
up_write(&shares_table_lock);
kill_share(share);
}
static struct ksmbd_share_config *
__get_share_config(struct ksmbd_share_config *share)
{
if (!atomic_inc_not_zero(&share->refcount))
return NULL;
return share;
}
static struct ksmbd_share_config *__share_lookup(char *name)
{
struct ksmbd_share_config *share;
unsigned int key = share_name_hash(name);
hash_for_each_possible(shares_table, share, hlist, key) {
if (!strcmp(name, share->name))
return share;
}
return NULL;
}
static int parse_veto_list(struct ksmbd_share_config *share,
char *veto_list,
int veto_list_sz)
{
int sz = 0;
if (!veto_list_sz)
return 0;
while (veto_list_sz > 0) {
struct ksmbd_veto_pattern *p;
p = ksmbd_alloc(sizeof(struct ksmbd_veto_pattern));
if (!p)
return -ENOMEM;
sz = strlen(veto_list);
if (!sz)
break;
p->pattern = kstrdup(veto_list, GFP_KERNEL);
if (!p->pattern) {
ksmbd_free(p);
return -ENOMEM;
}
list_add(&p->list, &share->veto_list);
veto_list += sz + 1;
veto_list_sz -= (sz + 1);
}
return 0;
}
static struct ksmbd_share_config *share_config_request(char *name)
{
struct ksmbd_share_config_response *resp;
struct ksmbd_share_config *share = NULL;
struct ksmbd_share_config *lookup;
int ret;
resp = ksmbd_ipc_share_config_request(name);
if (!resp)
return NULL;
if (resp->flags == KSMBD_SHARE_FLAG_INVALID)
goto out;
share = ksmbd_alloc(sizeof(struct ksmbd_share_config));
if (!share)
goto out;
share->flags = resp->flags;
atomic_set(&share->refcount, 1);
INIT_LIST_HEAD(&share->veto_list);
share->name = kstrdup(name, GFP_KERNEL);
if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
share->path = kstrdup(KSMBD_SHARE_CONFIG_PATH(resp),
GFP_KERNEL);
if (share->path)
share->path_sz = strlen(share->path);
share->create_mask = resp->create_mask;
share->directory_mask = resp->directory_mask;
share->force_create_mode = resp->force_create_mode;
share->force_directory_mode = resp->force_directory_mode;
share->force_uid = resp->force_uid;
share->force_gid = resp->force_gid;
ret = parse_veto_list(share,
KSMBD_SHARE_CONFIG_VETO_LIST(resp),
resp->veto_list_sz);
if (!ret && share->path) {
ret = kern_path(share->path, 0, &share->vfs_path);
if (ret) {
ksmbd_debug(SMB, "failed to access '%s'\n",
share->path);
/* Avoid put_path() */
kfree(share->path);
share->path = NULL;
}
}
if (ret || !share->name) {
kill_share(share);
share = NULL;
goto out;
}
}
down_write(&shares_table_lock);
lookup = __share_lookup(name);
if (lookup)
lookup = __get_share_config(lookup);
if (!lookup) {
hash_add(shares_table, &share->hlist, share_name_hash(name));
} else {
kill_share(share);
share = lookup;
}
up_write(&shares_table_lock);
out:
ksmbd_free(resp);
return share;
}
static void strtolower(char *share_name)
{
while (*share_name) {
*share_name = tolower(*share_name);
share_name++;
}
}
struct ksmbd_share_config *ksmbd_share_config_get(char *name)
{
struct ksmbd_share_config *share;
strtolower(name);
down_read(&shares_table_lock);
share = __share_lookup(name);
if (share)
share = __get_share_config(share);
up_read(&shares_table_lock);
if (share)
return share;
return share_config_request(name);
}
bool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
const char *filename)
{
struct ksmbd_veto_pattern *p;
list_for_each_entry(p, &share->veto_list, list) {
if (match_wildcard(p->pattern, filename))
return true;
}
return false;
}
void ksmbd_share_configs_cleanup(void)
{
struct ksmbd_share_config *share;
struct hlist_node *tmp;
int i;
down_write(&shares_table_lock);
hash_for_each_safe(shares_table, i, tmp, share, hlist) {
hash_del(&share->hlist);
kill_share(share);
}
up_write(&shares_table_lock);
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#ifndef __SHARE_CONFIG_MANAGEMENT_H__
#define __SHARE_CONFIG_MANAGEMENT_H__
#include <linux/workqueue.h>
#include <linux/hashtable.h>
#include <linux/path.h>
#include "../glob.h" /* FIXME */
struct ksmbd_share_config {
char *name;
char *path;
unsigned int path_sz;
unsigned int flags;
struct list_head veto_list;
struct path vfs_path;
atomic_t refcount;
struct hlist_node hlist;
unsigned short create_mask;
unsigned short directory_mask;
unsigned short force_create_mode;
unsigned short force_directory_mode;
unsigned short force_uid;
unsigned short force_gid;
};
#define KSMBD_SHARE_INVALID_UID ((__u16)-1)
#define KSMBD_SHARE_INVALID_GID ((__u16)-1)
static inline int share_config_create_mode(struct ksmbd_share_config *share,
umode_t posix_mode)
{
if (!share->force_create_mode) {
if (!posix_mode)
return share->create_mask;
else
return posix_mode & share->create_mask;
}
return share->force_create_mode & share->create_mask;
}
static inline int share_config_directory_mode(struct ksmbd_share_config *share,
umode_t posix_mode)
{
if (!share->force_directory_mode) {
if (!posix_mode)
return share->directory_mask;
else
return posix_mode & share->directory_mask;
}
return share->force_directory_mode & share->directory_mask;
}
static inline int test_share_config_flag(struct ksmbd_share_config *share,
int flag)
{
return share->flags & flag;
}
extern void __ksmbd_share_config_put(struct ksmbd_share_config *share);
static inline void ksmbd_share_config_put(struct ksmbd_share_config *share)
{
if (!atomic_dec_and_test(&share->refcount))
return;
__ksmbd_share_config_put(share);
}
struct ksmbd_share_config *ksmbd_share_config_get(char *name);
bool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
const char *filename);
void ksmbd_share_configs_cleanup(void);
#endif /* __SHARE_CONFIG_MANAGEMENT_H__ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#include <linux/list.h>
#include <linux/slab.h>
#include "../ksmbd_server.h" /* FIXME */
#include "../buffer_pool.h"
#include "../transport_ipc.h"
#include "../connection.h"
#include "tree_connect.h"
#include "user_config.h"
#include "share_config.h"
#include "user_session.h"
struct ksmbd_tree_conn_status
ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name)
{
struct ksmbd_tree_conn_status status = {-EINVAL, NULL};
struct ksmbd_tree_connect_response *resp = NULL;
struct ksmbd_share_config *sc;
struct ksmbd_tree_connect *tree_conn = NULL;
struct sockaddr *peer_addr;
sc = ksmbd_share_config_get(share_name);
if (!sc)
return status;
tree_conn = ksmbd_alloc(sizeof(struct ksmbd_tree_connect));
if (!tree_conn) {
status.ret = -ENOMEM;
goto out_error;
}
tree_conn->id = ksmbd_acquire_tree_conn_id(sess);
if (tree_conn->id < 0) {
status.ret = -EINVAL;
goto out_error;
}
peer_addr = KSMBD_TCP_PEER_SOCKADDR(sess->conn);
resp = ksmbd_ipc_tree_connect_request(sess,
sc,
tree_conn,
peer_addr);
if (!resp) {
status.ret = -EINVAL;
goto out_error;
}
status.ret = resp->status;
if (status.ret != KSMBD_TREE_CONN_STATUS_OK)
goto out_error;
tree_conn->flags = resp->connection_flags;
tree_conn->user = sess->user;
tree_conn->share_conf = sc;
status.tree_conn = tree_conn;
list_add(&tree_conn->list, &sess->tree_conn_list);
ksmbd_free(resp);
return status;
out_error:
if (tree_conn)
ksmbd_release_tree_conn_id(sess, tree_conn->id);
ksmbd_share_config_put(sc);
ksmbd_free(tree_conn);
ksmbd_free(resp);
return status;
}
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
struct ksmbd_tree_connect *tree_conn)
{
int ret;
ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
ksmbd_release_tree_conn_id(sess, tree_conn->id);
list_del(&tree_conn->list);
ksmbd_share_config_put(tree_conn->share_conf);
ksmbd_free(tree_conn);
return ret;
}
struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
unsigned int id)
{
struct ksmbd_tree_connect *tree_conn;
struct list_head *tmp;
list_for_each(tmp, &sess->tree_conn_list) {
tree_conn = list_entry(tmp, struct ksmbd_tree_connect, list);
if (tree_conn->id == id)
return tree_conn;
}
return NULL;
}
struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess,
unsigned int id)
{
struct ksmbd_tree_connect *tc;
tc = ksmbd_tree_conn_lookup(sess, id);
if (tc)
return tc->share_conf;
return NULL;
}
int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
{
int ret = 0;
while (!list_empty(&sess->tree_conn_list)) {
struct ksmbd_tree_connect *tc;
tc = list_entry(sess->tree_conn_list.next,
struct ksmbd_tree_connect,
list);
ret |= ksmbd_tree_conn_disconnect(sess, tc);
}
return ret;
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#ifndef __TREE_CONNECT_MANAGEMENT_H__
#define __TREE_CONNECT_MANAGEMENT_H__
#include <linux/hashtable.h>
#include "../ksmbd_server.h" /* FIXME */
struct ksmbd_share_config;
struct ksmbd_user;
struct ksmbd_tree_connect {
int id;
unsigned int flags;
struct ksmbd_share_config *share_conf;
struct ksmbd_user *user;
struct list_head list;
int maximal_access;
bool posix_extensions;
};
struct ksmbd_tree_conn_status {
unsigned int ret;
struct ksmbd_tree_connect *tree_conn;
};
static inline int test_tree_conn_flag(struct ksmbd_tree_connect *tree_conn,
int flag)
{
return tree_conn->flags & flag;
}
struct ksmbd_session;
struct ksmbd_tree_conn_status
ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name);
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
struct ksmbd_tree_connect *tree_conn);
struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
unsigned int id);
struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess,
unsigned int id);
int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess);
#endif /* __TREE_CONNECT_MANAGEMENT_H__ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#include <linux/slab.h>
#include "user_config.h"
#include "../buffer_pool.h"
#include "../transport_ipc.h"
#include "../ksmbd_server.h" /* FIXME */
struct ksmbd_user *ksmbd_login_user(const char *account)
{
struct ksmbd_login_response *resp;
struct ksmbd_user *user = NULL;
resp = ksmbd_ipc_login_request(account);
if (!resp)
return NULL;
if (!(resp->status & KSMBD_USER_FLAG_OK))
goto out;
user = ksmbd_alloc_user(resp);
out:
ksmbd_free(resp);
return user;
}
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp)
{
struct ksmbd_user *user = NULL;
user = ksmbd_alloc(sizeof(struct ksmbd_user));
if (!user)
return NULL;
user->name = kstrdup(resp->account, GFP_KERNEL);
user->flags = resp->status;
user->gid = resp->gid;
user->uid = resp->uid;
user->passkey_sz = resp->hash_sz;
user->passkey = ksmbd_alloc(resp->hash_sz);
if (user->passkey)
memcpy(user->passkey, resp->hash, resp->hash_sz);
if (!user->name || !user->passkey) {
kfree(user->name);
ksmbd_free(user->passkey);
ksmbd_free(user);
user = NULL;
}
return user;
}
void ksmbd_free_user(struct ksmbd_user *user)
{
ksmbd_ipc_logout_request(user->name);
kfree(user->name);
ksmbd_free(user->passkey);
ksmbd_free(user);
}
int ksmbd_anonymous_user(struct ksmbd_user *user)
{
if (user->name[0] == '\0')
return 1;
return 0;
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#ifndef __USER_CONFIG_MANAGEMENT_H__
#define __USER_CONFIG_MANAGEMENT_H__
#include "../glob.h" /* FIXME */
#include "../ksmbd_server.h" /* FIXME */
struct ksmbd_user {
unsigned short flags;
unsigned int uid;
unsigned int gid;
char *name;
size_t passkey_sz;
char *passkey;
};
static inline bool user_guest(struct ksmbd_user *user)
{
return user->flags & KSMBD_USER_FLAG_GUEST_ACCOUNT;
}
static inline void set_user_flag(struct ksmbd_user *user, int flag)
{
user->flags |= flag;
}
static inline int test_user_flag(struct ksmbd_user *user, int flag)
{
return user->flags & flag;
}
static inline void set_user_guest(struct ksmbd_user *user)
{
}
static inline char *user_passkey(struct ksmbd_user *user)
{
return user->passkey;
}
static inline char *user_name(struct ksmbd_user *user)
{
return user->name;
}
static inline unsigned int user_uid(struct ksmbd_user *user)
{
return user->uid;
}
static inline unsigned int user_gid(struct ksmbd_user *user)
{
return user->gid;
}
struct ksmbd_user *ksmbd_login_user(const char *account);
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp);
void ksmbd_free_user(struct ksmbd_user *user);
int ksmbd_anonymous_user(struct ksmbd_user *user);
#endif /* __USER_CONFIG_MANAGEMENT_H__ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/rwsem.h>
#include "ksmbd_ida.h"
#include "user_session.h"
#include "user_config.h"
#include "tree_connect.h"
#include "../transport_ipc.h"
#include "../connection.h"
#include "../buffer_pool.h"
#include "../ksmbd_server.h" /* FIXME */
#include "../vfs_cache.h"
static struct ksmbd_ida *session_ida;
#define SESSION_HASH_BITS 3
static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS);
static DECLARE_RWSEM(sessions_table_lock);
struct ksmbd_session_rpc {
int id;
unsigned int method;
struct list_head list;
};
static void free_channel_list(struct ksmbd_session *sess)
{
struct channel *chann;
struct list_head *tmp, *t;
list_for_each_safe(tmp, t, &sess->ksmbd_chann_list) {
chann = list_entry(tmp, struct channel, chann_list);
if (chann) {
list_del(&chann->chann_list);
kfree(chann);
}
}
}
static void __session_rpc_close(struct ksmbd_session *sess,
struct ksmbd_session_rpc *entry)
{
struct ksmbd_rpc_command *resp;
resp = ksmbd_rpc_close(sess, entry->id);
if (!resp)
pr_err("Unable to close RPC pipe %d\n", entry->id);
ksmbd_free(resp);
ksmbd_rpc_id_free(entry->id);
ksmbd_free(entry);
}
static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess)
{
struct ksmbd_session_rpc *entry;
while (!list_empty(&sess->rpc_handle_list)) {
entry = list_entry(sess->rpc_handle_list.next,
struct ksmbd_session_rpc,
list);
list_del(&entry->list);
__session_rpc_close(sess, entry);
}
}
static int __rpc_method(char *rpc_name)
{
if (!strcmp(rpc_name, "\\srvsvc") || !strcmp(rpc_name, "srvsvc"))
return KSMBD_RPC_SRVSVC_METHOD_INVOKE;
if (!strcmp(rpc_name, "\\wkssvc") || !strcmp(rpc_name, "wkssvc"))
return KSMBD_RPC_WKSSVC_METHOD_INVOKE;
if (!strcmp(rpc_name, "LANMAN") || !strcmp(rpc_name, "lanman"))
return KSMBD_RPC_RAP_METHOD;
if (!strcmp(rpc_name, "\\samr") || !strcmp(rpc_name, "samr"))
return KSMBD_RPC_SAMR_METHOD_INVOKE;
if (!strcmp(rpc_name, "\\lsarpc") || !strcmp(rpc_name, "lsarpc"))
return KSMBD_RPC_LSARPC_METHOD_INVOKE;
ksmbd_err("Unsupported RPC: %s\n", rpc_name);
return 0;
}
int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
{
struct ksmbd_session_rpc *entry;
struct ksmbd_rpc_command *resp;
int method;
method = __rpc_method(rpc_name);
if (!method)
return -EINVAL;
entry = ksmbd_alloc(sizeof(struct ksmbd_session_rpc));
if (!entry)
return -EINVAL;
list_add(&entry->list, &sess->rpc_handle_list);
entry->method = method;
entry->id = ksmbd_ipc_id_alloc();
if (entry->id < 0)
goto error;
resp = ksmbd_rpc_open(sess, entry->id);
if (!resp)
goto error;
ksmbd_free(resp);
return entry->id;
error:
list_del(&entry->list);
ksmbd_free(entry);
return -EINVAL;
}
void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id)
{
struct ksmbd_session_rpc *entry;
list_for_each_entry(entry, &sess->rpc_handle_list, list) {
if (entry->id == id) {
list_del(&entry->list);
__session_rpc_close(sess, entry);
break;
}
}
}
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id)
{
struct ksmbd_session_rpc *entry;
list_for_each_entry(entry, &sess->rpc_handle_list, list) {
if (entry->id == id)
return entry->method;
}
return 0;
}
void ksmbd_session_destroy(struct ksmbd_session *sess)
{
if (!sess)
return;
if (!atomic_dec_and_test(&sess->refcnt))
return;
list_del(&sess->sessions_entry);
if (IS_SMB2(sess->conn)) {
down_write(&sessions_table_lock);
hash_del(&sess->hlist);
up_write(&sessions_table_lock);
}
if (sess->user)
ksmbd_free_user(sess->user);
ksmbd_tree_conn_session_logoff(sess);
ksmbd_destroy_file_table(&sess->file_table);
ksmbd_session_rpc_clear_list(sess);
free_channel_list(sess);
kfree(sess->Preauth_HashValue);
ksmbd_release_id(session_ida, sess->id);
ksmbd_ida_free(sess->tree_conn_ida);
ksmbd_free(sess);
}
static struct ksmbd_session *__session_lookup(unsigned long long id)
{
struct ksmbd_session *sess;
hash_for_each_possible(sessions_table, sess, hlist, id) {
if (id == sess->id)
return sess;
}
return NULL;
}
void ksmbd_session_register(struct ksmbd_conn *conn,
struct ksmbd_session *sess)
{
sess->conn = conn;
list_add(&sess->sessions_entry, &conn->sessions);
}
void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
{
struct ksmbd_session *sess;
while (!list_empty(&conn->sessions)) {
sess = list_entry(conn->sessions.next,
struct ksmbd_session,
sessions_entry);
ksmbd_session_destroy(sess);
}
}
bool ksmbd_session_id_match(struct ksmbd_session *sess, unsigned long long id)
{
return sess->id == id;
}
struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
unsigned long long id)
{
struct ksmbd_session *sess = NULL;
list_for_each_entry(sess, &conn->sessions, sessions_entry) {
if (ksmbd_session_id_match(sess, id))
return sess;
}
return NULL;
}
int get_session(struct ksmbd_session *sess)
{
return atomic_inc_not_zero(&sess->refcnt);
}
void put_session(struct ksmbd_session *sess)
{
if (atomic_dec_and_test(&sess->refcnt))
ksmbd_err("get/%s seems to be mismatched.", __func__);
}
struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
{
struct ksmbd_session *sess;
down_read(&sessions_table_lock);
sess = __session_lookup(id);
if (sess) {
if (!get_session(sess))
sess = NULL;
}
up_read(&sessions_table_lock);
return sess;
}
static int __init_smb2_session(struct ksmbd_session *sess)
{
int id = ksmbd_acquire_smb2_uid(session_ida);
if (id < 0)
return -EINVAL;
sess->id = id;
return 0;
}
static struct ksmbd_session *__session_create(int protocol)
{
struct ksmbd_session *sess;
int ret;
sess = ksmbd_alloc(sizeof(struct ksmbd_session));
if (!sess)
return NULL;
if (ksmbd_init_file_table(&sess->file_table))
goto error;
set_session_flag(sess, protocol);
INIT_LIST_HEAD(&sess->sessions_entry);
INIT_LIST_HEAD(&sess->tree_conn_list);
INIT_LIST_HEAD(&sess->ksmbd_chann_list);
INIT_LIST_HEAD(&sess->rpc_handle_list);
sess->sequence_number = 1;
atomic_set(&sess->refcnt, 1);
switch (protocol) {
case CIFDS_SESSION_FLAG_SMB2:
ret = __init_smb2_session(sess);
break;
default:
ret = -EINVAL;
break;
}
if (ret)
goto error;
sess->tree_conn_ida = ksmbd_ida_alloc();
if (!sess->tree_conn_ida)
goto error;
if (protocol == CIFDS_SESSION_FLAG_SMB2) {
down_read(&sessions_table_lock);
hash_add(sessions_table, &sess->hlist, sess->id);
up_read(&sessions_table_lock);
}
return sess;
error:
ksmbd_session_destroy(sess);
return NULL;
}
struct ksmbd_session *ksmbd_smb2_session_create(void)
{
return __session_create(CIFDS_SESSION_FLAG_SMB2);
}
int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess)
{
int id = -EINVAL;
if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2))
id = ksmbd_acquire_smb2_tid(sess->tree_conn_ida);
return id;
}
void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id)
{
if (id >= 0)
ksmbd_release_id(sess->tree_conn_ida, id);
}
int ksmbd_init_session_table(void)
{
session_ida = ksmbd_ida_alloc();
if (!session_ida)
return -ENOMEM;
return 0;
}
void ksmbd_free_session_table(void)
{
ksmbd_ida_free(session_ida);
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#ifndef __USER_SESSION_MANAGEMENT_H__
#define __USER_SESSION_MANAGEMENT_H__
#include <linux/hashtable.h>
#include "../smb_common.h"
#include "../ntlmssp.h"
#define CIFDS_SESSION_FLAG_SMB2 (1 << 1)
#define PREAUTH_HASHVALUE_SIZE 64
struct ksmbd_ida;
struct ksmbd_file_table;
struct channel {
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
struct ksmbd_conn *conn;
struct list_head chann_list;
};
struct preauth_session {
__u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE];
uint64_t sess_id;
struct list_head list_entry;
};
struct ksmbd_session {
uint64_t id;
struct ksmbd_user *user;
struct ksmbd_conn *conn;
unsigned int sequence_number;
unsigned int flags;
bool sign;
bool enc;
bool is_anonymous;
int state;
__u8 *Preauth_HashValue;
struct ntlmssp_auth ntlmssp;
char sess_key[CIFS_KEY_SIZE];
struct hlist_node hlist;
struct list_head ksmbd_chann_list;
struct list_head tree_conn_list;
struct ksmbd_ida *tree_conn_ida;
struct list_head rpc_handle_list;
__u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
struct list_head sessions_entry;
struct ksmbd_file_table file_table;
atomic_t refcnt;
};
static inline int test_session_flag(struct ksmbd_session *sess, int bit)
{
return sess->flags & bit;
}
static inline void set_session_flag(struct ksmbd_session *sess, int bit)
{
sess->flags |= bit;
}
static inline void clear_session_flag(struct ksmbd_session *sess, int bit)
{
sess->flags &= ~bit;
}
struct ksmbd_session *ksmbd_smb2_session_create(void);
void ksmbd_session_destroy(struct ksmbd_session *sess);
bool ksmbd_session_id_match(struct ksmbd_session *sess, unsigned long long id);
struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id);
struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
unsigned long long id);
void ksmbd_session_register(struct ksmbd_conn *conn,
struct ksmbd_session *sess);
void ksmbd_sessions_deregister(struct ksmbd_conn *conn);
int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess);
void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id);
int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name);
void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id);
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id);
int get_session(struct ksmbd_session *sess);
void put_session(struct ksmbd_session *sess);
int ksmbd_init_session_table(void);
void ksmbd_free_session_table(void);
#endif /* __USER_SESSION_MANAGEMENT_H__ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/xattr.h>
#include <linux/fs.h>
#include "misc.h"
#include "smb_common.h"
#include "connection.h"
#include "vfs.h"
#include "mgmt/share_config.h"
/**
* match_pattern() - compare a string with a pattern which might include
* wildcard '*' and '?'
* TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR
*
* @string: string to compare with a pattern
* @pattern: pattern string which might include wildcard '*' and '?'
*
* Return: 0 if pattern matched with the string, otherwise non zero value
*/
int match_pattern(const char *str, const char *pattern)
{
const char *s = str;
const char *p = pattern;
bool star = false;
while (*s) {
switch (*p) {
case '?':
s++;
p++;
break;
case '*':
star = true;
str = s;
if (!*++p)
return true;
pattern = p;
break;
default:
if (tolower(*s) == tolower(*p)) {
s++;
p++;
} else {
if (!star)
return false;
str++;
s = str;
p = pattern;
}
break;
}
}
if (*p == '*')
++p;
return !*p;
}
/*
* is_char_allowed() - check for valid character
* @ch: input character to be checked
*
* Return: 1 if char is allowed, otherwise 0
*/
static inline int is_char_allowed(char ch)
{
/* check for control chars, wildcards etc. */
if (!(ch & 0x80) &&
(ch <= 0x1f ||
ch == '?' || ch == '"' || ch == '<' ||
ch == '>' || ch == '|' || ch == '*'))
return 0;
return 1;
}
int ksmbd_validate_filename(char *filename)
{
while (*filename) {
char c = *filename;
filename++;
if (!is_char_allowed(c)) {
ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c);
return -ENOENT;
}
}
return 0;
}
static int ksmbd_validate_stream_name(char *stream_name)
{
while (*stream_name) {
char c = *stream_name;
stream_name++;
if (c == '/' || c == ':' || c == '\\') {
ksmbd_err("Stream name validation failed: %c\n", c);
return -ENOENT;
}
}
return 0;
}
int parse_stream_name(char *filename, char **stream_name, int *s_type)
{
char *stream_type;
char *s_name;
int rc = 0;
s_name = filename;
filename = strsep(&s_name, ":");
ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name);
if (strchr(s_name, ':')) {
stream_type = s_name;
s_name = strsep(&stream_type, ":");
rc = ksmbd_validate_stream_name(s_name);
if (rc < 0) {
rc = -ENOENT;
goto out;
}
ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name,
stream_type);
if (!strncasecmp("$data", stream_type, 5))
*s_type = DATA_STREAM;
else if (!strncasecmp("$index_allocation", stream_type, 17))
*s_type = DIR_STREAM;
else
rc = -ENOENT;
}
*stream_name = s_name;
out:
return rc;
}
/**
* convert_to_nt_pathname() - extract and return windows path string
* whose share directory prefix was removed from file path
* @filename : unix filename
* @sharepath: share path string
*
* Return : windows path string or error
*/
char *convert_to_nt_pathname(char *filename, char *sharepath)
{
char *ab_pathname;
int len, name_len;
name_len = strlen(filename);
ab_pathname = kmalloc(name_len, GFP_KERNEL);
if (!ab_pathname)
return NULL;
ab_pathname[0] = '\\';
ab_pathname[1] = '\0';
len = strlen(sharepath);
if (!strncmp(filename, sharepath, len) && name_len != len) {
strscpy(ab_pathname, &filename[len], name_len);
ksmbd_conv_path_to_windows(ab_pathname);
}
return ab_pathname;
}
int get_nlink(struct kstat *st)
{
int nlink;
nlink = st->nlink;
if (S_ISDIR(st->mode))
nlink--;
return nlink;
}
void ksmbd_conv_path_to_unix(char *path)
{
strreplace(path, '\\', '/');
}
void ksmbd_strip_last_slash(char *path)
{
int len = strlen(path);
while (len && path[len - 1] == '/') {
path[len - 1] = '\0';
len--;
}
}
void ksmbd_conv_path_to_windows(char *path)
{
strreplace(path, '/', '\\');
}
/**
* extract_sharename() - get share name from tree connect request
* @treename: buffer containing tree name and share name
*
* Return: share name on success, otherwise error
*/
char *extract_sharename(char *treename)
{
char *name = treename;
char *dst;
char *pos = strrchr(name, '\\');
if (pos)
name = (pos + 1);
/* caller has to free the memory */
dst = kstrdup(name, GFP_KERNEL);
if (!dst)
return ERR_PTR(-ENOMEM);
return dst;
}
/**
* convert_to_unix_name() - convert windows name to unix format
* @path: name to be converted
* @tid: tree id of mathing share
*
* Return: converted name on success, otherwise NULL
*/
char *convert_to_unix_name(struct ksmbd_share_config *share, char *name)
{
int no_slash = 0, name_len, path_len;
char *new_name;
if (name[0] == '/')
name++;
path_len = share->path_sz;
name_len = strlen(name);
new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL);
if (!new_name)
return new_name;
memcpy(new_name, share->path, path_len);
if (new_name[path_len - 1] != '/') {
new_name[path_len] = '/';
no_slash = 1;
}
memcpy(new_name + path_len + no_slash, name, name_len);
path_len += name_len + no_slash;
new_name[path_len] = 0x00;
return new_name;
}
char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
const struct nls_table *local_nls,
int *conv_len)
{
char *conv;
int sz = min(4 * d_info->name_len, PATH_MAX);
if (!sz)
return NULL;
conv = kmalloc(sz, GFP_KERNEL);
if (!conv)
return NULL;
/* XXX */
*conv_len = smbConvertToUTF16((__le16 *)conv,
d_info->name,
d_info->name_len,
local_nls,
0);
*conv_len *= 2;
/* We allocate buffer twice bigger than needed. */
conv[*conv_len] = 0x00;
conv[*conv_len + 1] = 0x00;
return conv;
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#ifndef __KSMBD_MISC_H__
#define __KSMBD_MISC_H__
struct ksmbd_share_config;
struct nls_table;
struct kstat;
struct ksmbd_file;
int match_pattern(const char *str, const char *pattern);
int ksmbd_validate_filename(char *filename);
int parse_stream_name(char *filename, char **stream_name, int *s_type);
char *convert_to_nt_pathname(char *filename, char *sharepath);
int get_nlink(struct kstat *st);
void ksmbd_conv_path_to_unix(char *path);
void ksmbd_strip_last_slash(char *path);
void ksmbd_conv_path_to_windows(char *path);
char *extract_sharename(char *treename);
char *convert_to_unix_name(struct ksmbd_share_config *share, char *name);
#define KSMBD_DIR_INFO_ALIGNMENT 8
struct ksmbd_dir_info;
char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
const struct nls_table *local_nls,
int *conv_len);
#endif /* __KSMBD_MISC_H__ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2021 Samsung Electronics Co., Ltd.
* Author(s): Namjae Jeon <linkinjeon@kernel.org>
*/
#include <linux/fs.h>
#include "glob.h"
#include "ndr.h"
#define PAYLOAD_HEAD(d) ((d)->data + (d)->offset)
#define KSMBD_ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask))
#define KSMBD_ALIGN(x, a) \
({ \
typeof(x) ret = (x); \
if (((x) & ((typeof(x))(a) - 1)) != 0) \
ret = KSMBD_ALIGN_MASK(x, (typeof(x))(a) - 1); \
ret; \
})
static void align_offset(struct ndr *ndr, int n)
{
ndr->offset = KSMBD_ALIGN(ndr->offset, n);
}
static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz)
{
char *data;
data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL);
if (!data)
return -ENOMEM;
n->data = data;
n->length += 1024;
memset(n->data + n->offset, 0, 1024);
return 0;
}
static void ndr_write_int16(struct ndr *n, __u16 value)
{
if (n->length <= n->offset + sizeof(value))
try_to_realloc_ndr_blob(n, sizeof(value));
*(__le16 *)PAYLOAD_HEAD(n) = cpu_to_le16(value);
n->offset += sizeof(value);
}
static void ndr_write_int32(struct ndr *n, __u32 value)
{
if (n->length <= n->offset + sizeof(value))
try_to_realloc_ndr_blob(n, sizeof(value));
*(__le32 *)PAYLOAD_HEAD(n) = cpu_to_le32(value);
n->offset += sizeof(value);
}
static void ndr_write_int64(struct ndr *n, __u64 value)
{
if (n->length <= n->offset + sizeof(value))
try_to_realloc_ndr_blob(n, sizeof(value));
*(__le64 *)PAYLOAD_HEAD(n) = cpu_to_le64(value);
n->offset += sizeof(value);
}
static int ndr_write_bytes(struct ndr *n, void *value, size_t sz)
{
if (n->length <= n->offset + sz)
try_to_realloc_ndr_blob(n, sz);
memcpy(PAYLOAD_HEAD(n), value, sz);
n->offset += sz;
return 0;
}
static int ndr_write_string(struct ndr *n, void *value, size_t sz)
{
if (n->length <= n->offset + sz)
try_to_realloc_ndr_blob(n, sz);
strncpy(PAYLOAD_HEAD(n), value, sz);
sz++;
n->offset += sz;
align_offset(n, 2);
return 0;
}
static int ndr_read_string(struct ndr *n, void *value, size_t sz)
{
int len = strnlen(PAYLOAD_HEAD(n), sz);
memcpy(value, PAYLOAD_HEAD(n), len);
len++;
n->offset += len;
align_offset(n, 2);
return 0;
}
static int ndr_read_bytes(struct ndr *n, void *value, size_t sz)
{
memcpy(value, PAYLOAD_HEAD(n), sz);
n->offset += sz;
return 0;
}
static __u16 ndr_read_int16(struct ndr *n)
{
__u16 ret;
ret = le16_to_cpu(*(__le16 *)PAYLOAD_HEAD(n));
n->offset += sizeof(__u16);
return ret;
}
static __u32 ndr_read_int32(struct ndr *n)
{
__u32 ret;
ret = le32_to_cpu(*(__le32 *)PAYLOAD_HEAD(n));
n->offset += sizeof(__u32);
return ret;
}
static __u64 ndr_read_int64(struct ndr *n)
{
__u64 ret;
ret = le64_to_cpu(*(__le64 *)PAYLOAD_HEAD(n));
n->offset += sizeof(__u64);
return ret;
}
int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da)
{
char hex_attr[12] = {0};
n->offset = 0;
n->length = 1024;
n->data = kzalloc(n->length, GFP_KERNEL);
if (!n->data)
return -ENOMEM;
if (da->version == 3) {
snprintf(hex_attr, 10, "0x%x", da->attr);
ndr_write_string(n, hex_attr, strlen(hex_attr));
} else {
ndr_write_string(n, "", strlen(""));
}
ndr_write_int16(n, da->version);
ndr_write_int32(n, da->version);
ndr_write_int32(n, da->flags);
ndr_write_int32(n, da->attr);
if (da->version == 3) {
ndr_write_int32(n, da->ea_size);
ndr_write_int64(n, da->size);
ndr_write_int64(n, da->alloc_size);
} else
ndr_write_int64(n, da->itime);
ndr_write_int64(n, da->create_time);
if (da->version == 3)
ndr_write_int64(n, da->change_time);
return 0;
}
int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da)
{
char hex_attr[12] = {0};
int version2;
n->offset = 0;
ndr_read_string(n, hex_attr, n->length - n->offset);
da->version = ndr_read_int16(n);
if (da->version != 3 && da->version != 4) {
ksmbd_err("v%d version is not supported\n", da->version);
return -EINVAL;
}
version2 = ndr_read_int32(n);
if (da->version != version2) {
ksmbd_err("ndr version mismatched(version: %d, version2: %d)\n",
da->version, version2);
return -EINVAL;
}
ndr_read_int32(n);
da->attr = ndr_read_int32(n);
if (da->version == 4) {
da->itime = ndr_read_int64(n);
da->create_time = ndr_read_int64(n);
} else {
ndr_read_int32(n);
ndr_read_int64(n);
ndr_read_int64(n);
da->create_time = ndr_read_int64(n);
ndr_read_int64(n);
}
return 0;
}
static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl)
{
int i;
ndr_write_int32(n, acl->count);
align_offset(n, 8);
ndr_write_int32(n, acl->count);
ndr_write_int32(n, 0);
for (i = 0; i < acl->count; i++) {
align_offset(n, 8);
ndr_write_int16(n, acl->entries[i].type);
ndr_write_int16(n, acl->entries[i].type);
if (acl->entries[i].type == SMB_ACL_USER) {
align_offset(n, 8);
ndr_write_int64(n, acl->entries[i].uid);
} else if (acl->entries[i].type == SMB_ACL_GROUP) {
align_offset(n, 8);
ndr_write_int64(n, acl->entries[i].gid);
}
/* push permission */
ndr_write_int32(n, acl->entries[i].perm);
}
return 0;
}
int ndr_encode_posix_acl(struct ndr *n, struct inode *inode,
struct xattr_smb_acl *acl, struct xattr_smb_acl *def_acl)
{
int ref_id = 0x00020000;
n->offset = 0;
n->length = 1024;
n->data = kzalloc(n->length, GFP_KERNEL);
if (!n->data)
return -ENOMEM;
if (acl) {
/* ACL ACCESS */
ndr_write_int32(n, ref_id);
ref_id += 4;
} else
ndr_write_int32(n, 0);
if (def_acl) {
/* DEFAULT ACL ACCESS */
ndr_write_int32(n, ref_id);
ref_id += 4;
} else
ndr_write_int32(n, 0);
ndr_write_int64(n, from_kuid(&init_user_ns, inode->i_uid));
ndr_write_int64(n, from_kgid(&init_user_ns, inode->i_gid));
ndr_write_int32(n, inode->i_mode);
if (acl) {
ndr_encode_posix_acl_entry(n, acl);
if (def_acl)
ndr_encode_posix_acl_entry(n, def_acl);
}
return 0;
}
int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
{
int ref_id = 0x00020004;
n->offset = 0;
n->length = 2048;
n->data = kzalloc(n->length, GFP_KERNEL);
if (!n->data)
return -ENOMEM;
ndr_write_int16(n, acl->version);
ndr_write_int32(n, acl->version);
ndr_write_int16(n, 2);
ndr_write_int32(n, ref_id);
/* push hash type and hash 64bytes */
ndr_write_int16(n, acl->hash_type);
ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE);
ndr_write_bytes(n, acl->desc, acl->desc_len);
ndr_write_int64(n, acl->current_time);
ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE);
/* push ndr for security descriptor */
ndr_write_bytes(n, acl->sd_buf, acl->sd_size);
return 0;
}
int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
{
int version2;
n->offset = 0;
acl->version = ndr_read_int16(n);
if (acl->version != 4) {
ksmbd_err("v%d version is not supported\n", acl->version);
return -EINVAL;
}
version2 = ndr_read_int32(n);
if (acl->version != version2) {
ksmbd_err("ndr version mismatched(version: %d, version2: %d)\n",
acl->version, version2);
return -EINVAL;
}
/* Read Level */
ndr_read_int16(n);
/* Read Ref Id */
ndr_read_int32(n);
acl->hash_type = ndr_read_int16(n);
ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE);
ndr_read_bytes(n, acl->desc, 10);
if (strncmp(acl->desc, "posix_acl", 9)) {
ksmbd_err("Invalid acl desciption : %s\n", acl->desc);
return -EINVAL;
}
/* Read Time */
ndr_read_int64(n);
/* Read Posix ACL hash */
ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE);
acl->sd_size = n->length - n->offset;
acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL);
if (!acl->sd_buf)
return -ENOMEM;
ndr_read_bytes(n, acl->sd_buf, acl->sd_size);
return 0;
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2020 Samsung Electronics Co., Ltd.
* Author(s): Namjae Jeon <linkinjeon@kernel.org>
*/
struct ndr {
char *data;
int offset;
int length;
};
#define NDR_NTSD_OFFSETOF 0xA0
int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da);
int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da);
int ndr_encode_posix_acl(struct ndr *n, struct inode *inode,
struct xattr_smb_acl *acl, struct xattr_smb_acl *def_acl);
int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl);
int ndr_encode_v3_ntacl(struct ndr *n, struct xattr_ntacl *acl);
int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl);
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* fs/ksmbd/netmisc.c
*
* Copyright (c) International Business Machines Corp., 2002,2008
* Author(s): Steve French (sfrench@us.ibm.com)
*
* Error mapping routines from Samba libsmb/errormap.c
* Copyright (C) Andrew Tridgell 2001
*/
#include "glob.h"
#include "smberr.h"
#include "nterr.h"
#include "time_wrappers.h"
/*
* Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
* into Unix UTC (based 1970-01-01, in seconds).
*/
struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc)
{
struct timespec64 ts;
/* Subtract the NTFS time offset, then convert to 1s intervals. */
s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET;
u64 abs_t;
/*
* Unfortunately can not use normal 64 bit division on 32 bit arch, but
* the alternative, do_div, does not work with negative numbers so have
* to special case them
*/
if (t < 0) {
abs_t = -t;
ts.tv_nsec = do_div(abs_t, 10000000) * 100;
ts.tv_nsec = -ts.tv_nsec;
ts.tv_sec = -abs_t;
} else {
abs_t = t;
ts.tv_nsec = do_div(abs_t, 10000000) * 100;
ts.tv_sec = abs_t;
}
return ts;
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Unix SMB/Netbios implementation.
* Version 1.9.
* RPC Pipe client / server routines
* Copyright (C) Luke Kenneth Casson Leighton 1997-2001.
*/
/* NT error codes - see nterr.h */
#include <linux/types.h>
#include <linux/fs.h>
#include "nterr.h"
const struct nt_err_code_struct nt_errs[] = {
{"NT_STATUS_OK", NT_STATUS_OK},
{"NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL},
{"NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED},
{"NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS},
{"NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH},
{"NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION},
{"NT_STATUS_BUFFER_OVERFLOW", NT_STATUS_BUFFER_OVERFLOW},
{"NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR},
{"NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA},
{"NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE},
{"NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK},
{"NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC},
{"NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID},
{"NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED},
{"NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER},
{"NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE},
{"NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE},
{"NT_STATUS_INVALID_DEVICE_REQUEST",
NT_STATUS_INVALID_DEVICE_REQUEST},
{"NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE},
{"NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME},
{"NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE},
{"NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA},
{"NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR},
{"NT_STATUS_MORE_PROCESSING_REQUIRED",
NT_STATUS_MORE_PROCESSING_REQUIRED},
{"NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY},
{"NT_STATUS_CONFLICTING_ADDRESSES",
NT_STATUS_CONFLICTING_ADDRESSES},
{"NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW},
{"NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM},
{"NT_STATUS_UNABLE_TO_DELETE_SECTION",
NT_STATUS_UNABLE_TO_DELETE_SECTION},
{"NT_STATUS_INVALID_SYSTEM_SERVICE",
NT_STATUS_INVALID_SYSTEM_SERVICE},
{"NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION},
{"NT_STATUS_INVALID_LOCK_SEQUENCE",
NT_STATUS_INVALID_LOCK_SEQUENCE},
{"NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE},
{"NT_STATUS_INVALID_FILE_FOR_SECTION",
NT_STATUS_INVALID_FILE_FOR_SECTION},
{"NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED},
{"NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED},
{"NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL},
{"NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH},
{"NT_STATUS_NONCONTINUABLE_EXCEPTION",
NT_STATUS_NONCONTINUABLE_EXCEPTION},
{"NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION},
{"NT_STATUS_UNWIND", NT_STATUS_UNWIND},
{"NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK},
{"NT_STATUS_INVALID_UNWIND_TARGET",
NT_STATUS_INVALID_UNWIND_TARGET},
{"NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED},
{"NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR},
{"NT_STATUS_UNABLE_TO_DECOMMIT_VM",
NT_STATUS_UNABLE_TO_DECOMMIT_VM},
{"NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED},
{"NT_STATUS_INVALID_PORT_ATTRIBUTES",
NT_STATUS_INVALID_PORT_ATTRIBUTES},
{"NT_STATUS_PORT_MESSAGE_TOO_LONG",
NT_STATUS_PORT_MESSAGE_TOO_LONG},
{"NT_STATUS_INVALID_PARAMETER_MIX",
NT_STATUS_INVALID_PARAMETER_MIX},
{"NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER},
{"NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR},
{"NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID},
{"NT_STATUS_OBJECT_NAME_NOT_FOUND",
NT_STATUS_OBJECT_NAME_NOT_FOUND},
{"NT_STATUS_OBJECT_NAME_COLLISION",
NT_STATUS_OBJECT_NAME_COLLISION},
{"NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE},
{"NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED},
{"NT_STATUS_DEVICE_ALREADY_ATTACHED",
NT_STATUS_DEVICE_ALREADY_ATTACHED},
{"NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID},
{"NT_STATUS_OBJECT_PATH_NOT_FOUND",
NT_STATUS_OBJECT_PATH_NOT_FOUND},
{"NT_STATUS_OBJECT_PATH_SYNTAX_BAD",
NT_STATUS_OBJECT_PATH_SYNTAX_BAD},
{"NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN},
{"NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR},
{"NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR},
{"NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR},
{"NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG},
{"NT_STATUS_PORT_CONNECTION_REFUSED",
NT_STATUS_PORT_CONNECTION_REFUSED},
{"NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE},
{"NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION},
{"NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED},
{"NT_STATUS_INVALID_PAGE_PROTECTION",
NT_STATUS_INVALID_PAGE_PROTECTION},
{"NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED},
{"NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED",
NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
{"NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET},
{"NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE},
{"NT_STATUS_SUSPEND_COUNT_EXCEEDED",
NT_STATUS_SUSPEND_COUNT_EXCEEDED},
{"NT_STATUS_THREAD_IS_TERMINATING",
NT_STATUS_THREAD_IS_TERMINATING},
{"NT_STATUS_BAD_WORKING_SET_LIMIT",
NT_STATUS_BAD_WORKING_SET_LIMIT},
{"NT_STATUS_INCOMPATIBLE_FILE_MAP",
NT_STATUS_INCOMPATIBLE_FILE_MAP},
{"NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION},
{"NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED},
{"NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE},
{"NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY},
{"NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE},
{"NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR},
{"NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT},
{"NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED},
{"NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING},
{"NT_STATUS_CTL_FILE_NOT_SUPPORTED",
NT_STATUS_CTL_FILE_NOT_SUPPORTED},
{"NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION},
{"NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH},
{"NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER},
{"NT_STATUS_INVALID_PRIMARY_GROUP",
NT_STATUS_INVALID_PRIMARY_GROUP},
{"NT_STATUS_NO_IMPERSONATION_TOKEN",
NT_STATUS_NO_IMPERSONATION_TOKEN},
{"NT_STATUS_CANT_DISABLE_MANDATORY",
NT_STATUS_CANT_DISABLE_MANDATORY},
{"NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS},
{"NT_STATUS_NO_SUCH_LOGON_SESSION",
NT_STATUS_NO_SUCH_LOGON_SESSION},
{"NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE},
{"NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD},
{"NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME},
{"NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS},
{"NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER},
{"NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS},
{"NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP},
{"NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP},
{"NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP},
{"NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN},
{"NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD},
{"NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD},
{"NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION},
{"NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE},
{"NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION},
{"NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS},
{"NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION},
{"NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED},
{"NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED},
{"NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED},
{"NT_STATUS_TOO_MANY_LUIDS_REQUESTED",
NT_STATUS_TOO_MANY_LUIDS_REQUESTED},
{"NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED},
{"NT_STATUS_INVALID_SUB_AUTHORITY",
NT_STATUS_INVALID_SUB_AUTHORITY},
{"NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL},
{"NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID},
{"NT_STATUS_INVALID_SECURITY_DESCR",
NT_STATUS_INVALID_SECURITY_DESCR},
{"NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND},
{"NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT},
{"NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN},
{"NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL},
{"NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED},
{"NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL},
{"NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED},
{"NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED},
{"NT_STATUS_TOO_MANY_GUIDS_REQUESTED",
NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
{"NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED},
{"NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY},
{"NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED},
{"NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL},
{"NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED},
{"NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA},
{"NT_STATUS_RESOURCE_DATA_NOT_FOUND",
NT_STATUS_RESOURCE_DATA_NOT_FOUND},
{"NT_STATUS_RESOURCE_TYPE_NOT_FOUND",
NT_STATUS_RESOURCE_TYPE_NOT_FOUND},
{"NT_STATUS_RESOURCE_NAME_NOT_FOUND",
NT_STATUS_RESOURCE_NAME_NOT_FOUND},
{"NT_STATUS_ARRAY_BOUNDS_EXCEEDED",
NT_STATUS_ARRAY_BOUNDS_EXCEEDED},
{"NT_STATUS_FLOAT_DENORMAL_OPERAND",
NT_STATUS_FLOAT_DENORMAL_OPERAND},
{"NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO},
{"NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT},
{"NT_STATUS_FLOAT_INVALID_OPERATION",
NT_STATUS_FLOAT_INVALID_OPERATION},
{"NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW},
{"NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK},
{"NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW},
{"NT_STATUS_INTEGER_DIVIDE_BY_ZERO",
NT_STATUS_INTEGER_DIVIDE_BY_ZERO},
{"NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW},
{"NT_STATUS_PRIVILEGED_INSTRUCTION",
NT_STATUS_PRIVILEGED_INSTRUCTION},
{"NT_STATUS_TOO_MANY_PAGING_FILES",
NT_STATUS_TOO_MANY_PAGING_FILES},
{"NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID},
{"NT_STATUS_ALLOTTED_SPACE_EXCEEDED",
NT_STATUS_ALLOTTED_SPACE_EXCEEDED},
{"NT_STATUS_INSUFFICIENT_RESOURCES",
NT_STATUS_INSUFFICIENT_RESOURCES},
{"NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND},
{"NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR},
{"NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED},
{"NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE},
{"NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE},
{"NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED},
{"NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA},
{"NT_STATUS_MEDIA_WRITE_PROTECTED",
NT_STATUS_MEDIA_WRITE_PROTECTED},
{"NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY},
{"NT_STATUS_INVALID_GROUP_ATTRIBUTES",
NT_STATUS_INVALID_GROUP_ATTRIBUTES},
{"NT_STATUS_BAD_IMPERSONATION_LEVEL",
NT_STATUS_BAD_IMPERSONATION_LEVEL},
{"NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS},
{"NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS},
{"NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE},
{"NT_STATUS_BAD_MASTER_BOOT_RECORD",
NT_STATUS_BAD_MASTER_BOOT_RECORD},
{"NT_STATUS_INSTRUCTION_MISALIGNMENT",
NT_STATUS_INSTRUCTION_MISALIGNMENT},
{"NT_STATUS_INSTANCE_NOT_AVAILABLE",
NT_STATUS_INSTANCE_NOT_AVAILABLE},
{"NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE},
{"NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE},
{"NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY},
{"NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION},
{"NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED},
{"NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING},
{"NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED},
{"NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING},
{"NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE},
{"NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT},
{"NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED},
{"NT_STATUS_PROFILING_NOT_STARTED",
NT_STATUS_PROFILING_NOT_STARTED},
{"NT_STATUS_PROFILING_NOT_STOPPED",
NT_STATUS_PROFILING_NOT_STOPPED},
{"NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET},
{"NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY},
{"NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED},
{"NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING},
{"NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME},
{"NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH},
{"NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY},
{"NT_STATUS_DEVICE_DOES_NOT_EXIST",
NT_STATUS_DEVICE_DOES_NOT_EXIST},
{"NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS},
{"NT_STATUS_ADAPTER_HARDWARE_ERROR",
NT_STATUS_ADAPTER_HARDWARE_ERROR},
{"NT_STATUS_INVALID_NETWORK_RESPONSE",
NT_STATUS_INVALID_NETWORK_RESPONSE},
{"NT_STATUS_UNEXPECTED_NETWORK_ERROR",
NT_STATUS_UNEXPECTED_NETWORK_ERROR},
{"NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER},
{"NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL},
{"NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE},
{"NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED},
{"NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED},
{"NT_STATUS_NETWORK_ACCESS_DENIED",
NT_STATUS_NETWORK_ACCESS_DENIED},
{"NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE},
{"NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME},
{"NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES},
{"NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS},
{"NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED},
{"NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED},
{"NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED},
{"NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT},
{"NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT},
{"NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE},
{"NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED},
{"NT_STATUS_VIRTUAL_CIRCUIT_CLOSED",
NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
{"NT_STATUS_NO_SECURITY_ON_OBJECT",
NT_STATUS_NO_SECURITY_ON_OBJECT},
{"NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT},
{"NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY},
{"NT_STATUS_CANT_ACCESS_DOMAIN_INFO",
NT_STATUS_CANT_ACCESS_DOMAIN_INFO},
{"NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF},
{"NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE},
{"NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE},
{"NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE},
{"NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN},
{"NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS},
{"NT_STATUS_DOMAIN_LIMIT_EXCEEDED",
NT_STATUS_DOMAIN_LIMIT_EXCEEDED},
{"NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED},
{"NT_STATUS_INVALID_OPLOCK_PROTOCOL",
NT_STATUS_INVALID_OPLOCK_PROTOCOL},
{"NT_STATUS_INTERNAL_DB_CORRUPTION",
NT_STATUS_INTERNAL_DB_CORRUPTION},
{"NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR},
{"NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED},
{"NT_STATUS_BAD_DESCRIPTOR_FORMAT",
NT_STATUS_BAD_DESCRIPTOR_FORMAT},
{"NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER},
{"NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR},
{"NT_STATUS_UNEXPECTED_MM_CREATE_ERR",
NT_STATUS_UNEXPECTED_MM_CREATE_ERR},
{"NT_STATUS_UNEXPECTED_MM_MAP_ERROR",
NT_STATUS_UNEXPECTED_MM_MAP_ERROR},
{"NT_STATUS_UNEXPECTED_MM_EXTEND_ERR",
NT_STATUS_UNEXPECTED_MM_EXTEND_ERR},
{"NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS},
{"NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS},
{"NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1},
{"NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2},
{"NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3},
{"NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4},
{"NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5},
{"NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6},
{"NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7},
{"NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8},
{"NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9},
{"NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10},
{"NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11},
{"NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12},
{"NT_STATUS_REDIRECTOR_NOT_STARTED",
NT_STATUS_REDIRECTOR_NOT_STARTED},
{"NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED},
{"NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW},
{"NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE},
{"NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE},
{"NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY},
{"NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR},
{"NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY},
{"NT_STATUS_BAD_LOGON_SESSION_STATE",
NT_STATUS_BAD_LOGON_SESSION_STATE},
{"NT_STATUS_LOGON_SESSION_COLLISION",
NT_STATUS_LOGON_SESSION_COLLISION},
{"NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG},
{"NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN},
{"NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE},
{"NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND},
{"NT_STATUS_PROCESS_IS_TERMINATING",
NT_STATUS_PROCESS_IS_TERMINATING},
{"NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE},
{"NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION},
{"NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE},
{"NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED},
{"NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT},
{"NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST},
{"NT_STATUS_ABIOS_LID_ALREADY_OWNED",
NT_STATUS_ABIOS_LID_ALREADY_OWNED},
{"NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER},
{"NT_STATUS_ABIOS_INVALID_COMMAND",
NT_STATUS_ABIOS_INVALID_COMMAND},
{"NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID},
{"NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE",
NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE},
{"NT_STATUS_ABIOS_INVALID_SELECTOR",
NT_STATUS_ABIOS_INVALID_SELECTOR},
{"NT_STATUS_NO_LDT", NT_STATUS_NO_LDT},
{"NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE},
{"NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET},
{"NT_STATUS_INVALID_LDT_DESCRIPTOR",
NT_STATUS_INVALID_LDT_DESCRIPTOR},
{"NT_STATUS_INVALID_IMAGE_NE_FORMAT",
NT_STATUS_INVALID_IMAGE_NE_FORMAT},
{"NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE},
{"NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE},
{"NT_STATUS_MAPPED_FILE_SIZE_ZERO",
NT_STATUS_MAPPED_FILE_SIZE_ZERO},
{"NT_STATUS_TOO_MANY_OPENED_FILES",
NT_STATUS_TOO_MANY_OPENED_FILES},
{"NT_STATUS_CANCELLED", NT_STATUS_CANCELLED},
{"NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE},
{"NT_STATUS_INVALID_COMPUTER_NAME",
NT_STATUS_INVALID_COMPUTER_NAME},
{"NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED},
{"NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT},
{"NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP},
{"NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER},
{"NT_STATUS_MEMBERS_PRIMARY_GROUP",
NT_STATUS_MEMBERS_PRIMARY_GROUP},
{"NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED},
{"NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS},
{"NT_STATUS_THREAD_NOT_IN_PROCESS",
NT_STATUS_THREAD_NOT_IN_PROCESS},
{"NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE},
{"NT_STATUS_PAGEFILE_QUOTA_EXCEEDED",
NT_STATUS_PAGEFILE_QUOTA_EXCEEDED},
{"NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT},
{"NT_STATUS_INVALID_IMAGE_LE_FORMAT",
NT_STATUS_INVALID_IMAGE_LE_FORMAT},
{"NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ},
{"NT_STATUS_INVALID_IMAGE_PROTECT",
NT_STATUS_INVALID_IMAGE_PROTECT},
{"NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16},
{"NT_STATUS_LOGON_SERVER_CONFLICT",
NT_STATUS_LOGON_SERVER_CONFLICT},
{"NT_STATUS_TIME_DIFFERENCE_AT_DC",
NT_STATUS_TIME_DIFFERENCE_AT_DC},
{"NT_STATUS_SYNCHRONIZATION_REQUIRED",
NT_STATUS_SYNCHRONIZATION_REQUIRED},
{"NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND},
{"NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED},
{"NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED},
{"NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND},
{"NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND},
{"NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT},
{"NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT},
{"NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT},
{"NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES},
{"NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED},
{"NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT},
{"NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION},
{"NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS},
{"NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED},
{"NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE},
{"NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION},
{"NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE},
{"NT_STATUS_PAGEFILE_CREATE_FAILED",
NT_STATUS_PAGEFILE_CREATE_FAILED},
{"NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE},
{"NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL},
{"NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE},
{"NT_STATUS_ILLEGAL_FLOAT_CONTEXT",
NT_STATUS_ILLEGAL_FLOAT_CONTEXT},
{"NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN},
{"NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT},
{"NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED},
{"NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR},
{"NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME},
{"NT_STATUS_SERIAL_NO_DEVICE_INITED",
NT_STATUS_SERIAL_NO_DEVICE_INITED},
{"NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS},
{"NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS},
{"NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS},
{"NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS},
{"NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED},
{"NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS},
{"NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG},
{"NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR},
{"NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE},
{"NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS},
{"NT_STATUS_LOGON_TYPE_NOT_GRANTED",
NT_STATUS_LOGON_TYPE_NOT_GRANTED},
{"NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE},
{"NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED",
NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED},
{"NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR",
NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR},
{"NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER},
{"NT_STATUS_ILL_FORMED_SERVICE_ENTRY",
NT_STATUS_ILL_FORMED_SERVICE_ENTRY},
{"NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER},
{"NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER},
{"NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER},
{"NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME},
{"NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND",
NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND},
{"NT_STATUS_FLOPPY_WRONG_CYLINDER",
NT_STATUS_FLOPPY_WRONG_CYLINDER},
{"NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR},
{"NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS},
{"NT_STATUS_DISK_RECALIBRATE_FAILED",
NT_STATUS_DISK_RECALIBRATE_FAILED},
{"NT_STATUS_DISK_OPERATION_FAILED",
NT_STATUS_DISK_OPERATION_FAILED},
{"NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED},
{"NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY},
{"NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING},
{"NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE},
{"NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH},
{"NT_STATUS_DEVICE_NOT_PARTITIONED",
NT_STATUS_DEVICE_NOT_PARTITIONED},
{"NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA},
{"NT_STATUS_UNABLE_TO_UNLOAD_MEDIA",
NT_STATUS_UNABLE_TO_UNLOAD_MEDIA},
{"NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW},
{"NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA},
{"NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER},
{"NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER},
{"NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED},
{"NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE},
{"NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS},
{"NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED",
NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED},
{"NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN},
{"NT_STATUS_CHILD_MUST_BE_VOLATILE",
NT_STATUS_CHILD_MUST_BE_VOLATILE},
{"NT_STATUS_DEVICE_CONFIGURATION_ERROR",
NT_STATUS_DEVICE_CONFIGURATION_ERROR},
{"NT_STATUS_DRIVER_INTERNAL_ERROR",
NT_STATUS_DRIVER_INTERNAL_ERROR},
{"NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE},
{"NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR},
{"NT_STATUS_DEVICE_PROTOCOL_ERROR",
NT_STATUS_DEVICE_PROTOCOL_ERROR},
{"NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER},
{"NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL},
{"NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE},
{"NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET},
{"NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT},
{"NT_STATUS_TRUSTED_DOMAIN_FAILURE",
NT_STATUS_TRUSTED_DOMAIN_FAILURE},
{"NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE",
NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE},
{"NT_STATUS_EVENTLOG_FILE_CORRUPT",
NT_STATUS_EVENTLOG_FILE_CORRUPT},
{"NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START},
{"NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE},
{"NT_STATUS_MUTANT_LIMIT_EXCEEDED",
NT_STATUS_MUTANT_LIMIT_EXCEEDED},
{"NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED},
{"NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED},
{"NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK},
{"NT_STATUS_NETWORK_CREDENTIAL_CONFLICT",
NT_STATUS_NETWORK_CREDENTIAL_CONFLICT},
{"NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT},
{"NT_STATUS_EVENTLOG_FILE_CHANGED",
NT_STATUS_EVENTLOG_FILE_CHANGED},
{"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT},
{"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT},
{"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT},
{"NT_STATUS_DOMAIN_TRUST_INCONSISTENT",
NT_STATUS_DOMAIN_TRUST_INCONSISTENT},
{"NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED},
{"NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY},
{"NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED},
{"NT_STATUS_RESOURCE_LANG_NOT_FOUND",
NT_STATUS_RESOURCE_LANG_NOT_FOUND},
{"NT_STATUS_INSUFF_SERVER_RESOURCES",
NT_STATUS_INSUFF_SERVER_RESOURCES},
{"NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE},
{"NT_STATUS_INVALID_ADDRESS_COMPONENT",
NT_STATUS_INVALID_ADDRESS_COMPONENT},
{"NT_STATUS_INVALID_ADDRESS_WILDCARD",
NT_STATUS_INVALID_ADDRESS_WILDCARD},
{"NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES},
{"NT_STATUS_ADDRESS_ALREADY_EXISTS",
NT_STATUS_ADDRESS_ALREADY_EXISTS},
{"NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED},
{"NT_STATUS_CONNECTION_DISCONNECTED",
NT_STATUS_CONNECTION_DISCONNECTED},
{"NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET},
{"NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES},
{"NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED},
{"NT_STATUS_TRANSACTION_TIMED_OUT",
NT_STATUS_TRANSACTION_TIMED_OUT},
{"NT_STATUS_TRANSACTION_NO_RELEASE",
NT_STATUS_TRANSACTION_NO_RELEASE},
{"NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH},
{"NT_STATUS_TRANSACTION_RESPONDED",
NT_STATUS_TRANSACTION_RESPONDED},
{"NT_STATUS_TRANSACTION_INVALID_ID",
NT_STATUS_TRANSACTION_INVALID_ID},
{"NT_STATUS_TRANSACTION_INVALID_TYPE",
NT_STATUS_TRANSACTION_INVALID_TYPE},
{"NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION},
{"NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION},
{"NT_STATUS_CANNOT_LOAD_REGISTRY_FILE",
NT_STATUS_CANNOT_LOAD_REGISTRY_FILE},
{"NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED},
{"NT_STATUS_SYSTEM_PROCESS_TERMINATED",
NT_STATUS_SYSTEM_PROCESS_TERMINATED},
{"NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED},
{"NT_STATUS_NO_BROWSER_SERVERS_FOUND",
NT_STATUS_NO_BROWSER_SERVERS_FOUND},
{"NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR},
{"NT_STATUS_DRIVER_CANCEL_TIMEOUT",
NT_STATUS_DRIVER_CANCEL_TIMEOUT},
{"NT_STATUS_REPLY_MESSAGE_MISMATCH",
NT_STATUS_REPLY_MESSAGE_MISMATCH},
{"NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT},
{"NT_STATUS_IMAGE_CHECKSUM_MISMATCH",
NT_STATUS_IMAGE_CHECKSUM_MISMATCH},
{"NT_STATUS_LOST_WRITEBEHIND_DATA",
NT_STATUS_LOST_WRITEBEHIND_DATA},
{"NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID",
NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID},
{"NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE},
{"NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND},
{"NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM},
{"NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE},
{"NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ},
{"NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK},
{"NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID},
{"NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS},
{"NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE},
{"NT_STATUS_RETRY", NT_STATUS_RETRY},
{"NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE},
{"NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET},
{"NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND},
{"NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW},
{"NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT},
{"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND},
{"NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT},
{"NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE},
{"NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED},
{"NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT},
{"NT_STATUS_ADDRESS_ALREADY_ASSOCIATED",
NT_STATUS_ADDRESS_ALREADY_ASSOCIATED},
{"NT_STATUS_ADDRESS_NOT_ASSOCIATED",
NT_STATUS_ADDRESS_NOT_ASSOCIATED},
{"NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID},
{"NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE},
{"NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE},
{"NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE},
{"NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE},
{"NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE},
{"NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED},
{"NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED},
{"NT_STATUS_BAD_COMPRESSION_BUFFER",
NT_STATUS_BAD_COMPRESSION_BUFFER},
{"NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE},
{"NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED},
{"NT_STATUS_TIMER_RESOLUTION_NOT_SET",
NT_STATUS_TIMER_RESOLUTION_NOT_SET},
{"NT_STATUS_CONNECTION_COUNT_LIMIT",
NT_STATUS_CONNECTION_COUNT_LIMIT},
{"NT_STATUS_LOGIN_TIME_RESTRICTION",
NT_STATUS_LOGIN_TIME_RESTRICTION},
{"NT_STATUS_LOGIN_WKSTA_RESTRICTION",
NT_STATUS_LOGIN_WKSTA_RESTRICTION},
{"NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH},
{"NT_STATUS_INSUFFICIENT_LOGON_INFO",
NT_STATUS_INSUFFICIENT_LOGON_INFO},
{"NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT},
{"NT_STATUS_BAD_SERVICE_ENTRYPOINT",
NT_STATUS_BAD_SERVICE_ENTRYPOINT},
{"NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST},
{"NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1},
{"NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2},
{"NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT},
{"NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED},
{"NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE},
{"NT_STATUS_LICENSE_QUOTA_EXCEEDED",
NT_STATUS_LICENSE_QUOTA_EXCEEDED},
{"NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT},
{"NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT},
{"NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT},
{"NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE},
{"NT_STATUS_UNSUPPORTED_COMPRESSION",
NT_STATUS_UNSUPPORTED_COMPRESSION},
{"NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE},
{"NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH",
NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH},
{"NT_STATUS_DRIVER_ORDINAL_NOT_FOUND",
NT_STATUS_DRIVER_ORDINAL_NOT_FOUND},
{"NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND",
NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND},
{"NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED},
{"NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS},
{"NT_STATUS_QUOTA_LIST_INCONSISTENT",
NT_STATUS_QUOTA_LIST_INCONSISTENT},
{"NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE},
{"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES},
{"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES},
{"NT_STATUS_SOME_UNMAPPED", NT_STATUS_SOME_UNMAPPED},
{NULL, 0}
};
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Unix SMB/Netbios implementation.
* Version 1.9.
* NT error code constants
* Copyright (C) Andrew Tridgell 1992-2000
* Copyright (C) John H Terpstra 1996-2000
* Copyright (C) Luke Kenneth Casson Leighton 1996-2000
* Copyright (C) Paul Ashton 1998-2000
*/
#ifndef _NTERR_H
#define _NTERR_H
struct nt_err_code_struct {
char *nt_errstr;
__u32 nt_errcode;
};
extern const struct nt_err_code_struct nt_errs[];
/* Win32 Status codes. */
#define NT_STATUS_MORE_ENTRIES 0x0105
#define NT_ERROR_INVALID_PARAMETER 0x0057
#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a
#define NT_STATUS_1804 0x070c
#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c
#define NT_STATUS_INVALID_LOCK_RANGE (0xC0000000 | 0x01a1)
/*
* Win32 Error codes extracted using a loop in smbclient then printing a netmon
* sniff to a file.
*/
#define NT_STATUS_OK 0x0000
#define NT_STATUS_SOME_UNMAPPED 0x0107
#define NT_STATUS_BUFFER_OVERFLOW 0x80000005
#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a
#define NT_STATUS_MEDIA_CHANGED 0x8000001c
#define NT_STATUS_END_OF_MEDIA 0x8000001e
#define NT_STATUS_MEDIA_CHECK 0x80000020
#define NT_STATUS_NO_DATA_DETECTED 0x8000001c
#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d
#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288
#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288
#define NT_STATUS_UNSUCCESSFUL (0xC0000000 | 0x0001)
#define NT_STATUS_NOT_IMPLEMENTED (0xC0000000 | 0x0002)
#define NT_STATUS_INVALID_INFO_CLASS (0xC0000000 | 0x0003)
#define NT_STATUS_INFO_LENGTH_MISMATCH (0xC0000000 | 0x0004)
#define NT_STATUS_ACCESS_VIOLATION (0xC0000000 | 0x0005)
#define NT_STATUS_IN_PAGE_ERROR (0xC0000000 | 0x0006)
#define NT_STATUS_PAGEFILE_QUOTA (0xC0000000 | 0x0007)
#define NT_STATUS_INVALID_HANDLE (0xC0000000 | 0x0008)
#define NT_STATUS_BAD_INITIAL_STACK (0xC0000000 | 0x0009)
#define NT_STATUS_BAD_INITIAL_PC (0xC0000000 | 0x000a)
#define NT_STATUS_INVALID_CID (0xC0000000 | 0x000b)
#define NT_STATUS_TIMER_NOT_CANCELED (0xC0000000 | 0x000c)
#define NT_STATUS_INVALID_PARAMETER (0xC0000000 | 0x000d)
#define NT_STATUS_NO_SUCH_DEVICE (0xC0000000 | 0x000e)
#define NT_STATUS_NO_SUCH_FILE (0xC0000000 | 0x000f)
#define NT_STATUS_INVALID_DEVICE_REQUEST (0xC0000000 | 0x0010)
#define NT_STATUS_END_OF_FILE (0xC0000000 | 0x0011)
#define NT_STATUS_WRONG_VOLUME (0xC0000000 | 0x0012)
#define NT_STATUS_NO_MEDIA_IN_DEVICE (0xC0000000 | 0x0013)
#define NT_STATUS_UNRECOGNIZED_MEDIA (0xC0000000 | 0x0014)
#define NT_STATUS_NONEXISTENT_SECTOR (0xC0000000 | 0x0015)
#define NT_STATUS_MORE_PROCESSING_REQUIRED (0xC0000000 | 0x0016)
#define NT_STATUS_NO_MEMORY (0xC0000000 | 0x0017)
#define NT_STATUS_CONFLICTING_ADDRESSES (0xC0000000 | 0x0018)
#define NT_STATUS_NOT_MAPPED_VIEW (0xC0000000 | 0x0019)
#define NT_STATUS_UNABLE_TO_FREE_VM (0x80000000 | 0x001a)
#define NT_STATUS_UNABLE_TO_DELETE_SECTION (0xC0000000 | 0x001b)
#define NT_STATUS_INVALID_SYSTEM_SERVICE (0xC0000000 | 0x001c)
#define NT_STATUS_ILLEGAL_INSTRUCTION (0xC0000000 | 0x001d)
#define NT_STATUS_INVALID_LOCK_SEQUENCE (0xC0000000 | 0x001e)
#define NT_STATUS_INVALID_VIEW_SIZE (0xC0000000 | 0x001f)
#define NT_STATUS_INVALID_FILE_FOR_SECTION (0xC0000000 | 0x0020)
#define NT_STATUS_ALREADY_COMMITTED (0xC0000000 | 0x0021)
#define NT_STATUS_ACCESS_DENIED (0xC0000000 | 0x0022)
#define NT_STATUS_BUFFER_TOO_SMALL (0xC0000000 | 0x0023)
#define NT_STATUS_OBJECT_TYPE_MISMATCH (0xC0000000 | 0x0024)
#define NT_STATUS_NONCONTINUABLE_EXCEPTION (0xC0000000 | 0x0025)
#define NT_STATUS_INVALID_DISPOSITION (0xC0000000 | 0x0026)
#define NT_STATUS_UNWIND (0xC0000000 | 0x0027)
#define NT_STATUS_BAD_STACK (0xC0000000 | 0x0028)
#define NT_STATUS_INVALID_UNWIND_TARGET (0xC0000000 | 0x0029)
#define NT_STATUS_NOT_LOCKED (0xC0000000 | 0x002a)
#define NT_STATUS_PARITY_ERROR (0xC0000000 | 0x002b)
#define NT_STATUS_UNABLE_TO_DECOMMIT_VM (0xC0000000 | 0x002c)
#define NT_STATUS_NOT_COMMITTED (0xC0000000 | 0x002d)
#define NT_STATUS_INVALID_PORT_ATTRIBUTES (0xC0000000 | 0x002e)
#define NT_STATUS_PORT_MESSAGE_TOO_LONG (0xC0000000 | 0x002f)
#define NT_STATUS_INVALID_PARAMETER_MIX (0xC0000000 | 0x0030)
#define NT_STATUS_INVALID_QUOTA_LOWER (0xC0000000 | 0x0031)
#define NT_STATUS_DISK_CORRUPT_ERROR (0xC0000000 | 0x0032)
#define NT_STATUS_OBJECT_NAME_INVALID (0xC0000000 | 0x0033)
#define NT_STATUS_OBJECT_NAME_NOT_FOUND (0xC0000000 | 0x0034)
#define NT_STATUS_OBJECT_NAME_COLLISION (0xC0000000 | 0x0035)
#define NT_STATUS_HANDLE_NOT_WAITABLE (0xC0000000 | 0x0036)
#define NT_STATUS_PORT_DISCONNECTED (0xC0000000 | 0x0037)
#define NT_STATUS_DEVICE_ALREADY_ATTACHED (0xC0000000 | 0x0038)
#define NT_STATUS_OBJECT_PATH_INVALID (0xC0000000 | 0x0039)
#define NT_STATUS_OBJECT_PATH_NOT_FOUND (0xC0000000 | 0x003a)
#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD (0xC0000000 | 0x003b)
#define NT_STATUS_DATA_OVERRUN (0xC0000000 | 0x003c)
#define NT_STATUS_DATA_LATE_ERROR (0xC0000000 | 0x003d)
#define NT_STATUS_DATA_ERROR (0xC0000000 | 0x003e)
#define NT_STATUS_CRC_ERROR (0xC0000000 | 0x003f)
#define NT_STATUS_SECTION_TOO_BIG (0xC0000000 | 0x0040)
#define NT_STATUS_PORT_CONNECTION_REFUSED (0xC0000000 | 0x0041)
#define NT_STATUS_INVALID_PORT_HANDLE (0xC0000000 | 0x0042)
#define NT_STATUS_SHARING_VIOLATION (0xC0000000 | 0x0043)
#define NT_STATUS_QUOTA_EXCEEDED (0xC0000000 | 0x0044)
#define NT_STATUS_INVALID_PAGE_PROTECTION (0xC0000000 | 0x0045)
#define NT_STATUS_MUTANT_NOT_OWNED (0xC0000000 | 0x0046)
#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED (0xC0000000 | 0x0047)
#define NT_STATUS_PORT_ALREADY_SET (0xC0000000 | 0x0048)
#define NT_STATUS_SECTION_NOT_IMAGE (0xC0000000 | 0x0049)
#define NT_STATUS_SUSPEND_COUNT_EXCEEDED (0xC0000000 | 0x004a)
#define NT_STATUS_THREAD_IS_TERMINATING (0xC0000000 | 0x004b)
#define NT_STATUS_BAD_WORKING_SET_LIMIT (0xC0000000 | 0x004c)
#define NT_STATUS_INCOMPATIBLE_FILE_MAP (0xC0000000 | 0x004d)
#define NT_STATUS_SECTION_PROTECTION (0xC0000000 | 0x004e)
#define NT_STATUS_EAS_NOT_SUPPORTED (0xC0000000 | 0x004f)
#define NT_STATUS_EA_TOO_LARGE (0xC0000000 | 0x0050)
#define NT_STATUS_NONEXISTENT_EA_ENTRY (0xC0000000 | 0x0051)
#define NT_STATUS_NO_EAS_ON_FILE (0xC0000000 | 0x0052)
#define NT_STATUS_EA_CORRUPT_ERROR (0xC0000000 | 0x0053)
#define NT_STATUS_FILE_LOCK_CONFLICT (0xC0000000 | 0x0054)
#define NT_STATUS_LOCK_NOT_GRANTED (0xC0000000 | 0x0055)
#define NT_STATUS_DELETE_PENDING (0xC0000000 | 0x0056)
#define NT_STATUS_CTL_FILE_NOT_SUPPORTED (0xC0000000 | 0x0057)
#define NT_STATUS_UNKNOWN_REVISION (0xC0000000 | 0x0058)
#define NT_STATUS_REVISION_MISMATCH (0xC0000000 | 0x0059)
#define NT_STATUS_INVALID_OWNER (0xC0000000 | 0x005a)
#define NT_STATUS_INVALID_PRIMARY_GROUP (0xC0000000 | 0x005b)
#define NT_STATUS_NO_IMPERSONATION_TOKEN (0xC0000000 | 0x005c)
#define NT_STATUS_CANT_DISABLE_MANDATORY (0xC0000000 | 0x005d)
#define NT_STATUS_NO_LOGON_SERVERS (0xC0000000 | 0x005e)
#define NT_STATUS_NO_SUCH_LOGON_SESSION (0xC0000000 | 0x005f)
#define NT_STATUS_NO_SUCH_PRIVILEGE (0xC0000000 | 0x0060)
#define NT_STATUS_PRIVILEGE_NOT_HELD (0xC0000000 | 0x0061)
#define NT_STATUS_INVALID_ACCOUNT_NAME (0xC0000000 | 0x0062)
#define NT_STATUS_USER_EXISTS (0xC0000000 | 0x0063)
#define NT_STATUS_NO_SUCH_USER (0xC0000000 | 0x0064)
#define NT_STATUS_GROUP_EXISTS (0xC0000000 | 0x0065)
#define NT_STATUS_NO_SUCH_GROUP (0xC0000000 | 0x0066)
#define NT_STATUS_MEMBER_IN_GROUP (0xC0000000 | 0x0067)
#define NT_STATUS_MEMBER_NOT_IN_GROUP (0xC0000000 | 0x0068)
#define NT_STATUS_LAST_ADMIN (0xC0000000 | 0x0069)
#define NT_STATUS_WRONG_PASSWORD (0xC0000000 | 0x006a)
#define NT_STATUS_ILL_FORMED_PASSWORD (0xC0000000 | 0x006b)
#define NT_STATUS_PASSWORD_RESTRICTION (0xC0000000 | 0x006c)
#define NT_STATUS_LOGON_FAILURE (0xC0000000 | 0x006d)
#define NT_STATUS_ACCOUNT_RESTRICTION (0xC0000000 | 0x006e)
#define NT_STATUS_INVALID_LOGON_HOURS (0xC0000000 | 0x006f)
#define NT_STATUS_INVALID_WORKSTATION (0xC0000000 | 0x0070)
#define NT_STATUS_PASSWORD_EXPIRED (0xC0000000 | 0x0071)
#define NT_STATUS_ACCOUNT_DISABLED (0xC0000000 | 0x0072)
#define NT_STATUS_NONE_MAPPED (0xC0000000 | 0x0073)
#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED (0xC0000000 | 0x0074)
#define NT_STATUS_LUIDS_EXHAUSTED (0xC0000000 | 0x0075)
#define NT_STATUS_INVALID_SUB_AUTHORITY (0xC0000000 | 0x0076)
#define NT_STATUS_INVALID_ACL (0xC0000000 | 0x0077)
#define NT_STATUS_INVALID_SID (0xC0000000 | 0x0078)
#define NT_STATUS_INVALID_SECURITY_DESCR (0xC0000000 | 0x0079)
#define NT_STATUS_PROCEDURE_NOT_FOUND (0xC0000000 | 0x007a)
#define NT_STATUS_INVALID_IMAGE_FORMAT (0xC0000000 | 0x007b)
#define NT_STATUS_NO_TOKEN (0xC0000000 | 0x007c)
#define NT_STATUS_BAD_INHERITANCE_ACL (0xC0000000 | 0x007d)
#define NT_STATUS_RANGE_NOT_LOCKED (0xC0000000 | 0x007e)
#define NT_STATUS_DISK_FULL (0xC0000000 | 0x007f)
#define NT_STATUS_SERVER_DISABLED (0xC0000000 | 0x0080)
#define NT_STATUS_SERVER_NOT_DISABLED (0xC0000000 | 0x0081)
#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED (0xC0000000 | 0x0082)
#define NT_STATUS_GUIDS_EXHAUSTED (0xC0000000 | 0x0083)
#define NT_STATUS_INVALID_ID_AUTHORITY (0xC0000000 | 0x0084)
#define NT_STATUS_AGENTS_EXHAUSTED (0xC0000000 | 0x0085)
#define NT_STATUS_INVALID_VOLUME_LABEL (0xC0000000 | 0x0086)
#define NT_STATUS_SECTION_NOT_EXTENDED (0xC0000000 | 0x0087)
#define NT_STATUS_NOT_MAPPED_DATA (0xC0000000 | 0x0088)
#define NT_STATUS_RESOURCE_DATA_NOT_FOUND (0xC0000000 | 0x0089)
#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND (0xC0000000 | 0x008a)
#define NT_STATUS_RESOURCE_NAME_NOT_FOUND (0xC0000000 | 0x008b)
#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED (0xC0000000 | 0x008c)
#define NT_STATUS_FLOAT_DENORMAL_OPERAND (0xC0000000 | 0x008d)
#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO (0xC0000000 | 0x008e)
#define NT_STATUS_FLOAT_INEXACT_RESULT (0xC0000000 | 0x008f)
#define NT_STATUS_FLOAT_INVALID_OPERATION (0xC0000000 | 0x0090)
#define NT_STATUS_FLOAT_OVERFLOW (0xC0000000 | 0x0091)
#define NT_STATUS_FLOAT_STACK_CHECK (0xC0000000 | 0x0092)
#define NT_STATUS_FLOAT_UNDERFLOW (0xC0000000 | 0x0093)
#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO (0xC0000000 | 0x0094)
#define NT_STATUS_INTEGER_OVERFLOW (0xC0000000 | 0x0095)
#define NT_STATUS_PRIVILEGED_INSTRUCTION (0xC0000000 | 0x0096)
#define NT_STATUS_TOO_MANY_PAGING_FILES (0xC0000000 | 0x0097)
#define NT_STATUS_FILE_INVALID (0xC0000000 | 0x0098)
#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED (0xC0000000 | 0x0099)
#define NT_STATUS_INSUFFICIENT_RESOURCES (0xC0000000 | 0x009a)
#define NT_STATUS_DFS_EXIT_PATH_FOUND (0xC0000000 | 0x009b)
#define NT_STATUS_DEVICE_DATA_ERROR (0xC0000000 | 0x009c)
#define NT_STATUS_DEVICE_NOT_CONNECTED (0xC0000000 | 0x009d)
#define NT_STATUS_DEVICE_POWER_FAILURE (0xC0000000 | 0x009e)
#define NT_STATUS_FREE_VM_NOT_AT_BASE (0xC0000000 | 0x009f)
#define NT_STATUS_MEMORY_NOT_ALLOCATED (0xC0000000 | 0x00a0)
#define NT_STATUS_WORKING_SET_QUOTA (0xC0000000 | 0x00a1)
#define NT_STATUS_MEDIA_WRITE_PROTECTED (0xC0000000 | 0x00a2)
#define NT_STATUS_DEVICE_NOT_READY (0xC0000000 | 0x00a3)
#define NT_STATUS_INVALID_GROUP_ATTRIBUTES (0xC0000000 | 0x00a4)
#define NT_STATUS_BAD_IMPERSONATION_LEVEL (0xC0000000 | 0x00a5)
#define NT_STATUS_CANT_OPEN_ANONYMOUS (0xC0000000 | 0x00a6)
#define NT_STATUS_BAD_VALIDATION_CLASS (0xC0000000 | 0x00a7)
#define NT_STATUS_BAD_TOKEN_TYPE (0xC0000000 | 0x00a8)
#define NT_STATUS_BAD_MASTER_BOOT_RECORD (0xC0000000 | 0x00a9)
#define NT_STATUS_INSTRUCTION_MISALIGNMENT (0xC0000000 | 0x00aa)
#define NT_STATUS_INSTANCE_NOT_AVAILABLE (0xC0000000 | 0x00ab)
#define NT_STATUS_PIPE_NOT_AVAILABLE (0xC0000000 | 0x00ac)
#define NT_STATUS_INVALID_PIPE_STATE (0xC0000000 | 0x00ad)
#define NT_STATUS_PIPE_BUSY (0xC0000000 | 0x00ae)
#define NT_STATUS_ILLEGAL_FUNCTION (0xC0000000 | 0x00af)
#define NT_STATUS_PIPE_DISCONNECTED (0xC0000000 | 0x00b0)
#define NT_STATUS_PIPE_CLOSING (0xC0000000 | 0x00b1)
#define NT_STATUS_PIPE_CONNECTED (0xC0000000 | 0x00b2)
#define NT_STATUS_PIPE_LISTENING (0xC0000000 | 0x00b3)
#define NT_STATUS_INVALID_READ_MODE (0xC0000000 | 0x00b4)
#define NT_STATUS_IO_TIMEOUT (0xC0000000 | 0x00b5)
#define NT_STATUS_FILE_FORCED_CLOSED (0xC0000000 | 0x00b6)
#define NT_STATUS_PROFILING_NOT_STARTED (0xC0000000 | 0x00b7)
#define NT_STATUS_PROFILING_NOT_STOPPED (0xC0000000 | 0x00b8)
#define NT_STATUS_COULD_NOT_INTERPRET (0xC0000000 | 0x00b9)
#define NT_STATUS_FILE_IS_A_DIRECTORY (0xC0000000 | 0x00ba)
#define NT_STATUS_NOT_SUPPORTED (0xC0000000 | 0x00bb)
#define NT_STATUS_REMOTE_NOT_LISTENING (0xC0000000 | 0x00bc)
#define NT_STATUS_DUPLICATE_NAME (0xC0000000 | 0x00bd)
#define NT_STATUS_BAD_NETWORK_PATH (0xC0000000 | 0x00be)
#define NT_STATUS_NETWORK_BUSY (0xC0000000 | 0x00bf)
#define NT_STATUS_DEVICE_DOES_NOT_EXIST (0xC0000000 | 0x00c0)
#define NT_STATUS_TOO_MANY_COMMANDS (0xC0000000 | 0x00c1)
#define NT_STATUS_ADAPTER_HARDWARE_ERROR (0xC0000000 | 0x00c2)
#define NT_STATUS_INVALID_NETWORK_RESPONSE (0xC0000000 | 0x00c3)
#define NT_STATUS_UNEXPECTED_NETWORK_ERROR (0xC0000000 | 0x00c4)
#define NT_STATUS_BAD_REMOTE_ADAPTER (0xC0000000 | 0x00c5)
#define NT_STATUS_PRINT_QUEUE_FULL (0xC0000000 | 0x00c6)
#define NT_STATUS_NO_SPOOL_SPACE (0xC0000000 | 0x00c7)
#define NT_STATUS_PRINT_CANCELLED (0xC0000000 | 0x00c8)
#define NT_STATUS_NETWORK_NAME_DELETED (0xC0000000 | 0x00c9)
#define NT_STATUS_NETWORK_ACCESS_DENIED (0xC0000000 | 0x00ca)
#define NT_STATUS_BAD_DEVICE_TYPE (0xC0000000 | 0x00cb)
#define NT_STATUS_BAD_NETWORK_NAME (0xC0000000 | 0x00cc)
#define NT_STATUS_TOO_MANY_NAMES (0xC0000000 | 0x00cd)
#define NT_STATUS_TOO_MANY_SESSIONS (0xC0000000 | 0x00ce)
#define NT_STATUS_SHARING_PAUSED (0xC0000000 | 0x00cf)
#define NT_STATUS_REQUEST_NOT_ACCEPTED (0xC0000000 | 0x00d0)
#define NT_STATUS_REDIRECTOR_PAUSED (0xC0000000 | 0x00d1)
#define NT_STATUS_NET_WRITE_FAULT (0xC0000000 | 0x00d2)
#define NT_STATUS_PROFILING_AT_LIMIT (0xC0000000 | 0x00d3)
#define NT_STATUS_NOT_SAME_DEVICE (0xC0000000 | 0x00d4)
#define NT_STATUS_FILE_RENAMED (0xC0000000 | 0x00d5)
#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED (0xC0000000 | 0x00d6)
#define NT_STATUS_NO_SECURITY_ON_OBJECT (0xC0000000 | 0x00d7)
#define NT_STATUS_CANT_WAIT (0xC0000000 | 0x00d8)
#define NT_STATUS_PIPE_EMPTY (0xC0000000 | 0x00d9)
#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO (0xC0000000 | 0x00da)
#define NT_STATUS_CANT_TERMINATE_SELF (0xC0000000 | 0x00db)
#define NT_STATUS_INVALID_SERVER_STATE (0xC0000000 | 0x00dc)
#define NT_STATUS_INVALID_DOMAIN_STATE (0xC0000000 | 0x00dd)
#define NT_STATUS_INVALID_DOMAIN_ROLE (0xC0000000 | 0x00de)
#define NT_STATUS_NO_SUCH_DOMAIN (0xC0000000 | 0x00df)
#define NT_STATUS_DOMAIN_EXISTS (0xC0000000 | 0x00e0)
#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED (0xC0000000 | 0x00e1)
#define NT_STATUS_OPLOCK_NOT_GRANTED (0xC0000000 | 0x00e2)
#define NT_STATUS_INVALID_OPLOCK_PROTOCOL (0xC0000000 | 0x00e3)
#define NT_STATUS_INTERNAL_DB_CORRUPTION (0xC0000000 | 0x00e4)
#define NT_STATUS_INTERNAL_ERROR (0xC0000000 | 0x00e5)
#define NT_STATUS_GENERIC_NOT_MAPPED (0xC0000000 | 0x00e6)
#define NT_STATUS_BAD_DESCRIPTOR_FORMAT (0xC0000000 | 0x00e7)
#define NT_STATUS_INVALID_USER_BUFFER (0xC0000000 | 0x00e8)
#define NT_STATUS_UNEXPECTED_IO_ERROR (0xC0000000 | 0x00e9)
#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR (0xC0000000 | 0x00ea)
#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR (0xC0000000 | 0x00eb)
#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR (0xC0000000 | 0x00ec)
#define NT_STATUS_NOT_LOGON_PROCESS (0xC0000000 | 0x00ed)
#define NT_STATUS_LOGON_SESSION_EXISTS (0xC0000000 | 0x00ee)
#define NT_STATUS_INVALID_PARAMETER_1 (0xC0000000 | 0x00ef)
#define NT_STATUS_INVALID_PARAMETER_2 (0xC0000000 | 0x00f0)
#define NT_STATUS_INVALID_PARAMETER_3 (0xC0000000 | 0x00f1)
#define NT_STATUS_INVALID_PARAMETER_4 (0xC0000000 | 0x00f2)
#define NT_STATUS_INVALID_PARAMETER_5 (0xC0000000 | 0x00f3)
#define NT_STATUS_INVALID_PARAMETER_6 (0xC0000000 | 0x00f4)
#define NT_STATUS_INVALID_PARAMETER_7 (0xC0000000 | 0x00f5)
#define NT_STATUS_INVALID_PARAMETER_8 (0xC0000000 | 0x00f6)
#define NT_STATUS_INVALID_PARAMETER_9 (0xC0000000 | 0x00f7)
#define NT_STATUS_INVALID_PARAMETER_10 (0xC0000000 | 0x00f8)
#define NT_STATUS_INVALID_PARAMETER_11 (0xC0000000 | 0x00f9)
#define NT_STATUS_INVALID_PARAMETER_12 (0xC0000000 | 0x00fa)
#define NT_STATUS_REDIRECTOR_NOT_STARTED (0xC0000000 | 0x00fb)
#define NT_STATUS_REDIRECTOR_STARTED (0xC0000000 | 0x00fc)
#define NT_STATUS_STACK_OVERFLOW (0xC0000000 | 0x00fd)
#define NT_STATUS_NO_SUCH_PACKAGE (0xC0000000 | 0x00fe)
#define NT_STATUS_BAD_FUNCTION_TABLE (0xC0000000 | 0x00ff)
#define NT_STATUS_DIRECTORY_NOT_EMPTY (0xC0000000 | 0x0101)
#define NT_STATUS_FILE_CORRUPT_ERROR (0xC0000000 | 0x0102)
#define NT_STATUS_NOT_A_DIRECTORY (0xC0000000 | 0x0103)
#define NT_STATUS_BAD_LOGON_SESSION_STATE (0xC0000000 | 0x0104)
#define NT_STATUS_LOGON_SESSION_COLLISION (0xC0000000 | 0x0105)
#define NT_STATUS_NAME_TOO_LONG (0xC0000000 | 0x0106)
#define NT_STATUS_FILES_OPEN (0xC0000000 | 0x0107)
#define NT_STATUS_CONNECTION_IN_USE (0xC0000000 | 0x0108)
#define NT_STATUS_MESSAGE_NOT_FOUND (0xC0000000 | 0x0109)
#define NT_STATUS_PROCESS_IS_TERMINATING (0xC0000000 | 0x010a)
#define NT_STATUS_INVALID_LOGON_TYPE (0xC0000000 | 0x010b)
#define NT_STATUS_NO_GUID_TRANSLATION (0xC0000000 | 0x010c)
#define NT_STATUS_CANNOT_IMPERSONATE (0xC0000000 | 0x010d)
#define NT_STATUS_IMAGE_ALREADY_LOADED (0xC0000000 | 0x010e)
#define NT_STATUS_ABIOS_NOT_PRESENT (0xC0000000 | 0x010f)
#define NT_STATUS_ABIOS_LID_NOT_EXIST (0xC0000000 | 0x0110)
#define NT_STATUS_ABIOS_LID_ALREADY_OWNED (0xC0000000 | 0x0111)
#define NT_STATUS_ABIOS_NOT_LID_OWNER (0xC0000000 | 0x0112)
#define NT_STATUS_ABIOS_INVALID_COMMAND (0xC0000000 | 0x0113)
#define NT_STATUS_ABIOS_INVALID_LID (0xC0000000 | 0x0114)
#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE (0xC0000000 | 0x0115)
#define NT_STATUS_ABIOS_INVALID_SELECTOR (0xC0000000 | 0x0116)
#define NT_STATUS_NO_LDT (0xC0000000 | 0x0117)
#define NT_STATUS_INVALID_LDT_SIZE (0xC0000000 | 0x0118)
#define NT_STATUS_INVALID_LDT_OFFSET (0xC0000000 | 0x0119)
#define NT_STATUS_INVALID_LDT_DESCRIPTOR (0xC0000000 | 0x011a)
#define NT_STATUS_INVALID_IMAGE_NE_FORMAT (0xC0000000 | 0x011b)
#define NT_STATUS_RXACT_INVALID_STATE (0xC0000000 | 0x011c)
#define NT_STATUS_RXACT_COMMIT_FAILURE (0xC0000000 | 0x011d)
#define NT_STATUS_MAPPED_FILE_SIZE_ZERO (0xC0000000 | 0x011e)
#define NT_STATUS_TOO_MANY_OPENED_FILES (0xC0000000 | 0x011f)
#define NT_STATUS_CANCELLED (0xC0000000 | 0x0120)
#define NT_STATUS_CANNOT_DELETE (0xC0000000 | 0x0121)
#define NT_STATUS_INVALID_COMPUTER_NAME (0xC0000000 | 0x0122)
#define NT_STATUS_FILE_DELETED (0xC0000000 | 0x0123)
#define NT_STATUS_SPECIAL_ACCOUNT (0xC0000000 | 0x0124)
#define NT_STATUS_SPECIAL_GROUP (0xC0000000 | 0x0125)
#define NT_STATUS_SPECIAL_USER (0xC0000000 | 0x0126)
#define NT_STATUS_MEMBERS_PRIMARY_GROUP (0xC0000000 | 0x0127)
#define NT_STATUS_FILE_CLOSED (0xC0000000 | 0x0128)
#define NT_STATUS_TOO_MANY_THREADS (0xC0000000 | 0x0129)
#define NT_STATUS_THREAD_NOT_IN_PROCESS (0xC0000000 | 0x012a)
#define NT_STATUS_TOKEN_ALREADY_IN_USE (0xC0000000 | 0x012b)
#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED (0xC0000000 | 0x012c)
#define NT_STATUS_COMMITMENT_LIMIT (0xC0000000 | 0x012d)
#define NT_STATUS_INVALID_IMAGE_LE_FORMAT (0xC0000000 | 0x012e)
#define NT_STATUS_INVALID_IMAGE_NOT_MZ (0xC0000000 | 0x012f)
#define NT_STATUS_INVALID_IMAGE_PROTECT (0xC0000000 | 0x0130)
#define NT_STATUS_INVALID_IMAGE_WIN_16 (0xC0000000 | 0x0131)
#define NT_STATUS_LOGON_SERVER_CONFLICT (0xC0000000 | 0x0132)
#define NT_STATUS_TIME_DIFFERENCE_AT_DC (0xC0000000 | 0x0133)
#define NT_STATUS_SYNCHRONIZATION_REQUIRED (0xC0000000 | 0x0134)
#define NT_STATUS_DLL_NOT_FOUND (0xC0000000 | 0x0135)
#define NT_STATUS_OPEN_FAILED (0xC0000000 | 0x0136)
#define NT_STATUS_IO_PRIVILEGE_FAILED (0xC0000000 | 0x0137)
#define NT_STATUS_ORDINAL_NOT_FOUND (0xC0000000 | 0x0138)
#define NT_STATUS_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0139)
#define NT_STATUS_CONTROL_C_EXIT (0xC0000000 | 0x013a)
#define NT_STATUS_LOCAL_DISCONNECT (0xC0000000 | 0x013b)
#define NT_STATUS_REMOTE_DISCONNECT (0xC0000000 | 0x013c)
#define NT_STATUS_REMOTE_RESOURCES (0xC0000000 | 0x013d)
#define NT_STATUS_LINK_FAILED (0xC0000000 | 0x013e)
#define NT_STATUS_LINK_TIMEOUT (0xC0000000 | 0x013f)
#define NT_STATUS_INVALID_CONNECTION (0xC0000000 | 0x0140)
#define NT_STATUS_INVALID_ADDRESS (0xC0000000 | 0x0141)
#define NT_STATUS_DLL_INIT_FAILED (0xC0000000 | 0x0142)
#define NT_STATUS_MISSING_SYSTEMFILE (0xC0000000 | 0x0143)
#define NT_STATUS_UNHANDLED_EXCEPTION (0xC0000000 | 0x0144)
#define NT_STATUS_APP_INIT_FAILURE (0xC0000000 | 0x0145)
#define NT_STATUS_PAGEFILE_CREATE_FAILED (0xC0000000 | 0x0146)
#define NT_STATUS_NO_PAGEFILE (0xC0000000 | 0x0147)
#define NT_STATUS_INVALID_LEVEL (0xC0000000 | 0x0148)
#define NT_STATUS_WRONG_PASSWORD_CORE (0xC0000000 | 0x0149)
#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT (0xC0000000 | 0x014a)
#define NT_STATUS_PIPE_BROKEN (0xC0000000 | 0x014b)
#define NT_STATUS_REGISTRY_CORRUPT (0xC0000000 | 0x014c)
#define NT_STATUS_REGISTRY_IO_FAILED (0xC0000000 | 0x014d)
#define NT_STATUS_NO_EVENT_PAIR (0xC0000000 | 0x014e)
#define NT_STATUS_UNRECOGNIZED_VOLUME (0xC0000000 | 0x014f)
#define NT_STATUS_SERIAL_NO_DEVICE_INITED (0xC0000000 | 0x0150)
#define NT_STATUS_NO_SUCH_ALIAS (0xC0000000 | 0x0151)
#define NT_STATUS_MEMBER_NOT_IN_ALIAS (0xC0000000 | 0x0152)
#define NT_STATUS_MEMBER_IN_ALIAS (0xC0000000 | 0x0153)
#define NT_STATUS_ALIAS_EXISTS (0xC0000000 | 0x0154)
#define NT_STATUS_LOGON_NOT_GRANTED (0xC0000000 | 0x0155)
#define NT_STATUS_TOO_MANY_SECRETS (0xC0000000 | 0x0156)
#define NT_STATUS_SECRET_TOO_LONG (0xC0000000 | 0x0157)
#define NT_STATUS_INTERNAL_DB_ERROR (0xC0000000 | 0x0158)
#define NT_STATUS_FULLSCREEN_MODE (0xC0000000 | 0x0159)
#define NT_STATUS_TOO_MANY_CONTEXT_IDS (0xC0000000 | 0x015a)
#define NT_STATUS_LOGON_TYPE_NOT_GRANTED (0xC0000000 | 0x015b)
#define NT_STATUS_NOT_REGISTRY_FILE (0xC0000000 | 0x015c)
#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x015d)
#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR (0xC0000000 | 0x015e)
#define NT_STATUS_FT_MISSING_MEMBER (0xC0000000 | 0x015f)
#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY (0xC0000000 | 0x0160)
#define NT_STATUS_ILLEGAL_CHARACTER (0xC0000000 | 0x0161)
#define NT_STATUS_UNMAPPABLE_CHARACTER (0xC0000000 | 0x0162)
#define NT_STATUS_UNDEFINED_CHARACTER (0xC0000000 | 0x0163)
#define NT_STATUS_FLOPPY_VOLUME (0xC0000000 | 0x0164)
#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND (0xC0000000 | 0x0165)
#define NT_STATUS_FLOPPY_WRONG_CYLINDER (0xC0000000 | 0x0166)
#define NT_STATUS_FLOPPY_UNKNOWN_ERROR (0xC0000000 | 0x0167)
#define NT_STATUS_FLOPPY_BAD_REGISTERS (0xC0000000 | 0x0168)
#define NT_STATUS_DISK_RECALIBRATE_FAILED (0xC0000000 | 0x0169)
#define NT_STATUS_DISK_OPERATION_FAILED (0xC0000000 | 0x016a)
#define NT_STATUS_DISK_RESET_FAILED (0xC0000000 | 0x016b)
#define NT_STATUS_SHARED_IRQ_BUSY (0xC0000000 | 0x016c)
#define NT_STATUS_FT_ORPHANING (0xC0000000 | 0x016d)
#define NT_STATUS_PARTITION_FAILURE (0xC0000000 | 0x0172)
#define NT_STATUS_INVALID_BLOCK_LENGTH (0xC0000000 | 0x0173)
#define NT_STATUS_DEVICE_NOT_PARTITIONED (0xC0000000 | 0x0174)
#define NT_STATUS_UNABLE_TO_LOCK_MEDIA (0xC0000000 | 0x0175)
#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA (0xC0000000 | 0x0176)
#define NT_STATUS_EOM_OVERFLOW (0xC0000000 | 0x0177)
#define NT_STATUS_NO_MEDIA (0xC0000000 | 0x0178)
#define NT_STATUS_NO_SUCH_MEMBER (0xC0000000 | 0x017a)
#define NT_STATUS_INVALID_MEMBER (0xC0000000 | 0x017b)
#define NT_STATUS_KEY_DELETED (0xC0000000 | 0x017c)
#define NT_STATUS_NO_LOG_SPACE (0xC0000000 | 0x017d)
#define NT_STATUS_TOO_MANY_SIDS (0xC0000000 | 0x017e)
#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x017f)
#define NT_STATUS_KEY_HAS_CHILDREN (0xC0000000 | 0x0180)
#define NT_STATUS_CHILD_MUST_BE_VOLATILE (0xC0000000 | 0x0181)
#define NT_STATUS_DEVICE_CONFIGURATION_ERROR (0xC0000000 | 0x0182)
#define NT_STATUS_DRIVER_INTERNAL_ERROR (0xC0000000 | 0x0183)
#define NT_STATUS_INVALID_DEVICE_STATE (0xC0000000 | 0x0184)
#define NT_STATUS_IO_DEVICE_ERROR (0xC0000000 | 0x0185)
#define NT_STATUS_DEVICE_PROTOCOL_ERROR (0xC0000000 | 0x0186)
#define NT_STATUS_BACKUP_CONTROLLER (0xC0000000 | 0x0187)
#define NT_STATUS_LOG_FILE_FULL (0xC0000000 | 0x0188)
#define NT_STATUS_TOO_LATE (0xC0000000 | 0x0189)
#define NT_STATUS_NO_TRUST_LSA_SECRET (0xC0000000 | 0x018a)
#define NT_STATUS_NO_TRUST_SAM_ACCOUNT (0xC0000000 | 0x018b)
#define NT_STATUS_TRUSTED_DOMAIN_FAILURE (0xC0000000 | 0x018c)
#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE (0xC0000000 | 0x018d)
#define NT_STATUS_EVENTLOG_FILE_CORRUPT (0xC0000000 | 0x018e)
#define NT_STATUS_EVENTLOG_CANT_START (0xC0000000 | 0x018f)
#define NT_STATUS_TRUST_FAILURE (0xC0000000 | 0x0190)
#define NT_STATUS_MUTANT_LIMIT_EXCEEDED (0xC0000000 | 0x0191)
#define NT_STATUS_NETLOGON_NOT_STARTED (0xC0000000 | 0x0192)
#define NT_STATUS_ACCOUNT_EXPIRED (0xC0000000 | 0x0193)
#define NT_STATUS_POSSIBLE_DEADLOCK (0xC0000000 | 0x0194)
#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT (0xC0000000 | 0x0195)
#define NT_STATUS_REMOTE_SESSION_LIMIT (0xC0000000 | 0x0196)
#define NT_STATUS_EVENTLOG_FILE_CHANGED (0xC0000000 | 0x0197)
#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT (0xC0000000 | 0x0198)
#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT (0xC0000000 | 0x0199)
#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT (0xC0000000 | 0x019a)
#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT (0xC0000000 | 0x019b)
#define NT_STATUS_FS_DRIVER_REQUIRED (0xC0000000 | 0x019c)
#define NT_STATUS_NO_USER_SESSION_KEY (0xC0000000 | 0x0202)
#define NT_STATUS_USER_SESSION_DELETED (0xC0000000 | 0x0203)
#define NT_STATUS_RESOURCE_LANG_NOT_FOUND (0xC0000000 | 0x0204)
#define NT_STATUS_INSUFF_SERVER_RESOURCES (0xC0000000 | 0x0205)
#define NT_STATUS_INVALID_BUFFER_SIZE (0xC0000000 | 0x0206)
#define NT_STATUS_INVALID_ADDRESS_COMPONENT (0xC0000000 | 0x0207)
#define NT_STATUS_INVALID_ADDRESS_WILDCARD (0xC0000000 | 0x0208)
#define NT_STATUS_TOO_MANY_ADDRESSES (0xC0000000 | 0x0209)
#define NT_STATUS_ADDRESS_ALREADY_EXISTS (0xC0000000 | 0x020a)
#define NT_STATUS_ADDRESS_CLOSED (0xC0000000 | 0x020b)
#define NT_STATUS_CONNECTION_DISCONNECTED (0xC0000000 | 0x020c)
#define NT_STATUS_CONNECTION_RESET (0xC0000000 | 0x020d)
#define NT_STATUS_TOO_MANY_NODES (0xC0000000 | 0x020e)
#define NT_STATUS_TRANSACTION_ABORTED (0xC0000000 | 0x020f)
#define NT_STATUS_TRANSACTION_TIMED_OUT (0xC0000000 | 0x0210)
#define NT_STATUS_TRANSACTION_NO_RELEASE (0xC0000000 | 0x0211)
#define NT_STATUS_TRANSACTION_NO_MATCH (0xC0000000 | 0x0212)
#define NT_STATUS_TRANSACTION_RESPONDED (0xC0000000 | 0x0213)
#define NT_STATUS_TRANSACTION_INVALID_ID (0xC0000000 | 0x0214)
#define NT_STATUS_TRANSACTION_INVALID_TYPE (0xC0000000 | 0x0215)
#define NT_STATUS_NOT_SERVER_SESSION (0xC0000000 | 0x0216)
#define NT_STATUS_NOT_CLIENT_SESSION (0xC0000000 | 0x0217)
#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE (0xC0000000 | 0x0218)
#define NT_STATUS_DEBUG_ATTACH_FAILED (0xC0000000 | 0x0219)
#define NT_STATUS_SYSTEM_PROCESS_TERMINATED (0xC0000000 | 0x021a)
#define NT_STATUS_DATA_NOT_ACCEPTED (0xC0000000 | 0x021b)
#define NT_STATUS_NO_BROWSER_SERVERS_FOUND (0xC0000000 | 0x021c)
#define NT_STATUS_VDM_HARD_ERROR (0xC0000000 | 0x021d)
#define NT_STATUS_DRIVER_CANCEL_TIMEOUT (0xC0000000 | 0x021e)
#define NT_STATUS_REPLY_MESSAGE_MISMATCH (0xC0000000 | 0x021f)
#define NT_STATUS_MAPPED_ALIGNMENT (0xC0000000 | 0x0220)
#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH (0xC0000000 | 0x0221)
#define NT_STATUS_LOST_WRITEBEHIND_DATA (0xC0000000 | 0x0222)
#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID (0xC0000000 | 0x0223)
#define NT_STATUS_PASSWORD_MUST_CHANGE (0xC0000000 | 0x0224)
#define NT_STATUS_NOT_FOUND (0xC0000000 | 0x0225)
#define NT_STATUS_NOT_TINY_STREAM (0xC0000000 | 0x0226)
#define NT_STATUS_RECOVERY_FAILURE (0xC0000000 | 0x0227)
#define NT_STATUS_STACK_OVERFLOW_READ (0xC0000000 | 0x0228)
#define NT_STATUS_FAIL_CHECK (0xC0000000 | 0x0229)
#define NT_STATUS_DUPLICATE_OBJECTID (0xC0000000 | 0x022a)
#define NT_STATUS_OBJECTID_EXISTS (0xC0000000 | 0x022b)
#define NT_STATUS_CONVERT_TO_LARGE (0xC0000000 | 0x022c)
#define NT_STATUS_RETRY (0xC0000000 | 0x022d)
#define NT_STATUS_FOUND_OUT_OF_SCOPE (0xC0000000 | 0x022e)
#define NT_STATUS_ALLOCATE_BUCKET (0xC0000000 | 0x022f)
#define NT_STATUS_PROPSET_NOT_FOUND (0xC0000000 | 0x0230)
#define NT_STATUS_MARSHALL_OVERFLOW (0xC0000000 | 0x0231)
#define NT_STATUS_INVALID_VARIANT (0xC0000000 | 0x0232)
#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND (0xC0000000 | 0x0233)
#define NT_STATUS_ACCOUNT_LOCKED_OUT (0xC0000000 | 0x0234)
#define NT_STATUS_HANDLE_NOT_CLOSABLE (0xC0000000 | 0x0235)
#define NT_STATUS_CONNECTION_REFUSED (0xC0000000 | 0x0236)
#define NT_STATUS_GRACEFUL_DISCONNECT (0xC0000000 | 0x0237)
#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED (0xC0000000 | 0x0238)
#define NT_STATUS_ADDRESS_NOT_ASSOCIATED (0xC0000000 | 0x0239)
#define NT_STATUS_CONNECTION_INVALID (0xC0000000 | 0x023a)
#define NT_STATUS_CONNECTION_ACTIVE (0xC0000000 | 0x023b)
#define NT_STATUS_NETWORK_UNREACHABLE (0xC0000000 | 0x023c)
#define NT_STATUS_HOST_UNREACHABLE (0xC0000000 | 0x023d)
#define NT_STATUS_PROTOCOL_UNREACHABLE (0xC0000000 | 0x023e)
#define NT_STATUS_PORT_UNREACHABLE (0xC0000000 | 0x023f)
#define NT_STATUS_REQUEST_ABORTED (0xC0000000 | 0x0240)
#define NT_STATUS_CONNECTION_ABORTED (0xC0000000 | 0x0241)
#define NT_STATUS_BAD_COMPRESSION_BUFFER (0xC0000000 | 0x0242)
#define NT_STATUS_USER_MAPPED_FILE (0xC0000000 | 0x0243)
#define NT_STATUS_AUDIT_FAILED (0xC0000000 | 0x0244)
#define NT_STATUS_TIMER_RESOLUTION_NOT_SET (0xC0000000 | 0x0245)
#define NT_STATUS_CONNECTION_COUNT_LIMIT (0xC0000000 | 0x0246)
#define NT_STATUS_LOGIN_TIME_RESTRICTION (0xC0000000 | 0x0247)
#define NT_STATUS_LOGIN_WKSTA_RESTRICTION (0xC0000000 | 0x0248)
#define NT_STATUS_IMAGE_MP_UP_MISMATCH (0xC0000000 | 0x0249)
#define NT_STATUS_INSUFFICIENT_LOGON_INFO (0xC0000000 | 0x0250)
#define NT_STATUS_BAD_DLL_ENTRYPOINT (0xC0000000 | 0x0251)
#define NT_STATUS_BAD_SERVICE_ENTRYPOINT (0xC0000000 | 0x0252)
#define NT_STATUS_LPC_REPLY_LOST (0xC0000000 | 0x0253)
#define NT_STATUS_IP_ADDRESS_CONFLICT1 (0xC0000000 | 0x0254)
#define NT_STATUS_IP_ADDRESS_CONFLICT2 (0xC0000000 | 0x0255)
#define NT_STATUS_REGISTRY_QUOTA_LIMIT (0xC0000000 | 0x0256)
#define NT_STATUS_PATH_NOT_COVERED (0xC0000000 | 0x0257)
#define NT_STATUS_NO_CALLBACK_ACTIVE (0xC0000000 | 0x0258)
#define NT_STATUS_LICENSE_QUOTA_EXCEEDED (0xC0000000 | 0x0259)
#define NT_STATUS_PWD_TOO_SHORT (0xC0000000 | 0x025a)
#define NT_STATUS_PWD_TOO_RECENT (0xC0000000 | 0x025b)
#define NT_STATUS_PWD_HISTORY_CONFLICT (0xC0000000 | 0x025c)
#define NT_STATUS_PLUGPLAY_NO_DEVICE (0xC0000000 | 0x025e)
#define NT_STATUS_UNSUPPORTED_COMPRESSION (0xC0000000 | 0x025f)
#define NT_STATUS_INVALID_HW_PROFILE (0xC0000000 | 0x0260)
#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH (0xC0000000 | 0x0261)
#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND (0xC0000000 | 0x0262)
#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0263)
#define NT_STATUS_RESOURCE_NOT_OWNED (0xC0000000 | 0x0264)
#define NT_STATUS_TOO_MANY_LINKS (0xC0000000 | 0x0265)
#define NT_STATUS_QUOTA_LIST_INCONSISTENT (0xC0000000 | 0x0266)
#define NT_STATUS_FILE_IS_OFFLINE (0xC0000000 | 0x0267)
#define NT_STATUS_NETWORK_SESSION_EXPIRED (0xC0000000 | 0x035c)
#define NT_STATUS_NO_SUCH_JOB (0xC0000000 | 0xEDE) /* scheduler */
#define NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP (0xC0000000 | 0x5D0000)
#define NT_STATUS_PENDING 0x00000103
#endif /* _NTERR_H */
/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* Copyright (c) International Business Machines Corp., 2002,2007
* Author(s): Steve French (sfrench@us.ibm.com)
*/
#ifndef __KSMBD_NTLMSSP_H
#define __KSMBD_NTLMSSP_H
#define NTLMSSP_SIGNATURE "NTLMSSP"
/* Security blob target info data */
#define TGT_Name "KSMBD"
/*
* Size of the crypto key returned on the negotiate SMB in bytes
*/
#define CIFS_CRYPTO_KEY_SIZE (8)
#define CIFS_KEY_SIZE (40)
/*
* Size of encrypted user password in bytes
*/
#define CIFS_ENCPWD_SIZE (16)
#define CIFS_CPHTXT_SIZE (16)
/* Message Types */
#define NtLmNegotiate cpu_to_le32(1)
#define NtLmChallenge cpu_to_le32(2)
#define NtLmAuthenticate cpu_to_le32(3)
#define UnknownMessage cpu_to_le32(8)
/* Negotiate Flags */
#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */
#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */
#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */
/* define reserved9 0x08 */
#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */
#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */
#define NTLMSSP_NEGOTIATE_DGRAM 0x0040
#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */
/* defined reserved 8 0x0100 */
#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */
#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */
#define NTLMSSP_ANONYMOUS 0x0800
#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */
#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000
#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */
#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */
#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000
#define NTLMSSP_TARGET_TYPE_SERVER 0x20000
#define NTLMSSP_TARGET_TYPE_SHARE 0x40000
#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/
/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */
#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000
#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */
#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000
#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000
/* #define reserved4 0x1000000 */
#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we do not set */
/* #define reserved3 0x4000000 */
/* #define reserved2 0x8000000 */
/* #define reserved1 0x10000000 */
#define NTLMSSP_NEGOTIATE_128 0x20000000
#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000
#define NTLMSSP_NEGOTIATE_56 0x80000000
/* Define AV Pair Field IDs */
enum av_field_type {
NTLMSSP_AV_EOL = 0,
NTLMSSP_AV_NB_COMPUTER_NAME,
NTLMSSP_AV_NB_DOMAIN_NAME,
NTLMSSP_AV_DNS_COMPUTER_NAME,
NTLMSSP_AV_DNS_DOMAIN_NAME,
NTLMSSP_AV_DNS_TREE_NAME,
NTLMSSP_AV_FLAGS,
NTLMSSP_AV_TIMESTAMP,
NTLMSSP_AV_RESTRICTION,
NTLMSSP_AV_TARGET_NAME,
NTLMSSP_AV_CHANNEL_BINDINGS
};
/* Although typedefs are not commonly used for structure definitions */
/* in the Linux kernel, in this particular case they are useful */
/* to more closely match the standards document for NTLMSSP from */
/* OpenGroup and to make the code more closely match the standard in */
/* appearance */
struct security_buffer {
__le16 Length;
__le16 MaximumLength;
__le32 BufferOffset; /* offset to buffer */
} __packed;
struct target_info {
__le16 Type;
__le16 Length;
__u8 Content[0];
} __packed;
struct negotiate_message {
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
__le32 MessageType; /* NtLmNegotiate = 1 */
__le32 NegotiateFlags;
struct security_buffer DomainName; /* RFC 1001 style and ASCII */
struct security_buffer WorkstationName; /* RFC 1001 and ASCII */
/*
* struct security_buffer for version info not present since we
* do not set the version is present flag
*/
char DomainString[0];
/* followed by WorkstationString */
} __packed;
struct challenge_message {
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
__le32 MessageType; /* NtLmChallenge = 2 */
struct security_buffer TargetName;
__le32 NegotiateFlags;
__u8 Challenge[CIFS_CRYPTO_KEY_SIZE];
__u8 Reserved[8];
struct security_buffer TargetInfoArray;
/*
* struct security_buffer for version info not present since we
* do not set the version is present flag
*/
} __packed;
struct authenticate_message {
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
__le32 MessageType; /* NtLmsAuthenticate = 3 */
struct security_buffer LmChallengeResponse;
struct security_buffer NtChallengeResponse;
struct security_buffer DomainName;
struct security_buffer UserName;
struct security_buffer WorkstationName;
struct security_buffer SessionKey;
__le32 NegotiateFlags;
/*
* struct security_buffer for version info not present since we
* do not set the version is present flag
*/
char UserString[0];
} __packed;
struct ntlmv2_resp {
char ntlmv2_hash[CIFS_ENCPWD_SIZE];
__le32 blob_signature;
__u32 reserved;
__le64 time;
__u64 client_chal; /* random */
__u32 reserved2;
/* array of name entries could follow ending in minimum 4 byte struct */
} __packed;
/* per smb session structure/fields */
struct ntlmssp_auth {
/* whether session key is per smb session */
bool sesskey_per_smbsess;
/* sent by client in type 1 ntlmsssp exchange */
__u32 client_flags;
/* sent by server in type 2 ntlmssp exchange */
__u32 conn_flags;
/* sent to server */
unsigned char ciphertext[CIFS_CPHTXT_SIZE];
/* used by ntlmssp */
char cryptkey[CIFS_CRYPTO_KEY_SIZE];
};
#endif /* __KSMBD_NTLMSSP_H */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#include <linux/moduleparam.h>
#include "glob.h"
#include "oplock.h"
#include "smb_common.h"
#include "smbstatus.h"
#include "buffer_pool.h"
#include "connection.h"
#include "mgmt/user_session.h"
#include "mgmt/share_config.h"
#include "mgmt/tree_connect.h"
static LIST_HEAD(lease_table_list);
static DEFINE_RWLOCK(lease_list_lock);
/**
* get_new_opinfo() - allocate a new opinfo object for oplock info
* @conn: connection instance
* @id: fid of open file
* @Tid: tree id of connection
* @lctx: lease context information
*
* Return: allocated opinfo object on success, otherwise NULL
*/
static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
uint64_t id, __u16 Tid)
{
struct ksmbd_session *sess = work->sess;
struct oplock_info *opinfo;
opinfo = kzalloc(sizeof(struct oplock_info), GFP_KERNEL);
if (!opinfo)
return NULL;
opinfo->sess = sess;
opinfo->conn = sess->conn;
opinfo->level = OPLOCK_NONE;
opinfo->op_state = OPLOCK_STATE_NONE;
opinfo->pending_break = 0;
opinfo->fid = id;
opinfo->Tid = Tid;
INIT_LIST_HEAD(&opinfo->op_entry);
INIT_LIST_HEAD(&opinfo->interim_list);
init_waitqueue_head(&opinfo->oplock_q);
init_waitqueue_head(&opinfo->oplock_brk);
atomic_set(&opinfo->refcount, 1);
atomic_set(&opinfo->breaking_cnt, 0);
return opinfo;
}
static void lease_add_list(struct oplock_info *opinfo)
{
struct lease_table *lb = opinfo->o_lease->l_lb;
spin_lock(&lb->lb_lock);
list_add_rcu(&opinfo->lease_entry, &lb->lease_list);
spin_unlock(&lb->lb_lock);
}
static void lease_del_list(struct oplock_info *opinfo)
{
struct lease_table *lb = opinfo->o_lease->l_lb;
if (!lb)
return;
spin_lock(&lb->lb_lock);
if (list_empty(&opinfo->lease_entry)) {
spin_unlock(&lb->lb_lock);
return;
}
list_del_init(&opinfo->lease_entry);
opinfo->o_lease->l_lb = NULL;
spin_unlock(&lb->lb_lock);
}
static void lb_add(struct lease_table *lb)
{
write_lock(&lease_list_lock);
list_add(&lb->l_entry, &lease_table_list);
write_unlock(&lease_list_lock);
}
static int alloc_lease(struct oplock_info *opinfo,
struct lease_ctx_info *lctx)
{
struct lease *lease;
lease = kmalloc(sizeof(struct lease), GFP_KERNEL);
if (!lease)
return -ENOMEM;
memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE);
lease->state = lctx->req_state;
lease->new_state = 0;
lease->flags = lctx->flags;
lease->duration = lctx->duration;
INIT_LIST_HEAD(&opinfo->lease_entry);
opinfo->o_lease = lease;
return 0;
}
static void free_lease(struct oplock_info *opinfo)
{
struct lease *lease;
lease = opinfo->o_lease;
kfree(lease);
}
static void free_opinfo(struct oplock_info *opinfo)
{
if (opinfo->is_lease)
free_lease(opinfo);
kfree(opinfo);
}
static inline void opinfo_free_rcu(struct rcu_head *rcu_head)
{
struct oplock_info *opinfo;
opinfo = container_of(rcu_head, struct oplock_info, rcu_head);
free_opinfo(opinfo);
}
struct oplock_info *opinfo_get(struct ksmbd_file *fp)
{
struct oplock_info *opinfo;
rcu_read_lock();
opinfo = rcu_dereference(fp->f_opinfo);
if (opinfo && !atomic_inc_not_zero(&opinfo->refcount))
opinfo = NULL;
rcu_read_unlock();
return opinfo;
}
static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
{
struct oplock_info *opinfo;
if (list_empty(&ci->m_op_list))
return NULL;
rcu_read_lock();
opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info,
op_entry);
if (opinfo && !atomic_inc_not_zero(&opinfo->refcount))
opinfo = NULL;
rcu_read_unlock();
return opinfo;
}
void opinfo_put(struct oplock_info *opinfo)
{
if (!atomic_dec_and_test(&opinfo->refcount))
return;
call_rcu(&opinfo->rcu_head, opinfo_free_rcu);
}
static void opinfo_add(struct oplock_info *opinfo)
{
struct ksmbd_inode *ci = opinfo->o_fp->f_ci;
write_lock(&ci->m_lock);
list_add_rcu(&opinfo->op_entry, &ci->m_op_list);
write_unlock(&ci->m_lock);
}
static void opinfo_del(struct oplock_info *opinfo)
{
struct ksmbd_inode *ci = opinfo->o_fp->f_ci;
if (opinfo->is_lease) {
write_lock(&lease_list_lock);
lease_del_list(opinfo);
write_unlock(&lease_list_lock);
}
write_lock(&ci->m_lock);
list_del_rcu(&opinfo->op_entry);
write_unlock(&ci->m_lock);
}
static unsigned long opinfo_count(struct ksmbd_file *fp)
{
if (ksmbd_stream_fd(fp))
return atomic_read(&fp->f_ci->sop_count);
else
return atomic_read(&fp->f_ci->op_count);
}
static void opinfo_count_inc(struct ksmbd_file *fp)
{
if (ksmbd_stream_fd(fp))
return atomic_inc(&fp->f_ci->sop_count);
else
return atomic_inc(&fp->f_ci->op_count);
}
static void opinfo_count_dec(struct ksmbd_file *fp)
{
if (ksmbd_stream_fd(fp))
return atomic_dec(&fp->f_ci->sop_count);
else
return atomic_dec(&fp->f_ci->op_count);
}
/**
* opinfo_write_to_read() - convert a write oplock to read oplock
* @opinfo: current oplock info
*
* Return: 0 on success, otherwise -EINVAL
*/
int opinfo_write_to_read(struct oplock_info *opinfo)
{
struct lease *lease = opinfo->o_lease;
if (!((opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) ||
(opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE))) {
ksmbd_err("bad oplock(0x%x)\n", opinfo->level);
if (opinfo->is_lease)
ksmbd_err("lease state(0x%x)\n", lease->state);
return -EINVAL;
}
opinfo->level = SMB2_OPLOCK_LEVEL_II;
if (opinfo->is_lease)
lease->state = lease->new_state;
return 0;
}
/**
* opinfo_read_handle_to_read() - convert a read/handle oplock to read oplock
* @opinfo: current oplock info
*
* Return: 0 on success, otherwise -EINVAL
*/
int opinfo_read_handle_to_read(struct oplock_info *opinfo)
{
struct lease *lease = opinfo->o_lease;
lease->state = lease->new_state;
opinfo->level = SMB2_OPLOCK_LEVEL_II;
return 0;
}
/**
* opinfo_write_to_none() - convert a write oplock to none
* @opinfo: current oplock info
*
* Return: 0 on success, otherwise -EINVAL
*/
int opinfo_write_to_none(struct oplock_info *opinfo)
{
struct lease *lease = opinfo->o_lease;
if (!((opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) ||
(opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE))) {
ksmbd_err("bad oplock(0x%x)\n", opinfo->level);
if (opinfo->is_lease)
ksmbd_err("lease state(0x%x)\n",
lease->state);
return -EINVAL;
}
opinfo->level = SMB2_OPLOCK_LEVEL_NONE;
if (opinfo->is_lease)
lease->state = lease->new_state;
return 0;
}
/**
* opinfo_read_to_none() - convert a write read to none
* @opinfo: current oplock info
*
* Return: 0 on success, otherwise -EINVAL
*/
int opinfo_read_to_none(struct oplock_info *opinfo)
{
struct lease *lease = opinfo->o_lease;
if (opinfo->level != SMB2_OPLOCK_LEVEL_II) {
ksmbd_err("bad oplock(0x%x)\n", opinfo->level);
if (opinfo->is_lease)
ksmbd_err("lease state(0x%x)\n", lease->state);
return -EINVAL;
}
opinfo->level = SMB2_OPLOCK_LEVEL_NONE;
if (opinfo->is_lease)
lease->state = lease->new_state;
return 0;
}
/**
* lease_read_to_write() - upgrade lease state from read to write
* @opinfo: current lease info
*
* Return: 0 on success, otherwise -EINVAL
*/
int lease_read_to_write(struct oplock_info *opinfo)
{
struct lease *lease = opinfo->o_lease;
if (!(lease->state & SMB2_LEASE_READ_CACHING_LE)) {
ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n",
lease->state);
return -EINVAL;
}
lease->new_state = SMB2_LEASE_NONE_LE;
lease->state |= SMB2_LEASE_WRITE_CACHING_LE;
if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE)
opinfo->level = SMB2_OPLOCK_LEVEL_BATCH;
else
opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
return 0;
}
/**
* lease_none_upgrade() - upgrade lease state from none
* @opinfo: current lease info
* @new_state: new lease state
*
* Return: 0 on success, otherwise -EINVAL
*/
static int lease_none_upgrade(struct oplock_info *opinfo,
__le32 new_state)
{
struct lease *lease = opinfo->o_lease;
if (!(lease->state == SMB2_LEASE_NONE_LE)) {
ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n",
lease->state);
return -EINVAL;
}
lease->new_state = SMB2_LEASE_NONE_LE;
lease->state = new_state;
if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE)
if (lease->state & SMB2_LEASE_WRITE_CACHING_LE)
opinfo->level = SMB2_OPLOCK_LEVEL_BATCH;
else
opinfo->level = SMB2_OPLOCK_LEVEL_II;
else if (lease->state & SMB2_LEASE_WRITE_CACHING_LE)
opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
else if (lease->state & SMB2_LEASE_READ_CACHING_LE)
opinfo->level = SMB2_OPLOCK_LEVEL_II;
return 0;
}
/**
* close_id_del_oplock() - release oplock object at file close time
* @fp: ksmbd file pointer
*/
void close_id_del_oplock(struct ksmbd_file *fp)
{
struct oplock_info *opinfo;
if (S_ISDIR(file_inode(fp->filp)->i_mode))
return;
opinfo = opinfo_get(fp);
if (!opinfo)
return;
opinfo_del(opinfo);
rcu_assign_pointer(fp->f_opinfo, NULL);
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
opinfo->op_state = OPLOCK_CLOSING;
wake_up_interruptible_all(&opinfo->oplock_q);
if (opinfo->is_lease) {
atomic_set(&opinfo->breaking_cnt, 0);
wake_up_interruptible_all(&opinfo->oplock_brk);
}
}
opinfo_count_dec(fp);
atomic_dec(&opinfo->refcount);
opinfo_put(opinfo);
}
/**
* grant_write_oplock() - grant exclusive/batch oplock or write lease
* @opinfo_new: new oplock info object
* @req_oplock: request oplock
* @lctx: lease context information
*
* Return: 0
*/
static void grant_write_oplock(struct oplock_info *opinfo_new, int req_oplock,
struct lease_ctx_info *lctx)
{
struct lease *lease = opinfo_new->o_lease;
if (req_oplock == SMB2_OPLOCK_LEVEL_BATCH)
opinfo_new->level = SMB2_OPLOCK_LEVEL_BATCH;
else
opinfo_new->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
if (lctx) {
lease->state = lctx->req_state;
memcpy(lease->lease_key, lctx->lease_key,
SMB2_LEASE_KEY_SIZE);
}
}
/**
* grant_read_oplock() - grant level2 oplock or read lease
* @opinfo_new: new oplock info object
* @lctx: lease context information
*
* Return: 0
*/
static void grant_read_oplock(struct oplock_info *opinfo_new,
struct lease_ctx_info *lctx)
{
struct lease *lease = opinfo_new->o_lease;
opinfo_new->level = SMB2_OPLOCK_LEVEL_II;
if (lctx) {
lease->state = SMB2_LEASE_READ_CACHING_LE;
if (lctx->req_state & SMB2_LEASE_HANDLE_CACHING_LE)
lease->state |= SMB2_LEASE_HANDLE_CACHING_LE;
memcpy(lease->lease_key, lctx->lease_key,
SMB2_LEASE_KEY_SIZE);
}
}
/**
* grant_none_oplock() - grant none oplock or none lease
* @opinfo_new: new oplock info object
* @lctx: lease context information
*
* Return: 0
*/
static void grant_none_oplock(struct oplock_info *opinfo_new,
struct lease_ctx_info *lctx)
{
struct lease *lease = opinfo_new->o_lease;
opinfo_new->level = SMB2_OPLOCK_LEVEL_NONE;
if (lctx) {
lease->state = 0;
memcpy(lease->lease_key, lctx->lease_key,
SMB2_LEASE_KEY_SIZE);
}
}
/**
* find_opinfo() - find lease object for given client guid and lease key
* @head: oplock list(read,write or none) head
* @guid1: client guid of matching lease owner
* @key1: lease key of matching lease owner
*
* Return: oplock(lease) object on success, otherwise NULL
*/
static inline int compare_guid_key(struct oplock_info *opinfo,
const char *guid1, const char *key1)
{
const char *guid2, *key2;
guid2 = opinfo->conn->ClientGUID;
key2 = opinfo->o_lease->lease_key;
if (!memcmp(guid1, guid2, SMB2_CLIENT_GUID_SIZE) &&
!memcmp(key1, key2, SMB2_LEASE_KEY_SIZE))
return 1;
return 0;
}
/**
* same_client_has_lease() - check whether current lease request is
* from lease owner of file
* @ci: master file pointer
* @client_guid: Client GUID
* @lctx: lease context information
*
* Return: oplock(lease) object on success, otherwise NULL
*/
static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
char *client_guid, struct lease_ctx_info *lctx)
{
int ret;
struct lease *lease;
struct oplock_info *opinfo;
struct oplock_info *m_opinfo = NULL;
if (!lctx)
return NULL;
/*
* Compare lease key and client_guid to know request from same owner
* of same client
*/
read_lock(&ci->m_lock);
list_for_each_entry(opinfo, &ci->m_op_list, op_entry) {
if (!opinfo->is_lease)
continue;
read_unlock(&ci->m_lock);
lease = opinfo->o_lease;
ret = compare_guid_key(opinfo, client_guid, lctx->lease_key);
if (ret) {
m_opinfo = opinfo;
/* skip upgrading lease about breaking lease */
if (atomic_read(&opinfo->breaking_cnt)) {
read_lock(&ci->m_lock);
continue;
}
/* upgrading lease */
if ((atomic_read(&ci->op_count) +
atomic_read(&ci->sop_count)) == 1) {
if (lease->state ==
(lctx->req_state & lease->state)) {
lease->state |= lctx->req_state;
if (lctx->req_state &
SMB2_LEASE_WRITE_CACHING_LE)
lease_read_to_write(opinfo);
}
} else if ((atomic_read(&ci->op_count) +
atomic_read(&ci->sop_count)) > 1) {
if (lctx->req_state ==
(SMB2_LEASE_READ_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE))
lease->state = lctx->req_state;
}
if (lctx->req_state && lease->state ==
SMB2_LEASE_NONE_LE)
lease_none_upgrade(opinfo, lctx->req_state);
}
read_lock(&ci->m_lock);
}
read_unlock(&ci->m_lock);
return m_opinfo;
}
static void wait_for_break_ack(struct oplock_info *opinfo)
{
int rc = 0;
rc = wait_event_interruptible_timeout(opinfo->oplock_q,
opinfo->op_state == OPLOCK_STATE_NONE ||
opinfo->op_state == OPLOCK_CLOSING,
OPLOCK_WAIT_TIME);
/* is this a timeout ? */
if (!rc) {
if (opinfo->is_lease)
opinfo->o_lease->state = SMB2_LEASE_NONE_LE;
opinfo->level = SMB2_OPLOCK_LEVEL_NONE;
opinfo->op_state = OPLOCK_STATE_NONE;
}
}
static void wake_up_oplock_break(struct oplock_info *opinfo)
{
clear_bit_unlock(0, &opinfo->pending_break);
/* memory barrier is needed for wake_up_bit() */
smp_mb__after_atomic();
wake_up_bit(&opinfo->pending_break, 0);
}
static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level)
{
while (test_and_set_bit(0, &opinfo->pending_break)) {
wait_on_bit(&opinfo->pending_break, 0, TASK_UNINTERRUPTIBLE);
/* Not immediately break to none. */
opinfo->open_trunc = 0;
if (opinfo->op_state == OPLOCK_CLOSING)
return -ENOENT;
else if (!opinfo->is_lease && opinfo->level <= req_op_level)
return 1;
}
if (!opinfo->is_lease && opinfo->level <= req_op_level) {
wake_up_oplock_break(opinfo);
return 1;
}
return 0;
}
static inline int allocate_oplock_break_buf(struct ksmbd_work *work)
{
work->response_buf = ksmbd_alloc_response(MAX_CIFS_SMALL_BUFFER_SIZE);
if (!work->response_buf)
return -ENOMEM;
work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE;
return 0;
}
/**
* smb2_oplock_break_noti() - send smb2 oplock break cmd from conn
* to client
* @work: smb work object
*
* There are two ways this function can be called. 1- while file open we break
* from exclusive/batch lock to levelII oplock and 2- while file write/truncate
* we break from levelII oplock no oplock.
* REQUEST_BUF(work) contains oplock_info.
*/
static void __smb2_oplock_break_noti(struct work_struct *wk)
{
struct smb2_oplock_break *rsp = NULL;
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
struct ksmbd_conn *conn = work->conn;
struct oplock_break_info *br_info = REQUEST_BUF(work);
struct smb2_hdr *rsp_hdr;
struct ksmbd_file *fp;
fp = ksmbd_lookup_durable_fd(br_info->fid);
if (!fp) {
atomic_dec(&conn->r_count);
ksmbd_free_work_struct(work);
return;
}
if (allocate_oplock_break_buf(work)) {
ksmbd_err("smb2_allocate_rsp_buf failed! ");
atomic_dec(&conn->r_count);
ksmbd_free_work_struct(work);
ksmbd_fd_put(work, fp);
return;
}
rsp_hdr = RESPONSE_BUF(work);
memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
rsp_hdr->smb2_buf_length = cpu_to_be32(HEADER_SIZE_NO_BUF_LEN(conn));
rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER;
rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE;
rsp_hdr->CreditRequest = cpu_to_le16(0);
rsp_hdr->Command = SMB2_OPLOCK_BREAK;
rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR);
rsp_hdr->NextCommand = 0;
rsp_hdr->MessageId = cpu_to_le64(-1);
rsp_hdr->Id.SyncId.ProcessId = 0;
rsp_hdr->Id.SyncId.TreeId = 0;
rsp_hdr->SessionId = 0;
memset(rsp_hdr->Signature, 0, 16);
rsp = RESPONSE_BUF(work);
rsp->StructureSize = cpu_to_le16(24);
if (!br_info->open_trunc &&
(br_info->level == SMB2_OPLOCK_LEVEL_BATCH ||
br_info->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE))
rsp->OplockLevel = SMB2_OPLOCK_LEVEL_II;
else
rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE;
rsp->Reserved = 0;
rsp->Reserved2 = 0;
rsp->PersistentFid = cpu_to_le64(fp->persistent_id);
rsp->VolatileFid = cpu_to_le64(fp->volatile_id);
inc_rfc1001_len(rsp, 24);
ksmbd_debug(OPLOCK,
"sending oplock break v_id %llu p_id = %llu lock level = %d\n",
rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel);
ksmbd_fd_put(work, fp);
ksmbd_conn_write(work);
ksmbd_free_work_struct(work);
atomic_dec(&conn->r_count);
}
/**
* smb2_oplock_break() - send smb2 exclusive/batch to level2 oplock
* break command from server to client
* @opinfo: oplock info object
* @ack_required if requiring ack
*
* Return: 0 on success, otherwise error
*/
static int smb2_oplock_break_noti(struct oplock_info *opinfo)
{
struct ksmbd_conn *conn = opinfo->conn;
struct oplock_break_info *br_info;
int ret = 0;
struct ksmbd_work *work = ksmbd_alloc_work_struct();
if (!work)
return -ENOMEM;
br_info = kmalloc(sizeof(struct oplock_break_info), GFP_KERNEL);
if (!br_info) {
ksmbd_free_work_struct(work);
return -ENOMEM;
}
br_info->level = opinfo->level;
br_info->fid = opinfo->fid;
br_info->open_trunc = opinfo->open_trunc;
work->request_buf = (char *)br_info;
work->conn = conn;
work->sess = opinfo->sess;
atomic_inc(&conn->r_count);
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
INIT_WORK(&work->work, __smb2_oplock_break_noti);
ksmbd_queue_work(work);
wait_for_break_ack(opinfo);
} else {
__smb2_oplock_break_noti(&work->work);
if (opinfo->level == SMB2_OPLOCK_LEVEL_II)
opinfo->level = SMB2_OPLOCK_LEVEL_NONE;
}
return ret;
}
/**
* __smb2_lease_break_noti() - send lease break command from server
* to client
* @work: smb work object
*/
static void __smb2_lease_break_noti(struct work_struct *wk)
{
struct smb2_lease_break *rsp = NULL;
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
struct lease_break_info *br_info = REQUEST_BUF(work);
struct ksmbd_conn *conn = work->conn;
struct smb2_hdr *rsp_hdr;
if (allocate_oplock_break_buf(work)) {
ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! ");
ksmbd_free_work_struct(work);
atomic_dec(&conn->r_count);
return;
}
rsp_hdr = RESPONSE_BUF(work);
memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
rsp_hdr->smb2_buf_length = cpu_to_be32(HEADER_SIZE_NO_BUF_LEN(conn));
rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER;
rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE;
rsp_hdr->CreditRequest = cpu_to_le16(0);
rsp_hdr->Command = SMB2_OPLOCK_BREAK;
rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR);
rsp_hdr->NextCommand = 0;
rsp_hdr->MessageId = cpu_to_le64(-1);
rsp_hdr->Id.SyncId.ProcessId = 0;
rsp_hdr->Id.SyncId.TreeId = 0;
rsp_hdr->SessionId = 0;
memset(rsp_hdr->Signature, 0, 16);
rsp = RESPONSE_BUF(work);
rsp->StructureSize = cpu_to_le16(44);
rsp->Reserved = 0;
rsp->Flags = 0;
if (br_info->curr_state & (SMB2_LEASE_WRITE_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE))
rsp->Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
memcpy(rsp->LeaseKey, br_info->lease_key, SMB2_LEASE_KEY_SIZE);
rsp->CurrentLeaseState = br_info->curr_state;
rsp->NewLeaseState = br_info->new_state;
rsp->BreakReason = 0;
rsp->AccessMaskHint = 0;
rsp->ShareMaskHint = 0;
inc_rfc1001_len(rsp, 44);
ksmbd_conn_write(work);
ksmbd_free_work_struct(work);
atomic_dec(&conn->r_count);
}
/**
* smb2_break_lease() - break lease when a new client request
* write lease
* @opinfo: conains lease state information
* @ack_required: if requring ack
*
* Return: 0 on success, otherwise error
*/
static int smb2_lease_break_noti(struct oplock_info *opinfo)
{
struct ksmbd_conn *conn = opinfo->conn;
struct list_head *tmp, *t;
struct ksmbd_work *work;
struct lease_break_info *br_info;
struct lease *lease = opinfo->o_lease;
work = ksmbd_alloc_work_struct();
if (!work)
return -ENOMEM;
br_info = kmalloc(sizeof(struct lease_break_info), GFP_KERNEL);
if (!br_info) {
ksmbd_free_work_struct(work);
return -ENOMEM;
}
br_info->curr_state = lease->state;
br_info->new_state = lease->new_state;
memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE);
work->request_buf = (char *)br_info;
work->conn = conn;
work->sess = opinfo->sess;
atomic_inc(&conn->r_count);
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
list_for_each_safe(tmp, t, &opinfo->interim_list) {
struct ksmbd_work *in_work;
in_work = list_entry(tmp, struct ksmbd_work,
interim_entry);
setup_async_work(in_work, NULL, NULL);
smb2_send_interim_resp(in_work, STATUS_PENDING);
list_del(&in_work->interim_entry);
}
INIT_WORK(&work->work, __smb2_lease_break_noti);
ksmbd_queue_work(work);
wait_for_break_ack(opinfo);
} else {
__smb2_lease_break_noti(&work->work);
if (opinfo->o_lease->new_state == SMB2_LEASE_NONE_LE) {
opinfo->level = SMB2_OPLOCK_LEVEL_NONE;
opinfo->o_lease->state = SMB2_LEASE_NONE_LE;
}
}
return 0;
}
static void wait_lease_breaking(struct oplock_info *opinfo)
{
if (!opinfo->is_lease)
return;
wake_up_interruptible_all(&opinfo->oplock_brk);
if (atomic_read(&opinfo->breaking_cnt)) {
int ret = 0;
ret = wait_event_interruptible_timeout(
opinfo->oplock_brk,
atomic_read(&opinfo->breaking_cnt) == 0,
HZ);
if (!ret)
atomic_set(&opinfo->breaking_cnt, 0);
}
}
static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
{
int err = 0;
/* Need to break exclusive/batch oplock, write lease or overwrite_if */
ksmbd_debug(OPLOCK,
"request to send oplock(level : 0x%x) break notification\n",
brk_opinfo->level);
if (brk_opinfo->is_lease) {
struct lease *lease = brk_opinfo->o_lease;
atomic_inc(&brk_opinfo->breaking_cnt);
err = oplock_break_pending(brk_opinfo, req_op_level);
if (err)
return err < 0 ? err : 0;
if (brk_opinfo->open_trunc) {
/*
* Create overwrite break trigger the lease break to
* none.
*/
lease->new_state = SMB2_LEASE_NONE_LE;
} else {
if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) {
if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE)
lease->new_state =
SMB2_LEASE_READ_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE;
else
lease->new_state =
SMB2_LEASE_READ_CACHING_LE;
} else {
if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE)
lease->new_state =
SMB2_LEASE_READ_CACHING_LE;
else
lease->new_state = SMB2_LEASE_NONE_LE;
}
}
if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE))
brk_opinfo->op_state = OPLOCK_ACK_WAIT;
else
atomic_dec(&brk_opinfo->breaking_cnt);
} else {
err = oplock_break_pending(brk_opinfo, req_op_level);
if (err)
return err < 0 ? err : 0;
if (brk_opinfo->level == SMB2_OPLOCK_LEVEL_BATCH ||
brk_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)
brk_opinfo->op_state = OPLOCK_ACK_WAIT;
}
if (brk_opinfo->is_lease)
err = smb2_lease_break_noti(brk_opinfo);
else
err = smb2_oplock_break_noti(brk_opinfo);
ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level);
if (brk_opinfo->op_state == OPLOCK_CLOSING)
err = -ENOENT;
wake_up_oplock_break(brk_opinfo);
wait_lease_breaking(brk_opinfo);
return err;
}
void destroy_lease_table(struct ksmbd_conn *conn)
{
struct lease_table *lb, *lbtmp;
struct oplock_info *opinfo;
write_lock(&lease_list_lock);
if (list_empty(&lease_table_list)) {
write_unlock(&lease_list_lock);
return;
}
list_for_each_entry_safe(lb, lbtmp, &lease_table_list, l_entry) {
if (conn && memcmp(lb->client_guid, conn->ClientGUID,
SMB2_CLIENT_GUID_SIZE))
continue;
again:
rcu_read_lock();
list_for_each_entry_rcu(opinfo, &lb->lease_list,
lease_entry) {
rcu_read_unlock();
lease_del_list(opinfo);
goto again;
}
rcu_read_unlock();
list_del(&lb->l_entry);
kfree(lb);
}
write_unlock(&lease_list_lock);
}
int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci,
struct lease_ctx_info *lctx)
{
struct oplock_info *opinfo;
int err = 0;
struct lease_table *lb;
if (!lctx)
return err;
read_lock(&lease_list_lock);
if (list_empty(&lease_table_list)) {
read_unlock(&lease_list_lock);
return 0;
}
list_for_each_entry(lb, &lease_table_list, l_entry) {
if (!memcmp(lb->client_guid, sess->conn->ClientGUID,
SMB2_CLIENT_GUID_SIZE))
goto found;
}
read_unlock(&lease_list_lock);
return 0;
found:
rcu_read_lock();
list_for_each_entry_rcu(opinfo, &lb->lease_list,
lease_entry) {
if (!atomic_inc_not_zero(&opinfo->refcount))
continue;
rcu_read_unlock();
if (opinfo->o_fp->f_ci == ci)
goto op_next;
err = compare_guid_key(opinfo,
sess->conn->ClientGUID,
lctx->lease_key);
if (err) {
err = -EINVAL;
ksmbd_debug(OPLOCK,
"found same lease key is already used in other files\n");
opinfo_put(opinfo);
goto out;
}
op_next:
opinfo_put(opinfo);
rcu_read_lock();
}
rcu_read_unlock();
out:
read_unlock(&lease_list_lock);
return err;
}
static void copy_lease(struct oplock_info *op1, struct oplock_info *op2)
{
struct lease *lease1 = op1->o_lease;
struct lease *lease2 = op2->o_lease;
op2->level = op1->level;
lease2->state = lease1->state;
memcpy(lease2->lease_key, lease1->lease_key,
SMB2_LEASE_KEY_SIZE);
lease2->duration = lease1->duration;
lease2->flags = lease1->flags;
}
static int add_lease_global_list(struct oplock_info *opinfo)
{
struct lease_table *lb;
read_lock(&lease_list_lock);
list_for_each_entry(lb, &lease_table_list, l_entry) {
if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID,
SMB2_CLIENT_GUID_SIZE)) {
opinfo->o_lease->l_lb = lb;
lease_add_list(opinfo);
read_unlock(&lease_list_lock);
return 0;
}
}
read_unlock(&lease_list_lock);
lb = kmalloc(sizeof(struct lease_table), GFP_KERNEL);
if (!lb)
return -ENOMEM;
memcpy(lb->client_guid, opinfo->conn->ClientGUID,
SMB2_CLIENT_GUID_SIZE);
INIT_LIST_HEAD(&lb->lease_list);
spin_lock_init(&lb->lb_lock);
opinfo->o_lease->l_lb = lb;
lease_add_list(opinfo);
lb_add(lb);
return 0;
}
static void set_oplock_level(struct oplock_info *opinfo, int level,
struct lease_ctx_info *lctx)
{
switch (level) {
case SMB2_OPLOCK_LEVEL_BATCH:
case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
grant_write_oplock(opinfo,
level, lctx);
break;
case SMB2_OPLOCK_LEVEL_II:
grant_read_oplock(opinfo, lctx);
break;
default:
grant_none_oplock(opinfo, lctx);
break;
}
}
/**
* smb_grant_oplock() - handle oplock/lease request on file open
* @fp: ksmbd file pointer
* @oplock: granted oplock type
* @id: fid of open file
* @Tid: Tree id of connection
* @lctx: lease context information on file open
* @attr_only: attribute only file open type
*
* Return: 0 on success, otherwise error
*/
int smb_grant_oplock(struct ksmbd_work *work,
int req_op_level,
uint64_t pid,
struct ksmbd_file *fp,
__u16 tid,
struct lease_ctx_info *lctx,
int share_ret)
{
struct ksmbd_session *sess = work->sess;
int err = 0;
struct oplock_info *opinfo = NULL, *prev_opinfo = NULL;
struct ksmbd_inode *ci = fp->f_ci;
bool prev_op_has_lease;
__le32 prev_op_state = 0;
/* not support directory lease */
if (S_ISDIR(file_inode(fp->filp)->i_mode)) {
if (lctx)
lctx->dlease = 1;
return 0;
}
opinfo = alloc_opinfo(work, pid, tid);
if (!opinfo)
return -ENOMEM;
if (lctx) {
err = alloc_lease(opinfo, lctx);
if (err)
goto err_out;
opinfo->is_lease = 1;
}
/* ci does not have any oplock */
if (!opinfo_count(fp))
goto set_lev;
/* grant none-oplock if second open is trunc */
if (ATTR_FP(fp)) {
req_op_level = SMB2_OPLOCK_LEVEL_NONE;
goto set_lev;
}
if (lctx) {
struct oplock_info *m_opinfo;
/* is lease already granted ? */
m_opinfo = same_client_has_lease(ci, sess->conn->ClientGUID,
lctx);
if (m_opinfo) {
copy_lease(m_opinfo, opinfo);
if (atomic_read(&m_opinfo->breaking_cnt))
opinfo->o_lease->flags =
SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE;
goto out;
}
}
prev_opinfo = opinfo_get_list(ci);
if (!prev_opinfo ||
(prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx))
goto set_lev;
prev_op_has_lease = prev_opinfo->is_lease;
if (prev_op_has_lease)
prev_op_state = prev_opinfo->o_lease->state;
if (share_ret < 0 &&
(prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) {
err = share_ret;
opinfo_put(prev_opinfo);
goto err_out;
}
if ((prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH) &&
(prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE)) {
opinfo_put(prev_opinfo);
goto op_break_not_needed;
}
list_add(&work->interim_entry, &prev_opinfo->interim_list);
err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II);
opinfo_put(prev_opinfo);
if (err == -ENOENT)
goto set_lev;
/* Check all oplock was freed by close */
else if (err < 0)
goto err_out;
op_break_not_needed:
if (share_ret < 0) {
err = share_ret;
goto err_out;
}
if (req_op_level != SMB2_OPLOCK_LEVEL_NONE)
req_op_level = SMB2_OPLOCK_LEVEL_II;
/* grant fixed oplock on stacked locking between lease and oplock */
if (prev_op_has_lease && !lctx)
if (prev_op_state & SMB2_LEASE_HANDLE_CACHING_LE)
req_op_level = SMB2_OPLOCK_LEVEL_NONE;
if (!prev_op_has_lease && lctx) {
req_op_level = SMB2_OPLOCK_LEVEL_II;
lctx->req_state = SMB2_LEASE_READ_CACHING_LE;
}
set_lev:
set_oplock_level(opinfo, req_op_level, lctx);
out:
rcu_assign_pointer(fp->f_opinfo, opinfo);
opinfo->o_fp = fp;
opinfo_count_inc(fp);
opinfo_add(opinfo);
if (opinfo->is_lease) {
err = add_lease_global_list(opinfo);
if (err)
goto err_out;
}
return 0;
err_out:
free_opinfo(opinfo);
return err;
}
/**
* smb_break_write_oplock() - break batch/exclusive oplock to level2
* @work: smb work
* @fp: ksmbd file pointer
* @openfile: open file object
*/
static void smb_break_all_write_oplock(struct ksmbd_work *work,
struct ksmbd_file *fp, int is_trunc)
{
struct oplock_info *brk_opinfo;
brk_opinfo = opinfo_get_list(fp->f_ci);
if (!brk_opinfo)
return;
if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
opinfo_put(brk_opinfo);
return;
}
brk_opinfo->open_trunc = is_trunc;
list_add(&work->interim_entry, &brk_opinfo->interim_list);
oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II);
opinfo_put(brk_opinfo);
}
/**
* smb_break_all_levII_oplock() - send level2 oplock or read lease break command
* from server to client
* @conn: connection instance
* @fp: ksmbd file pointer
* @is_trunc: truncate on open
*/
void smb_break_all_levII_oplock(struct ksmbd_work *work,
struct ksmbd_file *fp, int is_trunc)
{
struct oplock_info *op, *brk_op;
struct ksmbd_inode *ci;
struct ksmbd_conn *conn = work->sess->conn;
if (!test_share_config_flag(work->tcon->share_conf,
KSMBD_SHARE_FLAG_OPLOCKS)) {
return;
}
ci = fp->f_ci;
op = opinfo_get(fp);
rcu_read_lock();
list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) {
if (!atomic_inc_not_zero(&brk_op->refcount))
continue;
rcu_read_unlock();
if (brk_op->is_lease && (brk_op->o_lease->state &
(~(SMB2_LEASE_READ_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE)))) {
ksmbd_debug(OPLOCK, "unexpected lease state(0x%x)\n",
brk_op->o_lease->state);
goto next;
} else if (brk_op->level !=
SMB2_OPLOCK_LEVEL_II) {
ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n",
brk_op->level);
goto next;
}
/* Skip oplock being break to none */
if (brk_op->is_lease && (brk_op->o_lease->new_state ==
SMB2_LEASE_NONE_LE) &&
atomic_read(&brk_op->breaking_cnt))
goto next;
if (op && op->is_lease &&
brk_op->is_lease &&
!memcmp(conn->ClientGUID, brk_op->conn->ClientGUID,
SMB2_CLIENT_GUID_SIZE) &&
!memcmp(op->o_lease->lease_key,
brk_op->o_lease->lease_key,
SMB2_LEASE_KEY_SIZE))
goto next;
brk_op->open_trunc = is_trunc;
oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE);
next:
opinfo_put(brk_op);
rcu_read_lock();
}
rcu_read_unlock();
if (op)
opinfo_put(op);
}
/**
* smb_break_all_oplock() - break both batch/exclusive and level2 oplock
* @work: smb work
* @fp: ksmbd file pointer
*/
void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp)
{
if (!test_share_config_flag(work->tcon->share_conf,
KSMBD_SHARE_FLAG_OPLOCKS))
return;
smb_break_all_write_oplock(work, fp, 1);
smb_break_all_levII_oplock(work, fp, 1);
}
/**
* smb2_map_lease_to_oplock() - map lease state to corresponding oplock type
* @lease_state: lease type
*
* Return: 0 if no mapping, otherwise corresponding oplock type
*/
__u8 smb2_map_lease_to_oplock(__le32 lease_state)
{
if (lease_state == (SMB2_LEASE_HANDLE_CACHING_LE |
SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_WRITE_CACHING_LE))
return SMB2_OPLOCK_LEVEL_BATCH;
else if (lease_state != SMB2_LEASE_WRITE_CACHING_LE &&
lease_state & SMB2_LEASE_WRITE_CACHING_LE) {
if (!(lease_state & SMB2_LEASE_HANDLE_CACHING_LE))
return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
} else if (lease_state & SMB2_LEASE_READ_CACHING_LE)
return SMB2_OPLOCK_LEVEL_II;
return 0;
}
/**
* create_lease_buf() - create lease context for open cmd response
* @rbuf: buffer to create lease context response
* @lreq: buffer to stored parsed lease state information
*/
void create_lease_buf(u8 *rbuf, struct lease *lease)
{
struct create_lease *buf = (struct create_lease *)rbuf;
char *LeaseKey = (char *)&lease->lease_key;
memset(buf, 0, sizeof(struct create_lease));
buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey);
buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8));
buf->lcontext.LeaseFlags = lease->flags;
buf->lcontext.LeaseState = lease->state;
buf->ccontext.DataOffset = cpu_to_le16(offsetof
(struct create_lease, lcontext));
buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context));
buf->ccontext.NameOffset = cpu_to_le16(offsetof
(struct create_lease, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
buf->Name[0] = 'R';
buf->Name[1] = 'q';
buf->Name[2] = 'L';
buf->Name[3] = 's';
}
/**
* parse_lease_state() - parse lease context containted in file open request
* @open_req: buffer containing smb2 file open(create) request
* @lreq: buffer to stored parsed lease state information
*
* Return: oplock state, -ENOENT if create lease context not found
*/
struct lease_ctx_info *parse_lease_state(void *open_req)
{
char *data_offset;
struct create_context *cc;
unsigned int next = 0;
char *name;
bool found = false;
struct smb2_create_req *req = (struct smb2_create_req *)open_req;
struct lease_ctx_info *lreq = kzalloc(sizeof(struct lease_ctx_info),
GFP_KERNEL);
if (!lreq)
return NULL;
data_offset = (char *)req + 4 + le32_to_cpu(req->CreateContextsOffset);
cc = (struct create_context *)data_offset;
do {
cc = (struct create_context *)((char *)cc + next);
name = le16_to_cpu(cc->NameOffset) + (char *)cc;
if (le16_to_cpu(cc->NameLength) != 4 ||
strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) {
next = le32_to_cpu(cc->Next);
continue;
}
found = true;
break;
} while (next != 0);
if (found) {
struct create_lease *lc = (struct create_lease *)cc;
*((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow;
*((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh;
lreq->req_state = lc->lcontext.LeaseState;
lreq->flags = lc->lcontext.LeaseFlags;
lreq->duration = lc->lcontext.LeaseDuration;
return lreq;
}
kfree(lreq);
return NULL;
}
/**
* smb2_find_context_vals() - find a particular context info in open request
* @open_req: buffer containing smb2 file open(create) request
* @str: context name to search for
*
* Return: pointer to requested context, NULL if @str context not found
*/
struct create_context *smb2_find_context_vals(void *open_req, const char *tag)
{
char *data_offset;
struct create_context *cc;
unsigned int next = 0;
char *name;
struct smb2_create_req *req = (struct smb2_create_req *)open_req;
data_offset = (char *)req + 4 + le32_to_cpu(req->CreateContextsOffset);
cc = (struct create_context *)data_offset;
do {
int val;
cc = (struct create_context *)((char *)cc + next);
name = le16_to_cpu(cc->NameOffset) + (char *)cc;
val = le16_to_cpu(cc->NameLength);
if (val < 4)
return ERR_PTR(-EINVAL);
if (memcmp(name, tag, val) == 0)
return cc;
next = le32_to_cpu(cc->Next);
} while (next != 0);
return ERR_PTR(-ENOENT);
}
/**
* create_durable_buf() - create durable handle context
* @cc: buffer to create durable context response
*/
void create_durable_rsp_buf(char *cc)
{
struct create_durable_rsp *buf;
buf = (struct create_durable_rsp *)cc;
memset(buf, 0, sizeof(struct create_durable_rsp));
buf->ccontext.DataOffset = cpu_to_le16(offsetof
(struct create_durable_rsp, Data));
buf->ccontext.DataLength = cpu_to_le32(8);
buf->ccontext.NameOffset = cpu_to_le16(offsetof
(struct create_durable_rsp, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
/* SMB2_CREATE_DURABLE_HANDLE_RESPONSE is "DHnQ" */
buf->Name[0] = 'D';
buf->Name[1] = 'H';
buf->Name[2] = 'n';
buf->Name[3] = 'Q';
}
/**
* create_durable_buf() - create durable handle v2 context
* @cc: buffer to create durable context response
*/
void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp)
{
struct create_durable_v2_rsp *buf;
buf = (struct create_durable_v2_rsp *)cc;
memset(buf, 0, sizeof(struct create_durable_rsp));
buf->ccontext.DataOffset = cpu_to_le16(offsetof
(struct create_durable_rsp, Data));
buf->ccontext.DataLength = cpu_to_le32(8);
buf->ccontext.NameOffset = cpu_to_le16(offsetof
(struct create_durable_rsp, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
/* SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2 is "DH2Q" */
buf->Name[0] = 'D';
buf->Name[1] = 'H';
buf->Name[2] = '2';
buf->Name[3] = 'Q';
buf->Timeout = cpu_to_le32(fp->durable_timeout);
if (fp->is_persistent)
buf->Flags = SMB2_FLAGS_REPLAY_OPERATIONS;
}
/**
* create_mxac_buf() - create query maximal access context
* @cc: buffer to create maximal access context response
*/
void create_mxac_rsp_buf(char *cc, int maximal_access)
{
struct create_mxac_rsp *buf;
buf = (struct create_mxac_rsp *)cc;
memset(buf, 0, sizeof(struct create_mxac_rsp));
buf->ccontext.DataOffset = cpu_to_le16(offsetof
(struct create_mxac_rsp, QueryStatus));
buf->ccontext.DataLength = cpu_to_le32(8);
buf->ccontext.NameOffset = cpu_to_le16(offsetof
(struct create_mxac_rsp, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
/* SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE is "MxAc" */
buf->Name[0] = 'M';
buf->Name[1] = 'x';
buf->Name[2] = 'A';
buf->Name[3] = 'c';
buf->QueryStatus = STATUS_SUCCESS;
buf->MaximalAccess = cpu_to_le32(maximal_access);
}
/**
* create_mxac_buf() - create query maximal access context
* @cc: buffer to create query disk on id context response
*/
void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id)
{
struct create_disk_id_rsp *buf;
buf = (struct create_disk_id_rsp *)cc;
memset(buf, 0, sizeof(struct create_disk_id_rsp));
buf->ccontext.DataOffset = cpu_to_le16(offsetof
(struct create_disk_id_rsp, DiskFileId));
buf->ccontext.DataLength = cpu_to_le32(32);
buf->ccontext.NameOffset = cpu_to_le16(offsetof
(struct create_mxac_rsp, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
/* SMB2_CREATE_QUERY_ON_DISK_ID_RESPONSE is "QFid" */
buf->Name[0] = 'Q';
buf->Name[1] = 'F';
buf->Name[2] = 'i';
buf->Name[3] = 'd';
buf->DiskFileId = cpu_to_le64(file_id);
buf->VolumeId = cpu_to_le64(vol_id);
}
/**
* create_posix_buf() - create posix extension context
* @cc: buffer to create posix on posix response
*/
void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp)
{
struct create_posix_rsp *buf;
struct inode *inode = FP_INODE(fp);
buf = (struct create_posix_rsp *)cc;
memset(buf, 0, sizeof(struct create_posix_rsp));
buf->ccontext.DataOffset = cpu_to_le16(offsetof
(struct create_posix_rsp, nlink));
buf->ccontext.DataLength = cpu_to_le32(52);
buf->ccontext.NameOffset = cpu_to_le16(offsetof
(struct create_posix_rsp, Name));
buf->ccontext.NameLength = cpu_to_le16(POSIX_CTXT_DATA_LEN);
/* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */
buf->Name[0] = 0x93;
buf->Name[1] = 0xAD;
buf->Name[2] = 0x25;
buf->Name[3] = 0x50;
buf->Name[4] = 0x9C;
buf->Name[5] = 0xB4;
buf->Name[6] = 0x11;
buf->Name[7] = 0xE7;
buf->Name[8] = 0xB4;
buf->Name[9] = 0x23;
buf->Name[10] = 0x83;
buf->Name[11] = 0xDE;
buf->Name[12] = 0x96;
buf->Name[13] = 0x8B;
buf->Name[14] = 0xCD;
buf->Name[15] = 0x7C;
buf->nlink = cpu_to_le32(inode->i_nlink);
buf->reparse_tag = cpu_to_le32(fp->volatile_id);
buf->mode = cpu_to_le32(inode->i_mode);
id_to_sid(from_kuid(&init_user_ns, inode->i_uid),
SIDNFS_USER, (struct smb_sid *)&buf->SidBuffer[0]);
id_to_sid(from_kgid(&init_user_ns, inode->i_gid),
SIDNFS_GROUP, (struct smb_sid *)&buf->SidBuffer[20]);
}
/*
* Find lease object(opinfo) for given lease key/fid from lease
* break/file close path.
*/
/**
* lookup_lease_in_table() - find a matching lease info object
* @conn: connection instance
* @lease_key: lease key to be searched for
*
* Return: opinfo if found matching opinfo, otherwise NULL
*/
struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
char *lease_key)
{
struct oplock_info *opinfo = NULL, *ret_op = NULL;
struct lease_table *lt;
int ret;
read_lock(&lease_list_lock);
list_for_each_entry(lt, &lease_table_list, l_entry) {
if (!memcmp(lt->client_guid, conn->ClientGUID,
SMB2_CLIENT_GUID_SIZE))
goto found;
}
read_unlock(&lease_list_lock);
return NULL;
found:
rcu_read_lock();
list_for_each_entry_rcu(opinfo, &lt->lease_list, lease_entry) {
if (!atomic_inc_not_zero(&opinfo->refcount))
continue;
rcu_read_unlock();
if (!opinfo->op_state ||
opinfo->op_state == OPLOCK_CLOSING)
goto op_next;
if (!(opinfo->o_lease->state &
(SMB2_LEASE_HANDLE_CACHING_LE |
SMB2_LEASE_WRITE_CACHING_LE)))
goto op_next;
ret = compare_guid_key(opinfo, conn->ClientGUID,
lease_key);
if (ret) {
ksmbd_debug(OPLOCK, "found opinfo\n");
ret_op = opinfo;
goto out;
}
op_next:
opinfo_put(opinfo);
rcu_read_lock();
}
rcu_read_unlock();
out:
read_unlock(&lease_list_lock);
return ret_op;
}
int smb2_check_durable_oplock(struct ksmbd_file *fp,
struct lease_ctx_info *lctx, char *name)
{
struct oplock_info *opinfo = opinfo_get(fp);
int ret = 0;
if (opinfo && opinfo->is_lease) {
if (!lctx) {
ksmbd_err("open does not include lease\n");
ret = -EBADF;
goto out;
}
if (memcmp(opinfo->o_lease->lease_key, lctx->lease_key,
SMB2_LEASE_KEY_SIZE)) {
ksmbd_err("invalid lease key\n");
ret = -EBADF;
goto out;
}
if (name && strcmp(fp->filename, name)) {
ksmbd_err("invalid name reconnect %s\n", name);
ret = -EINVAL;
goto out;
}
}
out:
if (opinfo)
opinfo_put(opinfo);
return ret;
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#ifndef __KSMBD_OPLOCK_H
#define __KSMBD_OPLOCK_H
#include "smb_common.h"
#define OPLOCK_WAIT_TIME (35*HZ)
/* SMB Oplock levels */
#define OPLOCK_NONE 0
#define OPLOCK_EXCLUSIVE 1
#define OPLOCK_BATCH 2
#define OPLOCK_READ 3 /* level 2 oplock */
/* SMB2 Oplock levels */
#define SMB2_OPLOCK_LEVEL_NONE 0x00
#define SMB2_OPLOCK_LEVEL_II 0x01
#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
#define SMB2_OPLOCK_LEVEL_BATCH 0x09
#define SMB2_OPLOCK_LEVEL_LEASE 0xFF
/* Oplock states */
#define OPLOCK_STATE_NONE 0x00
#define OPLOCK_ACK_WAIT 0x01
#define OPLOCK_CLOSING 0x02
#define OPLOCK_WRITE_TO_READ 0x01
#define OPLOCK_READ_HANDLE_TO_READ 0x02
#define OPLOCK_WRITE_TO_NONE 0x04
#define OPLOCK_READ_TO_NONE 0x08
#define SMB2_LEASE_KEY_SIZE 16
struct lease_ctx_info {
__u8 lease_key[SMB2_LEASE_KEY_SIZE];
__le32 req_state;
__le32 flags;
__le64 duration;
int dlease;
};
struct lease_table {
char client_guid[SMB2_CLIENT_GUID_SIZE];
struct list_head lease_list;
struct list_head l_entry;
spinlock_t lb_lock;
};
struct lease {
__u8 lease_key[SMB2_LEASE_KEY_SIZE];
__le32 state;
__le32 new_state;
__le32 flags;
__le64 duration;
struct lease_table *l_lb;
};
struct oplock_info {
struct ksmbd_conn *conn;
struct ksmbd_session *sess;
struct ksmbd_work *work;
struct ksmbd_file *o_fp;
int level;
int op_state;
unsigned long pending_break;
uint64_t fid;
atomic_t breaking_cnt;
atomic_t refcount;
__u16 Tid;
bool is_lease;
bool open_trunc; /* truncate on open */
struct lease *o_lease;
struct list_head interim_list;
struct list_head op_entry;
struct list_head lease_entry;
wait_queue_head_t oplock_q; /* Other server threads */
wait_queue_head_t oplock_brk; /* oplock breaking wait */
struct rcu_head rcu_head;
};
struct lease_break_info {
__le32 curr_state;
__le32 new_state;
char lease_key[SMB2_LEASE_KEY_SIZE];
};
struct oplock_break_info {
int level;
int open_trunc;
int fid;
};
extern int smb_grant_oplock(struct ksmbd_work *work, int req_op_level,
uint64_t pid, struct ksmbd_file *fp, __u16 tid,
struct lease_ctx_info *lctx, int share_ret);
extern void smb_break_all_levII_oplock(struct ksmbd_work *work,
struct ksmbd_file *fp, int is_trunc);
int opinfo_write_to_read(struct oplock_info *opinfo);
int opinfo_read_handle_to_read(struct oplock_info *opinfo);
int opinfo_write_to_none(struct oplock_info *opinfo);
int opinfo_read_to_none(struct oplock_info *opinfo);
void close_id_del_oplock(struct ksmbd_file *fp);
void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp);
struct oplock_info *opinfo_get(struct ksmbd_file *fp);
void opinfo_put(struct oplock_info *opinfo);
/* Lease related functions */
void create_lease_buf(u8 *rbuf, struct lease *lease);
struct lease_ctx_info *parse_lease_state(void *open_req);
__u8 smb2_map_lease_to_oplock(__le32 lease_state);
int lease_read_to_write(struct oplock_info *opinfo);
/* Durable related functions */
void create_durable_rsp_buf(char *cc);
void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp);
void create_mxac_rsp_buf(char *cc, int maximal_access);
void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id);
void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp);
struct create_context *smb2_find_context_vals(void *open_req, const char *str);
int ksmbd_durable_verify_and_del_oplock(struct ksmbd_session *curr_sess,
struct ksmbd_session *prev_sess,
int fid, struct file **filp,
uint64_t sess_id);
struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
char *lease_key);
int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci,
struct lease_ctx_info *lctx);
void destroy_lease_table(struct ksmbd_conn *conn);
int smb2_check_durable_oplock(struct ksmbd_file *fp,
struct lease_ctx_info *lctx, char *name);
#endif /* __KSMBD_OPLOCK_H */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#include "glob.h"
#include "nterr.h"
#include "smb2pdu.h"
#include "smb_common.h"
#include "smbstatus.h"
#include "mgmt/user_session.h"
#include "connection.h"
static int check_smb2_hdr(struct smb2_hdr *hdr)
{
/*
* Make sure that this really is an SMB, that it is a response.
*/
if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
return 1;
return 0;
}
/*
* The following table defines the expected "StructureSize" of SMB2 requests
* in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests.
*
* Note that commands are defined in smb2pdu.h in le16 but the array below is
* indexed by command in host byte order
*/
static const __le16 smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
/* SMB2_NEGOTIATE */ cpu_to_le16(36),
/* SMB2_SESSION_SETUP */ cpu_to_le16(25),
/* SMB2_LOGOFF */ cpu_to_le16(4),
/* SMB2_TREE_CONNECT */ cpu_to_le16(9),
/* SMB2_TREE_DISCONNECT */ cpu_to_le16(4),
/* SMB2_CREATE */ cpu_to_le16(57),
/* SMB2_CLOSE */ cpu_to_le16(24),
/* SMB2_FLUSH */ cpu_to_le16(24),
/* SMB2_READ */ cpu_to_le16(49),
/* SMB2_WRITE */ cpu_to_le16(49),
/* SMB2_LOCK */ cpu_to_le16(48),
/* SMB2_IOCTL */ cpu_to_le16(57),
/* SMB2_CANCEL */ cpu_to_le16(4),
/* SMB2_ECHO */ cpu_to_le16(4),
/* SMB2_QUERY_DIRECTORY */ cpu_to_le16(33),
/* SMB2_CHANGE_NOTIFY */ cpu_to_le16(32),
/* SMB2_QUERY_INFO */ cpu_to_le16(41),
/* SMB2_SET_INFO */ cpu_to_le16(33),
/* use 44 for lease break */
/* SMB2_OPLOCK_BREAK */ cpu_to_le16(36)
};
/*
* The size of the variable area depends on the offset and length fields
* located in different fields for various SMB2 requests. SMB2 requests
* with no variable length info, show an offset of zero for the offset field.
*/
static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = {
/* SMB2_NEGOTIATE */ true,
/* SMB2_SESSION_SETUP */ true,
/* SMB2_LOGOFF */ false,
/* SMB2_TREE_CONNECT */ true,
/* SMB2_TREE_DISCONNECT */ false,
/* SMB2_CREATE */ true,
/* SMB2_CLOSE */ false,
/* SMB2_FLUSH */ false,
/* SMB2_READ */ true,
/* SMB2_WRITE */ true,
/* SMB2_LOCK */ true,
/* SMB2_IOCTL */ true,
/* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */
/* SMB2_ECHO */ false,
/* SMB2_QUERY_DIRECTORY */ true,
/* SMB2_CHANGE_NOTIFY */ false,
/* SMB2_QUERY_INFO */ true,
/* SMB2_SET_INFO */ true,
/* SMB2_OPLOCK_BREAK */ false
};
/*
* Returns the pointer to the beginning of the data area. Length of the data
* area and the offset to it (from the beginning of the smb are also returned.
*/
static char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
{
*off = 0;
*len = 0;
/* error reqeusts do not have data area */
if (hdr->Status && hdr->Status != STATUS_MORE_PROCESSING_REQUIRED &&
(((struct smb2_err_rsp *)hdr)->StructureSize) ==
SMB2_ERROR_STRUCTURE_SIZE2_LE)
return NULL;
/*
* Following commands have data areas so we have to get the location
* of the data buffer offset and data buffer length for the particular
* command.
*/
switch (hdr->Command) {
case SMB2_SESSION_SETUP:
*off = le16_to_cpu(
((struct smb2_sess_setup_req *)hdr)->SecurityBufferOffset);
*len = le16_to_cpu(
((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength);
break;
case SMB2_TREE_CONNECT:
*off = le16_to_cpu(
((struct smb2_tree_connect_req *)hdr)->PathOffset);
*len = le16_to_cpu(
((struct smb2_tree_connect_req *)hdr)->PathLength);
break;
case SMB2_CREATE:
{
if (((struct smb2_create_req *)hdr)->CreateContextsLength) {
*off = le32_to_cpu(((struct smb2_create_req *)
hdr)->CreateContextsOffset);
*len = le32_to_cpu(((struct smb2_create_req *)
hdr)->CreateContextsLength);
break;
}
*off = le16_to_cpu(
((struct smb2_create_req *)hdr)->NameOffset);
*len = le16_to_cpu(
((struct smb2_create_req *)hdr)->NameLength);
break;
}
case SMB2_QUERY_INFO:
*off = le16_to_cpu(
((struct smb2_query_info_req *)hdr)->InputBufferOffset);
*len = le32_to_cpu(
((struct smb2_query_info_req *)hdr)->InputBufferLength);
break;
case SMB2_SET_INFO:
*off = le16_to_cpu(
((struct smb2_set_info_req *)hdr)->BufferOffset);
*len = le32_to_cpu(
((struct smb2_set_info_req *)hdr)->BufferLength);
break;
case SMB2_READ:
*off = le16_to_cpu(
((struct smb2_read_req *)hdr)->ReadChannelInfoOffset);
*len = le16_to_cpu(
((struct smb2_read_req *)hdr)->ReadChannelInfoLength);
break;
case SMB2_WRITE:
if (((struct smb2_write_req *)hdr)->DataOffset) {
*off = le16_to_cpu(
((struct smb2_write_req *)hdr)->DataOffset);
*len = le32_to_cpu(
((struct smb2_write_req *)hdr)->Length);
break;
}
*off = le16_to_cpu(
((struct smb2_write_req *)hdr)->WriteChannelInfoOffset);
*len = le16_to_cpu(
((struct smb2_write_req *)hdr)->WriteChannelInfoLength);
break;
case SMB2_QUERY_DIRECTORY:
*off = le16_to_cpu(
((struct smb2_query_directory_req *)hdr)->FileNameOffset);
*len = le16_to_cpu(
((struct smb2_query_directory_req *)hdr)->FileNameLength);
break;
case SMB2_LOCK:
{
int lock_count;
/*
* smb2_lock request size is 48 included single
* smb2_lock_element structure size.
*/
lock_count = le16_to_cpu(
((struct smb2_lock_req *)hdr)->LockCount) - 1;
if (lock_count > 0) {
*off = __SMB2_HEADER_STRUCTURE_SIZE + 48;
*len = sizeof(struct smb2_lock_element) * lock_count;
}
break;
}
case SMB2_IOCTL:
*off = le32_to_cpu(
((struct smb2_ioctl_req *)hdr)->InputOffset);
*len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount);
break;
default:
ksmbd_debug(SMB, "no length check for command\n");
break;
}
/*
* Invalid length or offset probably means data area is invalid, but
* we have little choice but to ignore the data area in this case.
*/
if (*off > 4096) {
ksmbd_debug(SMB, "offset %d too large, data area ignored\n",
*off);
*len = 0;
*off = 0;
} else if (*off < 0) {
ksmbd_debug(SMB,
"negative offset %d to data invalid ignore data area\n",
*off);
*off = 0;
*len = 0;
} else if (*len < 0) {
ksmbd_debug(SMB,
"negative data length %d invalid, data area ignored\n",
*len);
*len = 0;
} else if (*len > 128 * 1024) {
ksmbd_debug(SMB, "data area larger than 128K: %d\n", *len);
*len = 0;
}
/* return pointer to beginning of data area, ie offset from SMB start */
if ((*off != 0) && (*len != 0))
return (char *)hdr + *off;
else
return NULL;
}
/*
* Calculate the size of the SMB message based on the fixed header
* portion, the number of word parameters and the data portion of the message.
*/
static unsigned int smb2_calc_size(void *buf)
{
struct smb2_pdu *pdu = (struct smb2_pdu *)buf;
struct smb2_hdr *hdr = &pdu->hdr;
int offset; /* the offset from the beginning of SMB to data area */
int data_length; /* the length of the variable length data area */
/* Structure Size has already been checked to make sure it is 64 */
int len = le16_to_cpu(hdr->StructureSize);
/*
* StructureSize2, ie length of fixed parameter area has already
* been checked to make sure it is the correct length.
*/
len += le16_to_cpu(pdu->StructureSize2);
if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false)
goto calc_size_exit;
smb2_get_data_area_len(&offset, &data_length, hdr);
ksmbd_debug(SMB, "SMB2 data length %d offset %d\n", data_length,
offset);
if (data_length > 0) {
/*
* Check to make sure that data area begins after fixed area,
* Note that last byte of the fixed area is part of data area
* for some commands, typically those with odd StructureSize,
* so we must add one to the calculation.
*/
if (offset + 1 < len)
ksmbd_debug(SMB,
"data area offset %d overlaps SMB2 header %d\n",
offset + 1, len);
else
len = offset + data_length;
}
calc_size_exit:
ksmbd_debug(SMB, "SMB2 len %d\n", len);
return len;
}
static inline int smb2_query_info_req_len(struct smb2_query_info_req *h)
{
return le32_to_cpu(h->InputBufferLength) +
le32_to_cpu(h->OutputBufferLength);
}
static inline int smb2_set_info_req_len(struct smb2_set_info_req *h)
{
return le32_to_cpu(h->BufferLength);
}
static inline int smb2_read_req_len(struct smb2_read_req *h)
{
return le32_to_cpu(h->Length);
}
static inline int smb2_write_req_len(struct smb2_write_req *h)
{
return le32_to_cpu(h->Length);
}
static inline int smb2_query_dir_req_len(struct smb2_query_directory_req *h)
{
return le32_to_cpu(h->OutputBufferLength);
}
static inline int smb2_ioctl_req_len(struct smb2_ioctl_req *h)
{
return le32_to_cpu(h->InputCount) +
le32_to_cpu(h->OutputCount);
}
static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h)
{
return le32_to_cpu(h->MaxInputResponse) +
le32_to_cpu(h->MaxOutputResponse);
}
static int smb2_validate_credit_charge(struct smb2_hdr *hdr)
{
int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len;
int credit_charge = le16_to_cpu(hdr->CreditCharge);
void *__hdr = hdr;
switch (hdr->Command) {
case SMB2_QUERY_INFO:
req_len = smb2_query_info_req_len(__hdr);
break;
case SMB2_SET_INFO:
req_len = smb2_set_info_req_len(__hdr);
break;
case SMB2_READ:
req_len = smb2_read_req_len(__hdr);
break;
case SMB2_WRITE:
req_len = smb2_write_req_len(__hdr);
break;
case SMB2_QUERY_DIRECTORY:
req_len = smb2_query_dir_req_len(__hdr);
break;
case SMB2_IOCTL:
req_len = smb2_ioctl_req_len(__hdr);
expect_resp_len = smb2_ioctl_resp_len(__hdr);
break;
default:
return 0;
}
max_len = max(req_len, expect_resp_len);
calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE);
if (!credit_charge && max_len > SMB2_MAX_BUFFER_SIZE) {
ksmbd_err("credit charge is zero and payload size(%d) is bigger than 64K\n",
max_len);
return 1;
} else if (credit_charge < calc_credit_num) {
ksmbd_err("credit charge : %d, calc_credit_num : %d\n",
credit_charge, calc_credit_num);
return 1;
}
return 0;
}
int ksmbd_smb2_check_message(struct ksmbd_work *work)
{
struct smb2_pdu *pdu = REQUEST_BUF(work);
struct smb2_hdr *hdr = &pdu->hdr;
int command;
__u32 clc_len; /* calculated length */
__u32 len = get_rfc1002_len(pdu);
if (work->next_smb2_rcv_hdr_off) {
pdu = REQUEST_BUF_NEXT(work);
hdr = &pdu->hdr;
}
if (le32_to_cpu(hdr->NextCommand) > 0)
len = le32_to_cpu(hdr->NextCommand);
else if (work->next_smb2_rcv_hdr_off) {
len -= work->next_smb2_rcv_hdr_off;
len = round_up(len, 8);
}
if (check_smb2_hdr(hdr))
return 1;
if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) {
ksmbd_debug(SMB, "Illegal structure size %u\n",
le16_to_cpu(hdr->StructureSize));
return 1;
}
command = le16_to_cpu(hdr->Command);
if (command >= NUMBER_OF_SMB2_COMMANDS) {
ksmbd_debug(SMB, "Illegal SMB2 command %d\n", command);
return 1;
}
if (smb2_req_struct_sizes[command] != pdu->StructureSize2) {
if (command != SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0 ||
pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) {
/* error packets have 9 byte structure size */
ksmbd_debug(SMB,
"Illegal request size %u for command %d\n",
le16_to_cpu(pdu->StructureSize2), command);
return 1;
} else if (command == SMB2_OPLOCK_BREAK_HE
&& (hdr->Status == 0)
&& (le16_to_cpu(pdu->StructureSize2) !=
OP_BREAK_STRUCT_SIZE_20)
&& (le16_to_cpu(pdu->StructureSize2) !=
OP_BREAK_STRUCT_SIZE_21)) {
/* special case for SMB2.1 lease break message */
ksmbd_debug(SMB,
"Illegal request size %d for oplock break\n",
le16_to_cpu(pdu->StructureSize2));
return 1;
}
}
clc_len = smb2_calc_size(hdr);
if (len != clc_len) {
/* server can return one byte more due to implied bcc[0] */
if (clc_len == len + 1)
return 0;
/*
* Some windows servers (win2016) will pad also the final
* PDU in a compound to 8 bytes.
*/
if (ALIGN(clc_len, 8) == len)
return 0;
/*
* windows client also pad up to 8 bytes when compounding.
* If pad is longer than eight bytes, log the server behavior
* (once), since may indicate a problem but allow it and
* continue since the frame is parseable.
*/
if (clc_len < len) {
ksmbd_debug(SMB,
"cli req padded more than expected. Length %d not %d for cmd:%d mid:%llu\n",
len, clc_len, command,
le64_to_cpu(hdr->MessageId));
return 0;
}
if (command == SMB2_LOCK_HE && len == 88)
return 0;
ksmbd_debug(SMB,
"cli req too short, len %d not %d. cmd:%d mid:%llu\n",
len, clc_len, command,
le64_to_cpu(hdr->MessageId));
return 1;
}
return work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU ?
smb2_validate_credit_charge(hdr) : 0;
}
int smb2_negotiate_request(struct ksmbd_work *work)
{
return ksmbd_smb_negotiate_common(work, SMB2_NEGOTIATE_HE);
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#include <linux/slab.h>
#include "glob.h"
#include "smb2pdu.h"
#include "auth.h"
#include "connection.h"
#include "smb_common.h"
#include "server.h"
#include "ksmbd_server.h"
static struct smb_version_values smb21_server_values = {
.version_string = SMB21_VERSION_STRING,
.protocol_id = SMB21_PROT_ID,
.capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
.max_read_size = SMB21_DEFAULT_IOSIZE,
.max_write_size = SMB21_DEFAULT_IOSIZE,
.max_trans_size = SMB21_DEFAULT_IOSIZE,
.large_lock_type = 0,
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE,
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
.lock_cmd = SMB2_LOCK,
.cap_unix = 0,
.cap_nt_find = SMB2_NT_FIND,
.cap_large_files = SMB2_LARGE_FILES,
.create_lease_size = sizeof(struct create_lease),
.create_durable_size = sizeof(struct create_durable_rsp),
.create_mxac_size = sizeof(struct create_mxac_rsp),
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
.create_posix_size = sizeof(struct create_posix_rsp),
};
static struct smb_version_values smb30_server_values = {
.version_string = SMB30_VERSION_STRING,
.protocol_id = SMB30_PROT_ID,
.capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
.max_read_size = SMB3_DEFAULT_IOSIZE,
.max_write_size = SMB3_DEFAULT_IOSIZE,
.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
.large_lock_type = 0,
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE,
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
.lock_cmd = SMB2_LOCK,
.cap_unix = 0,
.cap_nt_find = SMB2_NT_FIND,
.cap_large_files = SMB2_LARGE_FILES,
.create_lease_size = sizeof(struct create_lease),
.create_durable_size = sizeof(struct create_durable_rsp),
.create_durable_v2_size = sizeof(struct create_durable_v2_rsp),
.create_mxac_size = sizeof(struct create_mxac_rsp),
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
.create_posix_size = sizeof(struct create_posix_rsp),
};
static struct smb_version_values smb302_server_values = {
.version_string = SMB302_VERSION_STRING,
.protocol_id = SMB302_PROT_ID,
.capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
.max_read_size = SMB3_DEFAULT_IOSIZE,
.max_write_size = SMB3_DEFAULT_IOSIZE,
.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
.large_lock_type = 0,
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE,
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
.lock_cmd = SMB2_LOCK,
.cap_unix = 0,
.cap_nt_find = SMB2_NT_FIND,
.cap_large_files = SMB2_LARGE_FILES,
.create_lease_size = sizeof(struct create_lease),
.create_durable_size = sizeof(struct create_durable_rsp),
.create_durable_v2_size = sizeof(struct create_durable_v2_rsp),
.create_mxac_size = sizeof(struct create_mxac_rsp),
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
.create_posix_size = sizeof(struct create_posix_rsp),
};
static struct smb_version_values smb311_server_values = {
.version_string = SMB311_VERSION_STRING,
.protocol_id = SMB311_PROT_ID,
.capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
.max_read_size = SMB3_DEFAULT_IOSIZE,
.max_write_size = SMB3_DEFAULT_IOSIZE,
.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
.large_lock_type = 0,
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE,
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
.lock_cmd = SMB2_LOCK,
.cap_unix = 0,
.cap_nt_find = SMB2_NT_FIND,
.cap_large_files = SMB2_LARGE_FILES,
.create_lease_size = sizeof(struct create_lease),
.create_durable_size = sizeof(struct create_durable_rsp),
.create_durable_v2_size = sizeof(struct create_durable_v2_rsp),
.create_mxac_size = sizeof(struct create_mxac_rsp),
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
.create_posix_size = sizeof(struct create_posix_rsp),
};
static struct smb_version_ops smb2_0_server_ops = {
.get_cmd_val = get_smb2_cmd_val,
.init_rsp_hdr = init_smb2_rsp_hdr,
.set_rsp_status = set_smb2_rsp_status,
.allocate_rsp_buf = smb2_allocate_rsp_buf,
.set_rsp_credits = smb2_set_rsp_credits,
.check_user_session = smb2_check_user_session,
.get_ksmbd_tcon = smb2_get_ksmbd_tcon,
.is_sign_req = smb2_is_sign_req,
.check_sign_req = smb2_check_sign_req,
.set_sign_rsp = smb2_set_sign_rsp
};
static struct smb_version_ops smb3_0_server_ops = {
.get_cmd_val = get_smb2_cmd_val,
.init_rsp_hdr = init_smb2_rsp_hdr,
.set_rsp_status = set_smb2_rsp_status,
.allocate_rsp_buf = smb2_allocate_rsp_buf,
.set_rsp_credits = smb2_set_rsp_credits,
.check_user_session = smb2_check_user_session,
.get_ksmbd_tcon = smb2_get_ksmbd_tcon,
.is_sign_req = smb2_is_sign_req,
.check_sign_req = smb3_check_sign_req,
.set_sign_rsp = smb3_set_sign_rsp,
.generate_signingkey = ksmbd_gen_smb30_signingkey,
.generate_encryptionkey = ksmbd_gen_smb30_encryptionkey,
.is_transform_hdr = smb3_is_transform_hdr,
.decrypt_req = smb3_decrypt_req,
.encrypt_resp = smb3_encrypt_resp
};
static struct smb_version_ops smb3_11_server_ops = {
.get_cmd_val = get_smb2_cmd_val,
.init_rsp_hdr = init_smb2_rsp_hdr,
.set_rsp_status = set_smb2_rsp_status,
.allocate_rsp_buf = smb2_allocate_rsp_buf,
.set_rsp_credits = smb2_set_rsp_credits,
.check_user_session = smb2_check_user_session,
.get_ksmbd_tcon = smb2_get_ksmbd_tcon,
.is_sign_req = smb2_is_sign_req,
.check_sign_req = smb3_check_sign_req,
.set_sign_rsp = smb3_set_sign_rsp,
.generate_signingkey = ksmbd_gen_smb311_signingkey,
.generate_encryptionkey = ksmbd_gen_smb311_encryptionkey,
.is_transform_hdr = smb3_is_transform_hdr,
.decrypt_req = smb3_decrypt_req,
.encrypt_resp = smb3_encrypt_resp
};
static struct smb_version_cmds smb2_0_server_cmds[NUMBER_OF_SMB2_COMMANDS] = {
[SMB2_NEGOTIATE_HE] = { .proc = smb2_negotiate_request, },
[SMB2_SESSION_SETUP_HE] = { .proc = smb2_sess_setup, },
[SMB2_TREE_CONNECT_HE] = { .proc = smb2_tree_connect,},
[SMB2_TREE_DISCONNECT_HE] = { .proc = smb2_tree_disconnect,},
[SMB2_LOGOFF_HE] = { .proc = smb2_session_logoff,},
[SMB2_CREATE_HE] = { .proc = smb2_open},
[SMB2_QUERY_INFO_HE] = { .proc = smb2_query_info},
[SMB2_QUERY_DIRECTORY_HE] = { .proc = smb2_query_dir},
[SMB2_CLOSE_HE] = { .proc = smb2_close},
[SMB2_ECHO_HE] = { .proc = smb2_echo},
[SMB2_SET_INFO_HE] = { .proc = smb2_set_info},
[SMB2_READ_HE] = { .proc = smb2_read},
[SMB2_WRITE_HE] = { .proc = smb2_write},
[SMB2_FLUSH_HE] = { .proc = smb2_flush},
[SMB2_CANCEL_HE] = { .proc = smb2_cancel},
[SMB2_LOCK_HE] = { .proc = smb2_lock},
[SMB2_IOCTL_HE] = { .proc = smb2_ioctl},
[SMB2_OPLOCK_BREAK_HE] = { .proc = smb2_oplock_break},
[SMB2_CHANGE_NOTIFY_HE] = { .proc = smb2_notify},
};
int init_smb2_0_server(struct ksmbd_conn *conn)
{
return -EOPNOTSUPP;
}
/**
* init_smb2_1_server() - initialize a smb server connection with smb2.1
* command dispatcher
* @conn: connection instance
*/
void init_smb2_1_server(struct ksmbd_conn *conn)
{
conn->vals = &smb21_server_values;
conn->ops = &smb2_0_server_ops;
conn->cmds = smb2_0_server_cmds;
conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds);
conn->max_credits = SMB2_MAX_CREDITS;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
}
/**
* init_smb3_0_server() - initialize a smb server connection with smb3.0
* command dispatcher
* @conn: connection instance
*/
void init_smb3_0_server(struct ksmbd_conn *conn)
{
conn->vals = &smb30_server_values;
conn->ops = &smb3_0_server_ops;
conn->cmds = smb2_0_server_cmds;
conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds);
conn->max_credits = SMB2_MAX_CREDITS;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
}
/**
* init_smb3_02_server() - initialize a smb server connection with smb3.02
* command dispatcher
* @conn: connection instance
*/
void init_smb3_02_server(struct ksmbd_conn *conn)
{
conn->vals = &smb302_server_values;
conn->ops = &smb3_0_server_ops;
conn->cmds = smb2_0_server_cmds;
conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds);
conn->max_credits = SMB2_MAX_CREDITS;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
}
/**
* init_smb3_11_server() - initialize a smb server connection with smb3.11
* command dispatcher
* @conn: connection instance
*/
int init_smb3_11_server(struct ksmbd_conn *conn)
{
conn->vals = &smb311_server_values;
conn->ops = &smb3_11_server_ops;
conn->cmds = smb2_0_server_cmds;
conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds);
conn->max_credits = SMB2_MAX_CREDITS;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
if (conn->cipher_type)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
INIT_LIST_HEAD(&conn->preauth_sess_table);
return 0;
}
void init_smb2_max_read_size(unsigned int sz)
{
smb21_server_values.max_read_size = sz;
smb30_server_values.max_read_size = sz;
smb302_server_values.max_read_size = sz;
smb311_server_values.max_read_size = sz;
}
void init_smb2_max_write_size(unsigned int sz)
{
smb21_server_values.max_write_size = sz;
smb30_server_values.max_write_size = sz;
smb302_server_values.max_write_size = sz;
smb311_server_values.max_write_size = sz;
}
void init_smb2_max_trans_size(unsigned int sz)
{
smb21_server_values.max_trans_size = sz;
smb30_server_values.max_trans_size = sz;
smb302_server_values.max_trans_size = sz;
smb311_server_values.max_trans_size = sz;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#ifndef _SMB2PDU_H
#define _SMB2PDU_H
#include "ntlmssp.h"
#include "smbacl.h"
/*
* Note that, due to trying to use names similar to the protocol specifications,
* there are many mixed case field names in the structures below. Although
* this does not match typical Linux kernel style, it is necessary to be
* able to match against the protocol specfication.
*
* SMB2 commands
* Some commands have minimal (wct=0,bcc=0), or uninteresting, responses
* (ie no useful data other than the SMB error code itself) and are marked such.
* Knowing this helps avoid response buffer allocations and copy in some cases.
*/
/* List of commands in host endian */
#define SMB2_NEGOTIATE_HE 0x0000
#define SMB2_SESSION_SETUP_HE 0x0001
#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */
#define SMB2_TREE_CONNECT_HE 0x0003
#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */
#define SMB2_CREATE_HE 0x0005
#define SMB2_CLOSE_HE 0x0006
#define SMB2_FLUSH_HE 0x0007 /* trivial resp */
#define SMB2_READ_HE 0x0008
#define SMB2_WRITE_HE 0x0009
#define SMB2_LOCK_HE 0x000A
#define SMB2_IOCTL_HE 0x000B
#define SMB2_CANCEL_HE 0x000C
#define SMB2_ECHO_HE 0x000D
#define SMB2_QUERY_DIRECTORY_HE 0x000E
#define SMB2_CHANGE_NOTIFY_HE 0x000F
#define SMB2_QUERY_INFO_HE 0x0010
#define SMB2_SET_INFO_HE 0x0011
#define SMB2_OPLOCK_BREAK_HE 0x0012
/* The same list in little endian */
#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE)
#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE)
#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE)
#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE)
#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE)
#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE)
#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE)
#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE)
#define SMB2_READ cpu_to_le16(SMB2_READ_HE)
#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE)
#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE)
#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE)
#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE)
#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE)
#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE)
#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE)
#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE)
#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE)
#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE)
/*Create Action Flags*/
#define FILE_SUPERSEDED 0x00000000
#define FILE_OPENED 0x00000001
#define FILE_CREATED 0x00000002
#define FILE_OVERWRITTEN 0x00000003
/*
* Size of the session key (crypto key encrypted with the password
*/
#define SMB2_NTLMV2_SESSKEY_SIZE 16
#define SMB2_SIGNATURE_SIZE 16
#define SMB2_HMACSHA256_SIZE 32
#define SMB2_CMACAES_SIZE 16
/*
* Size of the smb3 signing key
*/
#define SMB3_SIGN_KEY_SIZE 16
#define CIFS_CLIENT_CHALLENGE_SIZE 8
#define SMB_SERVER_CHALLENGE_SIZE 8
/* SMB2 Max Credits */
#define SMB2_MAX_CREDITS 8192
#define SMB2_CLIENT_GUID_SIZE 16
#define SMB2_CREATE_GUID_SIZE 16
/* Maximum buffer size value we can send with 1 credit */
#define SMB2_MAX_BUFFER_SIZE 65536
#define NUMBER_OF_SMB2_COMMANDS 0x0013
/* BB FIXME - analyze following length BB */
#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */
#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) /* 'B''M''S' */
#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd)
#define SMB21_DEFAULT_IOSIZE (1024 * 1024)
#define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024)
#define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024)
/*
* SMB2 Header Definition
*
* "MBZ" : Must be Zero
* "BB" : BugBug, Something to check/review/analyze later
* "PDU" : "Protocol Data Unit" (ie a network "frame")
*
*/
#define __SMB2_HEADER_STRUCTURE_SIZE 64
#define SMB2_HEADER_STRUCTURE_SIZE \
cpu_to_le16(__SMB2_HEADER_STRUCTURE_SIZE)
struct smb2_hdr {
__be32 smb2_buf_length; /* big endian on wire */
/*
* length is only two or three bytes - with
* one or two byte type preceding it that MBZ
*/
__le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */
__le16 StructureSize; /* 64 */
__le16 CreditCharge; /* MBZ */
__le32 Status; /* Error from server */
__le16 Command;
__le16 CreditRequest; /* CreditResponse */
__le32 Flags;
__le32 NextCommand;
__le64 MessageId;
union {
struct {
__le32 ProcessId;
__le32 TreeId;
} __packed SyncId;
__le64 AsyncId;
} __packed Id;
__le64 SessionId;
__u8 Signature[16];
} __packed;
struct smb2_pdu {
struct smb2_hdr hdr;
__le16 StructureSize2; /* size of wct area (varies, request specific) */
} __packed;
#define SMB3_AES128CCM_NONCE 11
#define SMB3_AES128GCM_NONCE 12
struct smb2_transform_hdr {
__be32 smb2_buf_length; /* big endian on wire */
/*
* length is only two or three bytes - with
* one or two byte type preceding it that MBZ
*/
__le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */
__u8 Signature[16];
__u8 Nonce[16];
__le32 OriginalMessageSize;
__u16 Reserved1;
__le16 Flags; /* EncryptionAlgorithm */
__le64 SessionId;
} __packed;
/*
* SMB2 flag definitions
*/
#define SMB2_FLAGS_SERVER_TO_REDIR cpu_to_le32(0x00000001)
#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002)
#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004)
#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008)
#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000)
#define SMB2_FLAGS_REPLAY_OPERATIONS cpu_to_le32(0x20000000)
/*
* Definitions for SMB2 Protocol Data Units (network frames)
*
* See MS-SMB2.PDF specification for protocol details.
* The Naming convention is the lower case version of the SMB2
* command code name for the struct. Note that structures must be packed.
*
*/
#define SMB2_ERROR_STRUCTURE_SIZE2 9
#define SMB2_ERROR_STRUCTURE_SIZE2_LE cpu_to_le16(SMB2_ERROR_STRUCTURE_SIZE2)
struct smb2_err_rsp {
struct smb2_hdr hdr;
__le16 StructureSize;
__u8 ErrorContextCount;
__u8 Reserved;
__le32 ByteCount; /* even if zero, at least one byte follows */
__u8 ErrorData[1]; /* variable length */
} __packed;
struct smb2_negotiate_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 36 */
__le16 DialectCount;
__le16 SecurityMode;
__le16 Reserved; /* MBZ */
__le32 Capabilities;
__u8 ClientGUID[SMB2_CLIENT_GUID_SIZE];
/* In SMB3.02 and earlier next three were MBZ le64 ClientStartTime */
__le32 NegotiateContextOffset; /* SMB3.1.1 only. MBZ earlier */
__le16 NegotiateContextCount; /* SMB3.1.1 only. MBZ earlier */
__le16 Reserved2;
__le16 Dialects[1]; /* One dialect (vers=) at a time for now */
} __packed;
/* SecurityMode flags */
#define SMB2_NEGOTIATE_SIGNING_ENABLED_LE cpu_to_le16(0x0001)
#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002
#define SMB2_NEGOTIATE_SIGNING_REQUIRED_LE cpu_to_le16(0x0002)
/* Capabilities flags */
#define SMB2_GLOBAL_CAP_DFS 0x00000001
#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */
#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */
#define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */
#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */
#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */
#define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */
/* Internal types */
#define SMB2_NT_FIND 0x00100000
#define SMB2_LARGE_FILES 0x00200000
#define SMB311_SALT_SIZE 32
/* Hash Algorithm Types */
#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001)
#define PREAUTH_HASHVALUE_SIZE 64
struct preauth_integrity_info {
/* PreAuth integrity Hash ID */
__le16 Preauth_HashId;
/* PreAuth integrity Hash Value */
__u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE];
};
/* offset is sizeof smb2_negotiate_rsp - 4 but rounded up to 8 bytes. */
#ifdef CONFIG_SMB_SERVER_KERBEROS5
/* sizeof(struct smb2_negotiate_rsp) - 4 =
* header(64) + response(64) + GSS_LENGTH(96) + GSS_PADDING(0)
*/
#define OFFSET_OF_NEG_CONTEXT 0xe0
#else
/* sizeof(struct smb2_negotiate_rsp) - 4 =
* header(64) + response(64) + GSS_LENGTH(74) + GSS_PADDING(6)
*/
#define OFFSET_OF_NEG_CONTEXT 0xd0
#endif
#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1)
#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2)
#define SMB2_COMPRESSION_CAPABILITIES cpu_to_le16(3)
#define SMB2_NETNAME_NEGOTIATE_CONTEXT_ID cpu_to_le16(5)
#define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100)
struct smb2_neg_context {
__le16 ContextType;
__le16 DataLength;
__le32 Reserved;
/* Followed by array of data */
} __packed;
struct smb2_preauth_neg_context {
__le16 ContextType; /* 1 */
__le16 DataLength;
__le32 Reserved;
__le16 HashAlgorithmCount; /* 1 */
__le16 SaltLength;
__le16 HashAlgorithms; /* HashAlgorithms[0] since only one defined */
__u8 Salt[SMB311_SALT_SIZE];
} __packed;
/* Encryption Algorithms Ciphers */
#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001)
#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002)
struct smb2_encryption_neg_context {
__le16 ContextType; /* 2 */
__le16 DataLength;
__le32 Reserved;
__le16 CipherCount; /* AES-128-GCM and AES-128-CCM */
__le16 Ciphers[1]; /* Ciphers[0] since only one used now */
} __packed;
#define SMB3_COMPRESS_NONE cpu_to_le16(0x0000)
#define SMB3_COMPRESS_LZNT1 cpu_to_le16(0x0001)
#define SMB3_COMPRESS_LZ77 cpu_to_le16(0x0002)
#define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003)
struct smb2_compression_ctx {
__le16 ContextType; /* 3 */
__le16 DataLength;
__le32 Reserved;
__le16 CompressionAlgorithmCount;
__u16 Padding;
__le32 Reserved1;
__le16 CompressionAlgorithms[1];
} __packed;
#define POSIX_CTXT_DATA_LEN 16
struct smb2_posix_neg_context {
__le16 ContextType; /* 0x100 */
__le16 DataLength;
__le32 Reserved;
__u8 Name[16]; /* POSIX ctxt GUID 93AD25509CB411E7B42383DE968BCD7C */
} __packed;
struct smb2_netname_neg_context {
__le16 ContextType; /* 0x100 */
__le16 DataLength;
__le32 Reserved;
__le16 NetName[0]; /* hostname of target converted to UCS-2 */
} __packed;
struct smb2_negotiate_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 65 */
__le16 SecurityMode;
__le16 DialectRevision;
__le16 NegotiateContextCount; /* Prior to SMB3.1.1 was Reserved & MBZ */
__u8 ServerGUID[16];
__le32 Capabilities;
__le32 MaxTransactSize;
__le32 MaxReadSize;
__le32 MaxWriteSize;
__le64 SystemTime; /* MBZ */
__le64 ServerStartTime;
__le16 SecurityBufferOffset;
__le16 SecurityBufferLength;
__le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */
__u8 Buffer[1]; /* variable length GSS security buffer */
} __packed;
/* Flags */
#define SMB2_SESSION_REQ_FLAG_BINDING 0x01
#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04
#define SMB2_SESSION_EXPIRED (0)
#define SMB2_SESSION_IN_PROGRESS (1 << 0)
#define SMB2_SESSION_VALID (1 << 1)
/* Flags */
#define SMB2_SESSION_REQ_FLAG_BINDING 0x01
#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04
struct smb2_sess_setup_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 25 */
__u8 Flags;
__u8 SecurityMode;
__le32 Capabilities;
__le32 Channel;
__le16 SecurityBufferOffset;
__le16 SecurityBufferLength;
__le64 PreviousSessionId;
__u8 Buffer[1]; /* variable length GSS security buffer */
} __packed;
/* Flags/Reserved for SMB3.1.1 */
#define SMB2_SHAREFLAG_CLUSTER_RECONNECT 0x0001
/* Currently defined SessionFlags */
#define SMB2_SESSION_FLAG_IS_GUEST_LE cpu_to_le16(0x0001)
#define SMB2_SESSION_FLAG_IS_NULL_LE cpu_to_le16(0x0002)
#define SMB2_SESSION_FLAG_ENCRYPT_DATA_LE cpu_to_le16(0x0004)
struct smb2_sess_setup_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 9 */
__le16 SessionFlags;
__le16 SecurityBufferOffset;
__le16 SecurityBufferLength;
__u8 Buffer[1]; /* variable length GSS security buffer */
} __packed;
struct smb2_logoff_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 4 */
__le16 Reserved;
} __packed;
struct smb2_logoff_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 4 */
__le16 Reserved;
} __packed;
struct smb2_tree_connect_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 9 */
__le16 Reserved; /* Flags in SMB3.1.1 */
__le16 PathOffset;
__le16 PathLength;
__u8 Buffer[1]; /* variable length */
} __packed;
struct smb2_tree_connect_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 16 */
__u8 ShareType; /* see below */
__u8 Reserved;
__le32 ShareFlags; /* see below */
__le32 Capabilities; /* see below */
__le32 MaximalAccess;
} __packed;
/* Possible ShareType values */
#define SMB2_SHARE_TYPE_DISK 0x01
#define SMB2_SHARE_TYPE_PIPE 0x02
#define SMB2_SHARE_TYPE_PRINT 0x03
/*
* Possible ShareFlags - exactly one and only one of the first 4 caching flags
* must be set (any of the remaining, SHI1005, flags may be set individually
* or in combination.
*/
#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000
#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010
#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020
#define SMB2_SHAREFLAG_NO_CACHING 0x00000030
#define SHI1005_FLAGS_DFS 0x00000001
#define SHI1005_FLAGS_DFS_ROOT 0x00000002
#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100
#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200
#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400
#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800
#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000
#define SHI1005_FLAGS_ENABLE_HASH 0x00002000
/* Possible share capabilities */
#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008)
struct smb2_tree_disconnect_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 4 */
__le16 Reserved;
} __packed;
struct smb2_tree_disconnect_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 4 */
__le16 Reserved;
} __packed;
#define ATTR_READONLY_LE cpu_to_le32(ATTR_READONLY)
#define ATTR_HIDDEN_LE cpu_to_le32(ATTR_HIDDEN)
#define ATTR_SYSTEM_LE cpu_to_le32(ATTR_SYSTEM)
#define ATTR_DIRECTORY_LE cpu_to_le32(ATTR_DIRECTORY)
#define ATTR_ARCHIVE_LE cpu_to_le32(ATTR_ARCHIVE)
#define ATTR_NORMAL_LE cpu_to_le32(ATTR_NORMAL)
#define ATTR_TEMPORARY_LE cpu_to_le32(ATTR_TEMPORARY)
#define ATTR_SPARSE_FILE_LE cpu_to_le32(ATTR_SPARSE)
#define ATTR_REPARSE_POINT_LE cpu_to_le32(ATTR_REPARSE)
#define ATTR_COMPRESSED_LE cpu_to_le32(ATTR_COMPRESSED)
#define ATTR_OFFLINE_LE cpu_to_le32(ATTR_OFFLINE)
#define ATTR_NOT_CONTENT_INDEXED_LE cpu_to_le32(ATTR_NOT_CONTENT_INDEXED)
#define ATTR_ENCRYPTED_LE cpu_to_le32(ATTR_ENCRYPTED)
#define ATTR_INTEGRITY_STREAML_LE cpu_to_le32(0x00008000)
#define ATTR_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000)
#define ATTR_MASK_LE cpu_to_le32(0x00007FB7)
/* Oplock levels */
#define SMB2_OPLOCK_LEVEL_NONE 0x00
#define SMB2_OPLOCK_LEVEL_II 0x01
#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
#define SMB2_OPLOCK_LEVEL_BATCH 0x09
#define SMB2_OPLOCK_LEVEL_LEASE 0xFF
/* Non-spec internal type */
#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99
/* Desired Access Flags */
#define FILE_READ_DATA_LE cpu_to_le32(0x00000001)
#define FILE_LIST_DIRECTORY_LE cpu_to_le32(0x00000001)
#define FILE_WRITE_DATA_LE cpu_to_le32(0x00000002)
#define FILE_ADD_FILE_LE cpu_to_le32(0x00000002)
#define FILE_APPEND_DATA_LE cpu_to_le32(0x00000004)
#define FILE_ADD_SUBDIRECTORY_LE cpu_to_le32(0x00000004)
#define FILE_READ_EA_LE cpu_to_le32(0x00000008)
#define FILE_WRITE_EA_LE cpu_to_le32(0x00000010)
#define FILE_EXECUTE_LE cpu_to_le32(0x00000020)
#define FILE_TRAVERSE_LE cpu_to_le32(0x00000020)
#define FILE_DELETE_CHILD_LE cpu_to_le32(0x00000040)
#define FILE_READ_ATTRIBUTES_LE cpu_to_le32(0x00000080)
#define FILE_WRITE_ATTRIBUTES_LE cpu_to_le32(0x00000100)
#define FILE_DELETE_LE cpu_to_le32(0x00010000)
#define FILE_READ_CONTROL_LE cpu_to_le32(0x00020000)
#define FILE_WRITE_DAC_LE cpu_to_le32(0x00040000)
#define FILE_WRITE_OWNER_LE cpu_to_le32(0x00080000)
#define FILE_SYNCHRONIZE_LE cpu_to_le32(0x00100000)
#define FILE_ACCESS_SYSTEM_SECURITY_LE cpu_to_le32(0x01000000)
#define FILE_MAXIMAL_ACCESS_LE cpu_to_le32(0x02000000)
#define FILE_GENERIC_ALL_LE cpu_to_le32(0x10000000)
#define FILE_GENERIC_EXECUTE_LE cpu_to_le32(0x20000000)
#define FILE_GENERIC_WRITE_LE cpu_to_le32(0x40000000)
#define FILE_GENERIC_READ_LE cpu_to_le32(0x80000000)
#define DESIRED_ACCESS_MASK cpu_to_le32(0xF21F01FF)
/* ShareAccess Flags */
#define FILE_SHARE_READ_LE cpu_to_le32(0x00000001)
#define FILE_SHARE_WRITE_LE cpu_to_le32(0x00000002)
#define FILE_SHARE_DELETE_LE cpu_to_le32(0x00000004)
#define FILE_SHARE_ALL_LE cpu_to_le32(0x00000007)
/* CreateDisposition Flags */
#define FILE_SUPERSEDE_LE cpu_to_le32(0x00000000)
#define FILE_OPEN_LE cpu_to_le32(0x00000001)
#define FILE_CREATE_LE cpu_to_le32(0x00000002)
#define FILE_OPEN_IF_LE cpu_to_le32(0x00000003)
#define FILE_OVERWRITE_LE cpu_to_le32(0x00000004)
#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005)
#define FILE_CREATE_MASK_LE cpu_to_le32(0x00000007)
#define FILE_READ_DESIRED_ACCESS_LE (FILE_READ_DATA_LE | \
FILE_READ_EA_LE | \
FILE_GENERIC_READ_LE)
#define FILE_WRITE_DESIRE_ACCESS_LE (FILE_WRITE_DATA_LE | \
FILE_APPEND_DATA_LE | \
FILE_WRITE_EA_LE | \
FILE_WRITE_ATTRIBUTES_LE | \
FILE_GENERIC_WRITE_LE)
/* Impersonation Levels */
#define IL_ANONYMOUS_LE cpu_to_le32(0x00000000)
#define IL_IDENTIFICATION_LE cpu_to_le32(0x00000001)
#define IL_IMPERSONATION_LE cpu_to_le32(0x00000002)
#define IL_DELEGATE_LE cpu_to_le32(0x00000003)
/* Create Context Values */
#define SMB2_CREATE_EA_BUFFER "ExtA" /* extended attributes */
#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */
#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ"
#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC"
#define SMB2_CREATE_ALLOCATION_SIZE "AlSi"
#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc"
#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp"
#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid"
#define SMB2_CREATE_REQUEST_LEASE "RqLs"
#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 "DH2Q"
#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 "DH2C"
#define SMB2_CREATE_APP_INSTANCE_ID "\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74"
#define SMB2_CREATE_APP_INSTANCE_VERSION "\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10"
#define SVHDX_OPEN_DEVICE_CONTEXT 0x83CE6F1AD851E0986E34401CC9BCFCE9
#define SMB2_CREATE_TAG_POSIX "\x93\xAD\x25\x50\x9C\xB4\x11\xE7\xB4\x23\x83\xDE\x96\x8B\xCD\x7C"
struct smb2_create_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 57 */
__u8 SecurityFlags;
__u8 RequestedOplockLevel;
__le32 ImpersonationLevel;
__le64 SmbCreateFlags;
__le64 Reserved;
__le32 DesiredAccess;
__le32 FileAttributes;
__le32 ShareAccess;
__le32 CreateDisposition;
__le32 CreateOptions;
__le16 NameOffset;
__le16 NameLength;
__le32 CreateContextsOffset;
__le32 CreateContextsLength;
__u8 Buffer[0];
} __packed;
struct smb2_create_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 89 */
__u8 OplockLevel;
__u8 Reserved;
__le32 CreateAction;
__le64 CreationTime;
__le64 LastAccessTime;
__le64 LastWriteTime;
__le64 ChangeTime;
__le64 AllocationSize;
__le64 EndofFile;
__le32 FileAttributes;
__le32 Reserved2;
__le64 PersistentFileId;
__le64 VolatileFileId;
__le32 CreateContextsOffset;
__le32 CreateContextsLength;
__u8 Buffer[1];
} __packed;
struct create_context {
__le32 Next;
__le16 NameOffset;
__le16 NameLength;
__le16 Reserved;
__le16 DataOffset;
__le32 DataLength;
__u8 Buffer[0];
} __packed;
struct create_durable_req_v2 {
struct create_context ccontext;
__u8 Name[8];
__le32 Timeout;
__le32 Flags;
__u8 Reserved[8];
__u8 CreateGuid[16];
} __packed;
struct create_durable_reconn_req {
struct create_context ccontext;
__u8 Name[8];
union {
__u8 Reserved[16];
struct {
__le64 PersistentFileId;
__le64 VolatileFileId;
} Fid;
} Data;
} __packed;
struct create_durable_reconn_v2_req {
struct create_context ccontext;
__u8 Name[8];
struct {
__le64 PersistentFileId;
__le64 VolatileFileId;
} Fid;
__u8 CreateGuid[16];
__le32 Flags;
} __packed;
struct create_app_inst_id {
struct create_context ccontext;
__u8 Name[8];
__u8 Reserved[8];
__u8 AppInstanceId[16];
} __packed;
struct create_app_inst_id_vers {
struct create_context ccontext;
__u8 Name[8];
__u8 Reserved[2];
__u8 Padding[4];
__le64 AppInstanceVersionHigh;
__le64 AppInstanceVersionLow;
} __packed;
struct create_mxac_req {
struct create_context ccontext;
__u8 Name[8];
__le64 Timestamp;
} __packed;
struct create_alloc_size_req {
struct create_context ccontext;
__u8 Name[8];
__le64 AllocationSize;
} __packed;
struct create_posix {
struct create_context ccontext;
__u8 Name[16];
__le32 Mode;
__u32 Reserved;
} __packed;
struct create_durable_rsp {
struct create_context ccontext;
__u8 Name[8];
union {
__u8 Reserved[8];
__u64 data;
} Data;
} __packed;
struct create_durable_v2_rsp {
struct create_context ccontext;
__u8 Name[8];
__le32 Timeout;
__le32 Flags;
} __packed;
struct create_mxac_rsp {
struct create_context ccontext;
__u8 Name[8];
__le32 QueryStatus;
__le32 MaximalAccess;
} __packed;
struct create_disk_id_rsp {
struct create_context ccontext;
__u8 Name[8];
__le64 DiskFileId;
__le64 VolumeId;
__u8 Reserved[16];
} __packed;
/* equivalent of the contents of SMB3.1.1 POSIX open context response */
struct create_posix_rsp {
struct create_context ccontext;
__u8 Name[16];
__le32 nlink;
__le32 reparse_tag;
__le32 mode;
u8 SidBuffer[40];
} __packed;
#define SMB2_LEASE_NONE_LE cpu_to_le32(0x00)
#define SMB2_LEASE_READ_CACHING_LE cpu_to_le32(0x01)
#define SMB2_LEASE_HANDLE_CACHING_LE cpu_to_le32(0x02)
#define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04)
#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02)
struct lease_context {
__le64 LeaseKeyLow;
__le64 LeaseKeyHigh;
__le32 LeaseState;
__le32 LeaseFlags;
__le64 LeaseDuration;
} __packed;
struct create_lease {
struct create_context ccontext;
__u8 Name[8];
struct lease_context lcontext;
} __packed;
/* Currently defined values for close flags */
#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001)
struct smb2_close_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 24 */
__le16 Flags;
__le32 Reserved;
__le64 PersistentFileId;
__le64 VolatileFileId;
} __packed;
struct smb2_close_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* 60 */
__le16 Flags;
__le32 Reserved;
__le64 CreationTime;
__le64 LastAccessTime;
__le64 LastWriteTime;
__le64 ChangeTime;
__le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */
__le64 EndOfFile;
__le32 Attributes;
} __packed;
struct smb2_flush_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 24 */
__le16 Reserved1;
__le32 Reserved2;
__le64 PersistentFileId;
__le64 VolatileFileId;
} __packed;
struct smb2_flush_rsp {
struct smb2_hdr hdr;
__le16 StructureSize;
__le16 Reserved;
} __packed;
struct smb2_buffer_desc_v1 {
__le64 offset;
__le32 token;
__le32 length;
} __packed;
#define SMB2_CHANNEL_NONE cpu_to_le32(0x00000000)
#define SMB2_CHANNEL_RDMA_V1 cpu_to_le32(0x00000001)
#define SMB2_CHANNEL_RDMA_V1_INVALIDATE cpu_to_le32(0x00000002)
struct smb2_read_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 49 */
__u8 Padding; /* offset from start of SMB2 header to place read */
__u8 Reserved;
__le32 Length;
__le64 Offset;
__le64 PersistentFileId;
__le64 VolatileFileId;
__le32 MinimumCount;
__le32 Channel; /* Reserved MBZ */
__le32 RemainingBytes;
__le16 ReadChannelInfoOffset; /* Reserved MBZ */
__le16 ReadChannelInfoLength; /* Reserved MBZ */
__u8 Buffer[1];
} __packed;
struct smb2_read_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 17 */
__u8 DataOffset;
__u8 Reserved;
__le32 DataLength;
__le32 DataRemaining;
__u32 Reserved2;
__u8 Buffer[1];
} __packed;
/* For write request Flags field below the following flag is defined: */
#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001
struct smb2_write_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 49 */
__le16 DataOffset; /* offset from start of SMB2 header to write data */
__le32 Length;
__le64 Offset;
__le64 PersistentFileId;
__le64 VolatileFileId;
__le32 Channel; /* Reserved MBZ */
__le32 RemainingBytes;
__le16 WriteChannelInfoOffset; /* Reserved MBZ */
__le16 WriteChannelInfoLength; /* Reserved MBZ */
__le32 Flags;
__u8 Buffer[1];
} __packed;
struct smb2_write_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 17 */
__u8 DataOffset;
__u8 Reserved;
__le32 DataLength;
__le32 DataRemaining;
__u32 Reserved2;
__u8 Buffer[1];
} __packed;
#define SMB2_0_IOCTL_IS_FSCTL 0x00000001
struct smb2_ioctl_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 57 */
__le16 Reserved; /* offset from start of SMB2 header to write data */
__le32 CntCode;
__le64 PersistentFileId;
__le64 VolatileFileId;
__le32 InputOffset; /* Reserved MBZ */
__le32 InputCount;
__le32 MaxInputResponse;
__le32 OutputOffset;
__le32 OutputCount;
__le32 MaxOutputResponse;
__le32 Flags;
__le32 Reserved2;
__u8 Buffer[1];
} __packed;
struct smb2_ioctl_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 49 */
__le16 Reserved; /* offset from start of SMB2 header to write data */
__le32 CntCode;
__le64 PersistentFileId;
__le64 VolatileFileId;
__le32 InputOffset; /* Reserved MBZ */
__le32 InputCount;
__le32 OutputOffset;
__le32 OutputCount;
__le32 Flags;
__le32 Reserved2;
__u8 Buffer[1];
} __packed;
struct validate_negotiate_info_req {
__le32 Capabilities;
__u8 Guid[SMB2_CLIENT_GUID_SIZE];
__le16 SecurityMode;
__le16 DialectCount;
__le16 Dialects[1]; /* dialect (someday maybe list) client asked for */
} __packed;
struct validate_negotiate_info_rsp {
__le32 Capabilities;
__u8 Guid[SMB2_CLIENT_GUID_SIZE];
__le16 SecurityMode;
__le16 Dialect; /* Dialect in use for the connection */
} __packed;
struct smb_sockaddr_in {
__be16 Port;
__be32 IPv4address;
__u8 Reserved[8];
} __packed;
struct smb_sockaddr_in6 {
__be16 Port;
__be32 FlowInfo;
__u8 IPv6address[16];
__be32 ScopeId;
} __packed;
#define INTERNETWORK 0x0002
#define INTERNETWORKV6 0x0017
struct sockaddr_storage_rsp {
__le16 Family;
union {
struct smb_sockaddr_in addr4;
struct smb_sockaddr_in6 addr6;
};
} __packed;
#define RSS_CAPABLE 0x00000001
#define RDMA_CAPABLE 0x00000002
struct network_interface_info_ioctl_rsp {
__le32 Next; /* next interface. zero if this is last one */
__le32 IfIndex;
__le32 Capability; /* RSS or RDMA Capable */
__le32 Reserved;
__le64 LinkSpeed;
char SockAddr_Storage[128];
} __packed;
struct file_object_buf_type1_ioctl_rsp {
__u8 ObjectId[16];
__u8 BirthVolumeId[16];
__u8 BirthObjectId[16];
__u8 DomainId[16];
} __packed;
struct resume_key_ioctl_rsp {
__le64 ResumeKey[3];
__le32 ContextLength;
__u8 Context[4]; /* ignored, Windows sets to 4 bytes of zero */
} __packed;
struct copychunk_ioctl_req {
__le64 ResumeKey[3];
__le32 ChunkCount;
__le32 Reserved;
__u8 Chunks[1]; /* array of srv_copychunk */
} __packed;
struct srv_copychunk {
__le64 SourceOffset;
__le64 TargetOffset;
__le32 Length;
__le32 Reserved;
} __packed;
struct copychunk_ioctl_rsp {
__le32 ChunksWritten;
__le32 ChunkBytesWritten;
__le32 TotalBytesWritten;
} __packed;
struct file_sparse {
__u8 SetSparse;
} __packed;
struct file_zero_data_information {
__le64 FileOffset;
__le64 BeyondFinalZero;
} __packed;
struct file_allocated_range_buffer {
__le64 file_offset;
__le64 length;
} __packed;
struct reparse_data_buffer {
__le32 ReparseTag;
__le16 ReparseDataLength;
__u16 Reserved;
__u8 DataBuffer[]; /* Variable Length */
} __packed;
/* Completion Filter flags for Notify */
#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001
#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002
#define FILE_NOTIFY_CHANGE_NAME 0x00000003
#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004
#define FILE_NOTIFY_CHANGE_SIZE 0x00000008
#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010
#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020
#define FILE_NOTIFY_CHANGE_CREATION 0x00000040
#define FILE_NOTIFY_CHANGE_EA 0x00000080
#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100
#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200
#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400
#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800
/* Flags */
#define SMB2_WATCH_TREE 0x0001
struct smb2_notify_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 32 */
__le16 Flags;
__le32 OutputBufferLength;
__le64 PersistentFileId;
__le64 VolatileFileId;
__u32 CompletionFileter;
__u32 Reserved;
} __packed;
struct smb2_notify_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 9 */
__le16 OutputBufferOffset;
__le32 OutputBufferLength;
__u8 Buffer[1];
} __packed;
/* SMB2 Notify Action Flags */
#define FILE_ACTION_ADDED 0x00000001
#define FILE_ACTION_REMOVED 0x00000002
#define FILE_ACTION_MODIFIED 0x00000003
#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004
#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005
#define FILE_ACTION_ADDED_STREAM 0x00000006
#define FILE_ACTION_REMOVED_STREAM 0x00000007
#define FILE_ACTION_MODIFIED_STREAM 0x00000008
#define FILE_ACTION_REMOVED_BY_DELETE 0x00000009
#define SMB2_LOCKFLAG_SHARED 0x0001
#define SMB2_LOCKFLAG_EXCLUSIVE 0x0002
#define SMB2_LOCKFLAG_UNLOCK 0x0004
#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x0010
#define SMB2_LOCKFLAG_MASK 0x0007
struct smb2_lock_element {
__le64 Offset;
__le64 Length;
__le32 Flags;
__le32 Reserved;
} __packed;
struct smb2_lock_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 48 */
__le16 LockCount;
__le32 Reserved;
__le64 PersistentFileId;
__le64 VolatileFileId;
/* Followed by at least one */
struct smb2_lock_element locks[1];
} __packed;
struct smb2_lock_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 4 */
__le16 Reserved;
} __packed;
struct smb2_echo_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 4 */
__u16 Reserved;
} __packed;
struct smb2_echo_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 4 */
__u16 Reserved;
} __packed;
/* search (query_directory) Flags field */
#define SMB2_RESTART_SCANS 0x01
#define SMB2_RETURN_SINGLE_ENTRY 0x02
#define SMB2_INDEX_SPECIFIED 0x04
#define SMB2_REOPEN 0x10
struct smb2_query_directory_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 33 */
__u8 FileInformationClass;
__u8 Flags;
__le32 FileIndex;
__le64 PersistentFileId;
__le64 VolatileFileId;
__le16 FileNameOffset;
__le16 FileNameLength;
__le32 OutputBufferLength;
__u8 Buffer[1];
} __packed;
struct smb2_query_directory_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 9 */
__le16 OutputBufferOffset;
__le32 OutputBufferLength;
__u8 Buffer[1];
} __packed;
/* Possible InfoType values */
#define SMB2_O_INFO_FILE 0x01
#define SMB2_O_INFO_FILESYSTEM 0x02
#define SMB2_O_INFO_SECURITY 0x03
#define SMB2_O_INFO_QUOTA 0x04
/* Security info type additionalinfo flags. See MS-SMB2 (2.2.37) or MS-DTYP */
#define OWNER_SECINFO 0x00000001
#define GROUP_SECINFO 0x00000002
#define DACL_SECINFO 0x00000004
#define SACL_SECINFO 0x00000008
#define LABEL_SECINFO 0x00000010
#define ATTRIBUTE_SECINFO 0x00000020
#define SCOPE_SECINFO 0x00000040
#define BACKUP_SECINFO 0x00010000
#define UNPROTECTED_SACL_SECINFO 0x10000000
#define UNPROTECTED_DACL_SECINFO 0x20000000
#define PROTECTED_SACL_SECINFO 0x40000000
#define PROTECTED_DACL_SECINFO 0x80000000
struct smb2_query_info_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 41 */
__u8 InfoType;
__u8 FileInfoClass;
__le32 OutputBufferLength;
__le16 InputBufferOffset;
__u16 Reserved;
__le32 InputBufferLength;
__le32 AdditionalInformation;
__le32 Flags;
__le64 PersistentFileId;
__le64 VolatileFileId;
__u8 Buffer[1];
} __packed;
struct smb2_query_info_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 9 */
__le16 OutputBufferOffset;
__le32 OutputBufferLength;
__u8 Buffer[1];
} __packed;
struct smb2_set_info_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 33 */
__u8 InfoType;
__u8 FileInfoClass;
__le32 BufferLength;
__le16 BufferOffset;
__u16 Reserved;
__le32 AdditionalInformation;
__le64 PersistentFileId;
__le64 VolatileFileId;
__u8 Buffer[1];
} __packed;
struct smb2_set_info_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 2 */
} __packed;
/* FILE Info response size */
#define FILE_DIRECTORY_INFORMATION_SIZE 1
#define FILE_FULL_DIRECTORY_INFORMATION_SIZE 2
#define FILE_BOTH_DIRECTORY_INFORMATION_SIZE 3
#define FILE_BASIC_INFORMATION_SIZE 40
#define FILE_STANDARD_INFORMATION_SIZE 24
#define FILE_INTERNAL_INFORMATION_SIZE 8
#define FILE_EA_INFORMATION_SIZE 4
#define FILE_ACCESS_INFORMATION_SIZE 4
#define FILE_NAME_INFORMATION_SIZE 9
#define FILE_RENAME_INFORMATION_SIZE 10
#define FILE_LINK_INFORMATION_SIZE 11
#define FILE_NAMES_INFORMATION_SIZE 12
#define FILE_DISPOSITION_INFORMATION_SIZE 13
#define FILE_POSITION_INFORMATION_SIZE 14
#define FILE_FULL_EA_INFORMATION_SIZE 15
#define FILE_MODE_INFORMATION_SIZE 4
#define FILE_ALIGNMENT_INFORMATION_SIZE 4
#define FILE_ALL_INFORMATION_SIZE 104
#define FILE_ALLOCATION_INFORMATION_SIZE 19
#define FILE_END_OF_FILE_INFORMATION_SIZE 20
#define FILE_ALTERNATE_NAME_INFORMATION_SIZE 8
#define FILE_STREAM_INFORMATION_SIZE 32
#define FILE_PIPE_INFORMATION_SIZE 23
#define FILE_PIPE_LOCAL_INFORMATION_SIZE 24
#define FILE_PIPE_REMOTE_INFORMATION_SIZE 25
#define FILE_MAILSLOT_QUERY_INFORMATION_SIZE 26
#define FILE_MAILSLOT_SET_INFORMATION_SIZE 27
#define FILE_COMPRESSION_INFORMATION_SIZE 16
#define FILE_OBJECT_ID_INFORMATION_SIZE 29
/* Number 30 not defined in documents */
#define FILE_MOVE_CLUSTER_INFORMATION_SIZE 31
#define FILE_QUOTA_INFORMATION_SIZE 32
#define FILE_REPARSE_POINT_INFORMATION_SIZE 33
#define FILE_NETWORK_OPEN_INFORMATION_SIZE 56
#define FILE_ATTRIBUTE_TAG_INFORMATION_SIZE 8
/* FS Info response size */
#define FS_DEVICE_INFORMATION_SIZE 8
#define FS_ATTRIBUTE_INFORMATION_SIZE 16
#define FS_VOLUME_INFORMATION_SIZE 24
#define FS_SIZE_INFORMATION_SIZE 24
#define FS_FULL_SIZE_INFORMATION_SIZE 32
#define FS_SECTOR_SIZE_INFORMATION_SIZE 28
#define FS_OBJECT_ID_INFORMATION_SIZE 64
#define FS_CONTROL_INFORMATION_SIZE 48
#define FS_POSIX_INFORMATION_SIZE 56
/* FS_ATTRIBUTE_File_System_Name */
#define FS_TYPE_SUPPORT_SIZE 44
struct fs_type_info {
char *fs_name;
long magic_number;
} __packed;
struct smb2_oplock_break {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 24 */
__u8 OplockLevel;
__u8 Reserved;
__le32 Reserved2;
__le64 PersistentFid;
__le64 VolatileFid;
} __packed;
#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01)
struct smb2_lease_break {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 44 */
__le16 Reserved;
__le32 Flags;
__u8 LeaseKey[16];
__le32 CurrentLeaseState;
__le32 NewLeaseState;
__le32 BreakReason;
__le32 AccessMaskHint;
__le32 ShareMaskHint;
} __packed;
struct smb2_lease_ack {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 36 */
__le16 Reserved;
__le32 Flags;
__u8 LeaseKey[16];
__le32 LeaseState;
__le64 LeaseDuration;
} __packed;
/*
* PDU infolevel structure definitions
* BB consider moving to a different header
*/
/* File System Information Classes */
#define FS_VOLUME_INFORMATION 1 /* Query */
#define FS_LABEL_INFORMATION 2 /* Set */
#define FS_SIZE_INFORMATION 3 /* Query */
#define FS_DEVICE_INFORMATION 4 /* Query */
#define FS_ATTRIBUTE_INFORMATION 5 /* Query */
#define FS_CONTROL_INFORMATION 6 /* Query, Set */
#define FS_FULL_SIZE_INFORMATION 7 /* Query */
#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */
#define FS_DRIVER_PATH_INFORMATION 9 /* Query */
#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */
#define FS_POSIX_INFORMATION 100 /* SMB3.1.1 POSIX. Query */
struct smb2_fs_full_size_info {
__le64 TotalAllocationUnits;
__le64 CallerAvailableAllocationUnits;
__le64 ActualAvailableAllocationUnits;
__le32 SectorsPerAllocationUnit;
__le32 BytesPerSector;
} __packed;
#define SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001
#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002
#define SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004
#define SSINFO_FLAGS_TRIM_ENABLED 0x00000008
/* sector size info struct */
struct smb3_fs_ss_info {
__le32 LogicalBytesPerSector;
__le32 PhysicalBytesPerSectorForAtomicity;
__le32 PhysicalBytesPerSectorForPerf;
__le32 FSEffPhysicalBytesPerSectorForAtomicity;
__le32 Flags;
__le32 ByteOffsetForSectorAlignment;
__le32 ByteOffsetForPartitionAlignment;
} __packed;
/* File System Control Information */
struct smb2_fs_control_info {
__le64 FreeSpaceStartFiltering;
__le64 FreeSpaceThreshold;
__le64 FreeSpaceStopFiltering;
__le64 DefaultQuotaThreshold;
__le64 DefaultQuotaLimit;
__le32 FileSystemControlFlags;
__le32 Padding;
} __packed;
/* partial list of QUERY INFO levels */
#define FILE_DIRECTORY_INFORMATION 1
#define FILE_FULL_DIRECTORY_INFORMATION 2
#define FILE_BOTH_DIRECTORY_INFORMATION 3
#define FILE_BASIC_INFORMATION 4
#define FILE_STANDARD_INFORMATION 5
#define FILE_INTERNAL_INFORMATION 6
#define FILE_EA_INFORMATION 7
#define FILE_ACCESS_INFORMATION 8
#define FILE_NAME_INFORMATION 9
#define FILE_RENAME_INFORMATION 10
#define FILE_LINK_INFORMATION 11
#define FILE_NAMES_INFORMATION 12
#define FILE_DISPOSITION_INFORMATION 13
#define FILE_POSITION_INFORMATION 14
#define FILE_FULL_EA_INFORMATION 15
#define FILE_MODE_INFORMATION 16
#define FILE_ALIGNMENT_INFORMATION 17
#define FILE_ALL_INFORMATION 18
#define FILE_ALLOCATION_INFORMATION 19
#define FILE_END_OF_FILE_INFORMATION 20
#define FILE_ALTERNATE_NAME_INFORMATION 21
#define FILE_STREAM_INFORMATION 22
#define FILE_PIPE_INFORMATION 23
#define FILE_PIPE_LOCAL_INFORMATION 24
#define FILE_PIPE_REMOTE_INFORMATION 25
#define FILE_MAILSLOT_QUERY_INFORMATION 26
#define FILE_MAILSLOT_SET_INFORMATION 27
#define FILE_COMPRESSION_INFORMATION 28
#define FILE_OBJECT_ID_INFORMATION 29
/* Number 30 not defined in documents */
#define FILE_MOVE_CLUSTER_INFORMATION 31
#define FILE_QUOTA_INFORMATION 32
#define FILE_REPARSE_POINT_INFORMATION 33
#define FILE_NETWORK_OPEN_INFORMATION 34
#define FILE_ATTRIBUTE_TAG_INFORMATION 35
#define FILE_TRACKING_INFORMATION 36
#define FILEID_BOTH_DIRECTORY_INFORMATION 37
#define FILEID_FULL_DIRECTORY_INFORMATION 38
#define FILE_VALID_DATA_LENGTH_INFORMATION 39
#define FILE_SHORT_NAME_INFORMATION 40
#define FILE_SFIO_RESERVE_INFORMATION 44
#define FILE_SFIO_VOLUME_INFORMATION 45
#define FILE_HARD_LINK_INFORMATION 46
#define FILE_NORMALIZED_NAME_INFORMATION 48
#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50
#define FILE_STANDARD_LINK_INFORMATION 54
#define OP_BREAK_STRUCT_SIZE_20 24
#define OP_BREAK_STRUCT_SIZE_21 36
struct smb2_file_access_info {
__le32 AccessFlags;
} __packed;
struct smb2_file_alignment_info {
__le32 AlignmentRequirement;
} __packed;
struct smb2_file_internal_info {
__le64 IndexNumber;
} __packed; /* level 6 Query */
struct smb2_file_rename_info { /* encoding of request for level 10 */
__u8 ReplaceIfExists; /* 1 = replace existing target with new */
/* 0 = fail if target already exists */
__u8 Reserved[7];
__u64 RootDirectory; /* MBZ for network operations (why says spec?) */
__le32 FileNameLength;
char FileName[0]; /* New name to be assigned */
} __packed; /* level 10 Set */
struct smb2_file_link_info { /* encoding of request for level 11 */
__u8 ReplaceIfExists; /* 1 = replace existing link with new */
/* 0 = fail if link already exists */
__u8 Reserved[7];
__u64 RootDirectory; /* MBZ for network operations (why says spec?) */
__le32 FileNameLength;
char FileName[0]; /* Name to be assigned to new link */
} __packed; /* level 11 Set */
/*
* This level 18, although with struct with same name is different from cifs
* level 0x107. Level 0x107 has an extra u64 between AccessFlags and
* CurrentByteOffset.
*/
struct smb2_file_all_info { /* data block encoding of response to level 18 */
__le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */
__le64 LastAccessTime;
__le64 LastWriteTime;
__le64 ChangeTime;
__le32 Attributes;
__u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */
__le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */
__le64 EndOfFile; /* size ie offset to first free byte in file */
__le32 NumberOfLinks; /* hard links */
__u8 DeletePending;
__u8 Directory;
__u16 Pad2; /* End of FILE_STANDARD_INFO equivalent */
__le64 IndexNumber;
__le32 EASize;
__le32 AccessFlags;
__le64 CurrentByteOffset;
__le32 Mode;
__le32 AlignmentRequirement;
__le32 FileNameLength;
char FileName[1];
} __packed; /* level 18 Query */
struct smb2_file_alt_name_info {
__le32 FileNameLength;
char FileName[0];
} __packed;
struct smb2_file_stream_info {
__le32 NextEntryOffset;
__le32 StreamNameLength;
__le64 StreamSize;
__le64 StreamAllocationSize;
char StreamName[0];
} __packed;
struct smb2_file_eof_info { /* encoding of request for level 10 */
__le64 EndOfFile; /* new end of file value */
} __packed; /* level 20 Set */
struct smb2_file_ntwrk_info {
__le64 CreationTime;
__le64 LastAccessTime;
__le64 LastWriteTime;
__le64 ChangeTime;
__le64 AllocationSize;
__le64 EndOfFile;
__le32 Attributes;
__le32 Reserved;
} __packed;
struct smb2_file_standard_info {
__le64 AllocationSize;
__le64 EndOfFile;
__le32 NumberOfLinks; /* hard links */
__u8 DeletePending;
__u8 Directory;
__le16 Reserved;
} __packed; /* level 18 Query */
struct smb2_file_ea_info {
__le32 EASize;
} __packed;
struct smb2_file_alloc_info {
__le64 AllocationSize;
} __packed;
struct smb2_file_disposition_info {
__u8 DeletePending;
} __packed;
struct smb2_file_pos_info {
__le64 CurrentByteOffset;
} __packed;
#define FILE_MODE_INFO_MASK cpu_to_le32(0x0000103e)
struct smb2_file_mode_info {
__le32 Mode;
} __packed;
#define COMPRESSION_FORMAT_NONE 0x0000
#define COMPRESSION_FORMAT_LZNT1 0x0002
struct smb2_file_comp_info {
__le64 CompressedFileSize;
__le16 CompressionFormat;
__u8 CompressionUnitShift;
__u8 ChunkShift;
__u8 ClusterShift;
__u8 Reserved[3];
} __packed;
struct smb2_file_attr_tag_info {
__le32 FileAttributes;
__le32 ReparseTag;
} __packed;
#define SL_RESTART_SCAN 0x00000001
#define SL_RETURN_SINGLE_ENTRY 0x00000002
#define SL_INDEX_SPECIFIED 0x00000004
struct smb2_ea_info_req {
__le32 NextEntryOffset;
__u8 EaNameLength;
char name[1];
} __packed; /* level 15 Query */
struct smb2_ea_info {
__le32 NextEntryOffset;
__u8 Flags;
__u8 EaNameLength;
__le16 EaValueLength;
char name[1];
/* optionally followed by value */
} __packed; /* level 15 Query */
struct create_ea_buf_req {
struct create_context ccontext;
__u8 Name[8];
struct smb2_ea_info ea;
} __packed;
struct create_sd_buf_req {
struct create_context ccontext;
__u8 Name[8];
struct smb_ntsd ntsd;
} __packed;
/* Find File infolevels */
#define SMB_FIND_FILE_POSIX_INFO 0x064
/* Level 100 query info */
struct smb311_posix_qinfo {
__le64 CreationTime;
__le64 LastAccessTime;
__le64 LastWriteTime;
__le64 ChangeTime;
__le64 EndOfFile;
__le64 AllocationSize;
__le32 DosAttributes;
__le64 Inode;
__le32 DeviceId;
__le32 Zero;
/* beginning of POSIX Create Context Response */
__le32 HardLinks;
__le32 ReparseTag;
__le32 Mode;
u8 Sids[];
/*
* var sized owner SID
* var sized group SID
* le32 filenamelength
* u8 filename[]
*/
} __packed;
struct smb2_posix_info {
__le32 NextEntryOffset;
__u32 Ignored;
__le64 CreationTime;
__le64 LastAccessTime;
__le64 LastWriteTime;
__le64 ChangeTime;
__le64 EndOfFile;
__le64 AllocationSize;
__le32 DosAttributes;
__le64 Inode;
__le32 DeviceId;
__le32 Zero;
/* beginning of POSIX Create Context Response */
__le32 HardLinks;
__le32 ReparseTag;
__le32 Mode;
u8 SidBuffer[40];
__le32 name_len;
u8 name[1];
/*
* var sized owner SID
* var sized group SID
* le32 filenamelength
* u8 filename[]
*/
} __packed;
/* functions */
extern int init_smb2_0_server(struct ksmbd_conn *conn);
extern void init_smb2_1_server(struct ksmbd_conn *conn);
extern void init_smb3_0_server(struct ksmbd_conn *conn);
extern void init_smb3_02_server(struct ksmbd_conn *conn);
extern int init_smb3_11_server(struct ksmbd_conn *conn);
extern void init_smb2_max_read_size(unsigned int sz);
extern void init_smb2_max_write_size(unsigned int sz);
extern void init_smb2_max_trans_size(unsigned int sz);
extern int is_smb2_neg_cmd(struct ksmbd_work *work);
extern int is_smb2_rsp(struct ksmbd_work *work);
extern uint16_t get_smb2_cmd_val(struct ksmbd_work *work);
extern void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err);
extern int init_smb2_rsp_hdr(struct ksmbd_work *work);
extern int smb2_allocate_rsp_buf(struct ksmbd_work *work);
extern bool is_chained_smb2_message(struct ksmbd_work *work);
extern int init_smb2_neg_rsp(struct ksmbd_work *work);
extern void smb2_set_err_rsp(struct ksmbd_work *work);
extern int smb2_check_user_session(struct ksmbd_work *work);
extern int smb2_get_ksmbd_tcon(struct ksmbd_work *work);
extern bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command);
extern int smb2_check_sign_req(struct ksmbd_work *work);
extern void smb2_set_sign_rsp(struct ksmbd_work *work);
extern int smb3_check_sign_req(struct ksmbd_work *work);
extern void smb3_set_sign_rsp(struct ksmbd_work *work);
extern int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects,
__le16 dialects_count);
extern struct file_lock *smb_flock_init(struct file *f);
extern int setup_async_work(struct ksmbd_work *work, void (*fn)(void **),
void **arg);
extern void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status);
extern struct channel *lookup_chann_list(struct ksmbd_session *sess);
extern void smb3_preauth_hash_rsp(struct ksmbd_work *work);
extern int smb3_is_transform_hdr(void *buf);
extern int smb3_decrypt_req(struct ksmbd_work *work);
extern int smb3_encrypt_resp(struct ksmbd_work *work);
extern bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work);
extern int smb2_set_rsp_credits(struct ksmbd_work *work);
/* smb2 misc functions */
extern int ksmbd_smb2_check_message(struct ksmbd_work *work);
/* smb2 command handlers */
extern int smb2_handle_negotiate(struct ksmbd_work *work);
extern int smb2_negotiate_request(struct ksmbd_work *work);
extern int smb2_sess_setup(struct ksmbd_work *work);
extern int smb2_tree_connect(struct ksmbd_work *work);
extern int smb2_tree_disconnect(struct ksmbd_work *work);
extern int smb2_session_logoff(struct ksmbd_work *work);
extern int smb2_open(struct ksmbd_work *work);
extern int smb2_query_info(struct ksmbd_work *work);
extern int smb2_query_dir(struct ksmbd_work *work);
extern int smb2_close(struct ksmbd_work *work);
extern int smb2_echo(struct ksmbd_work *work);
extern int smb2_set_info(struct ksmbd_work *work);
extern int smb2_read(struct ksmbd_work *work);
extern int smb2_write(struct ksmbd_work *work);
extern int smb2_flush(struct ksmbd_work *work);
extern int smb2_cancel(struct ksmbd_work *work);
extern int smb2_lock(struct ksmbd_work *work);
extern int smb2_ioctl(struct ksmbd_work *work);
extern int smb2_oplock_break(struct ksmbd_work *work);
extern int smb2_notify(struct ksmbd_work *ksmbd_work);
#endif /* _SMB2PDU_H */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
* Copyright (C) 2018 Namjae Jeon <linkinjeon@kernel.org>
*/
#include "smb_common.h"
#include "server.h"
#include "misc.h"
#include "smbstatus.h"
/* @FIXME */
#include "connection.h"
#include "ksmbd_work.h"
#include "mgmt/user_session.h"
#include "mgmt/user_config.h"
#include "mgmt/tree_connect.h"
#include "mgmt/share_config.h"
/*for shortname implementation */
static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%";
#define MANGLE_BASE (sizeof(basechars)/sizeof(char)-1)
#define MAGIC_CHAR '~'
#define PERIOD '.'
#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE]))
#define KSMBD_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr))
LIST_HEAD(global_lock_list);
struct smb_protocol {
int index;
char *name;
char *prot;
__u16 prot_id;
};
static struct smb_protocol smb_protos[] = {
{
SMB21_PROT,
"\2SMB 2.1",
"SMB2_10",
SMB21_PROT_ID
},
{
SMB2X_PROT,
"\2SMB 2.???",
"SMB2_22",
SMB2X_PROT_ID
},
{
SMB30_PROT,
"\2SMB 3.0",
"SMB3_00",
SMB30_PROT_ID
},
{
SMB302_PROT,
"\2SMB 3.02",
"SMB3_02",
SMB302_PROT_ID
},
{
SMB311_PROT,
"\2SMB 3.1.1",
"SMB3_11",
SMB311_PROT_ID
},
};
unsigned int ksmbd_server_side_copy_max_chunk_count(void)
{
return 256;
}
unsigned int ksmbd_server_side_copy_max_chunk_size(void)
{
return (2U << 30) - 1;
}
unsigned int ksmbd_server_side_copy_max_total_size(void)
{
return (2U << 30) - 1;
}
inline int ksmbd_min_protocol(void)
{
return SMB2_PROT;
}
inline int ksmbd_max_protocol(void)
{
return SMB311_PROT;
}
int ksmbd_lookup_protocol_idx(char *str)
{
int offt = ARRAY_SIZE(smb_protos) - 1;
int len = strlen(str);
while (offt >= 0) {
if (!strncmp(str, smb_protos[offt].prot, len)) {
ksmbd_debug(SMB, "selected %s dialect idx = %d\n",
smb_protos[offt].prot, offt);
return smb_protos[offt].index;
}
offt--;
}
return -1;
}
/**
* check_message() - check for valid smb2 request header
* @buf: smb2 header to be checked
*
* check for valid smb signature and packet direction(request/response)
*
* Return: 0 on success, otherwise 1
*/
int ksmbd_verify_smb_message(struct ksmbd_work *work)
{
struct smb2_hdr *smb2_hdr = REQUEST_BUF(work);
if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER)
return ksmbd_smb2_check_message(work);
return 0;
}
/**
* is_smb_request() - check for valid smb request type
* @conn: connection instance
* @type: smb request type
*
* Return: true on success, otherwise false
*/
bool ksmbd_smb_request(struct ksmbd_conn *conn)
{
int type = *(char *)conn->request_buf;
switch (type) {
case RFC1002_SESSION_MESSAGE:
/* Regular SMB request */
return true;
case RFC1002_SESSION_KEEP_ALIVE:
ksmbd_debug(SMB, "RFC 1002 session keep alive\n");
break;
default:
ksmbd_debug(SMB, "RFC 1002 unknown request type 0x%x\n", type);
}
return false;
}
static bool supported_protocol(int idx)
{
if (idx == SMB2X_PROT &&
(server_conf.min_protocol >= SMB21_PROT ||
server_conf.max_protocol <= SMB311_PROT))
return true;
return (server_conf.min_protocol <= idx &&
idx <= server_conf.max_protocol);
}
static char *next_dialect(char *dialect, int *next_off)
{
dialect = dialect + *next_off;
*next_off = strlen(dialect);
return dialect;
}
static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count)
{
int i, seq_num, bcount, next;
char *dialect;
for (i = ARRAY_SIZE(smb_protos) - 1; i >= 0; i--) {
seq_num = 0;
next = 0;
dialect = cli_dialects;
bcount = le16_to_cpu(byte_count);
do {
dialect = next_dialect(dialect, &next);
ksmbd_debug(SMB, "client requested dialect %s\n",
dialect);
if (!strcmp(dialect, smb_protos[i].name)) {
if (supported_protocol(smb_protos[i].index)) {
ksmbd_debug(SMB,
"selected %s dialect\n",
smb_protos[i].name);
if (smb_protos[i].index == SMB1_PROT)
return seq_num;
return smb_protos[i].prot_id;
}
}
seq_num++;
bcount -= (++next);
} while (bcount > 0);
}
return BAD_PROT_ID;
}
int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count)
{
int i;
int count;
for (i = ARRAY_SIZE(smb_protos) - 1; i >= 0; i--) {
count = le16_to_cpu(dialects_count);
while (--count >= 0) {
ksmbd_debug(SMB, "client requested dialect 0x%x\n",
le16_to_cpu(cli_dialects[count]));
if (le16_to_cpu(cli_dialects[count]) !=
smb_protos[i].prot_id)
continue;
if (supported_protocol(smb_protos[i].index)) {
ksmbd_debug(SMB, "selected %s dialect\n",
smb_protos[i].name);
return smb_protos[i].prot_id;
}
}
}
return BAD_PROT_ID;
}
int ksmbd_negotiate_smb_dialect(void *buf)
{
__le32 proto;
proto = ((struct smb2_hdr *)buf)->ProtocolId;
if (proto == SMB2_PROTO_NUMBER) {
struct smb2_negotiate_req *req;
req = (struct smb2_negotiate_req *)buf;
return ksmbd_lookup_dialect_by_id(req->Dialects,
req->DialectCount);
}
proto = *(__le32 *)((struct smb_hdr *)buf)->Protocol;
if (proto == SMB1_PROTO_NUMBER) {
struct smb_negotiate_req *req;
req = (struct smb_negotiate_req *)buf;
return ksmbd_lookup_dialect_by_name(req->DialectsArray,
req->ByteCount);
}
return BAD_PROT_ID;
}
#define SMB_COM_NEGOTIATE 0x72
int ksmbd_init_smb_server(struct ksmbd_work *work)
{
struct ksmbd_conn *conn = work->conn;
if (conn->need_neg == false)
return 0;
init_smb3_11_server(conn);
if (conn->ops->get_cmd_val(work) != SMB_COM_NEGOTIATE)
conn->need_neg = false;
return 0;
}
bool ksmbd_pdu_size_has_room(unsigned int pdu)
{
return (pdu >= KSMBD_MIN_SUPPORTED_HEADER_SIZE - 4);
}
int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work,
int info_level,
struct ksmbd_file *dir,
struct ksmbd_dir_info *d_info,
char *search_pattern,
int (*fn)(struct ksmbd_conn *,
int,
struct ksmbd_dir_info *,
struct ksmbd_kstat *))
{
int i, rc = 0;
struct ksmbd_conn *conn = work->conn;
for (i = 0; i < 2; i++) {
struct kstat kstat;
struct ksmbd_kstat ksmbd_kstat;
if (!dir->dot_dotdot[i]) { /* fill dot entry info */
if (i == 0) {
d_info->name = ".";
d_info->name_len = 1;
} else {
d_info->name = "..";
d_info->name_len = 2;
}
if (!match_pattern(d_info->name, search_pattern)) {
dir->dot_dotdot[i] = 1;
continue;
}
ksmbd_kstat.kstat = &kstat;
ksmbd_vfs_fill_dentry_attrs(work,
dir->filp->f_path.dentry->d_parent,
&ksmbd_kstat);
rc = fn(conn, info_level, d_info, &ksmbd_kstat);
if (rc)
break;
if (d_info->out_buf_len <= 0)
break;
dir->dot_dotdot[i] = 1;
if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) {
d_info->out_buf_len = 0;
break;
}
}
}
return rc;
}
/**
* ksmbd_extract_shortname() - get shortname from long filename
* @conn: connection instance
* @longname: source long filename
* @shortname: destination short filename
*
* Return: shortname length or 0 when source long name is '.' or '..'
* TODO: Though this function comforms the restriction of 8.3 Filename spec,
* but the result is different with Windows 7's one. need to check.
*/
int ksmbd_extract_shortname(struct ksmbd_conn *conn,
const char *longname,
char *shortname)
{
const char *p;
char base[9], extension[4];
char out[13] = {0};
int baselen = 0;
int extlen = 0, len = 0;
unsigned int csum = 0;
const unsigned char *ptr;
bool dot_present = true;
p = longname;
if ((*p == '.') || (!(strcmp(p, "..")))) {
/*no mangling required */
return 0;
}
p = strrchr(longname, '.');
if (p == longname) { /*name starts with a dot*/
strscpy(extension, "___", strlen("___"));
} else {
if (p != NULL) {
p++;
while (*p && extlen < 3) {
if (*p != '.')
extension[extlen++] = toupper(*p);
p++;
}
extension[extlen] = '\0';
} else
dot_present = false;
}
p = longname;
if (*p == '.') {
p++;
longname++;
}
while (*p && (baselen < 5)) {
if (*p != '.')
base[baselen++] = toupper(*p);
p++;
}
base[baselen] = MAGIC_CHAR;
memcpy(out, base, baselen+1);
ptr = longname;
len = strlen(longname);
for (; len > 0; len--, ptr++)
csum += *ptr;
csum = csum % (MANGLE_BASE * MANGLE_BASE);
out[baselen+1] = mangle(csum/MANGLE_BASE);
out[baselen+2] = mangle(csum);
out[baselen+3] = PERIOD;
if (dot_present)
memcpy(&out[baselen+4], extension, 4);
else
out[baselen+4] = '\0';
smbConvertToUTF16((__le16 *)shortname, out, PATH_MAX,
conn->local_nls, 0);
len = strlen(out) * 2;
return len;
}
static int __smb2_negotiate(struct ksmbd_conn *conn)
{
return (conn->dialect >= SMB20_PROT_ID &&
conn->dialect <= SMB311_PROT_ID);
}
static int smb_handle_negotiate(struct ksmbd_work *work)
{
struct smb_negotiate_rsp *neg_rsp = RESPONSE_BUF(work);
ksmbd_debug(SMB, "Unsupported SMB protocol\n");
neg_rsp->hdr.Status.CifsError = STATUS_INVALID_LOGON_TYPE;
return -EINVAL;
}
int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command)
{
struct ksmbd_conn *conn = work->conn;
int ret;
conn->dialect = ksmbd_negotiate_smb_dialect(REQUEST_BUF(work));
ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect);
if (command == SMB2_NEGOTIATE_HE) {
struct smb2_hdr *smb2_hdr = REQUEST_BUF(work);
if (smb2_hdr->ProtocolId != SMB2_PROTO_NUMBER) {
ksmbd_debug(SMB, "Downgrade to SMB1 negotiation\n");
command = SMB_COM_NEGOTIATE;
}
}
if (command == SMB2_NEGOTIATE_HE) {
ret = smb2_handle_negotiate(work);
init_smb2_neg_rsp(work);
return ret;
}
if (command == SMB_COM_NEGOTIATE) {
if (__smb2_negotiate(conn)) {
conn->need_neg = true;
init_smb3_11_server(conn);
init_smb2_neg_rsp(work);
ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n");
return 0;
}
return smb_handle_negotiate(work);
}
ksmbd_err("Unknown SMB negotiation command: %u\n", command);
return -EINVAL;
}
enum SHARED_MODE_ERRORS {
SHARE_DELETE_ERROR,
SHARE_READ_ERROR,
SHARE_WRITE_ERROR,
FILE_READ_ERROR,
FILE_WRITE_ERROR,
FILE_DELETE_ERROR,
};
static const char * const shared_mode_errors[] = {
"Current access mode does not permit SHARE_DELETE",
"Current access mode does not permit SHARE_READ",
"Current access mode does not permit SHARE_WRITE",
"Desired access mode does not permit FILE_READ",
"Desired access mode does not permit FILE_WRITE",
"Desired access mode does not permit FILE_DELETE",
};
static void smb_shared_mode_error(int error,
struct ksmbd_file *prev_fp,
struct ksmbd_file *curr_fp)
{
ksmbd_debug(SMB, "%s\n", shared_mode_errors[error]);
ksmbd_debug(SMB, "Current mode: 0x%x Desired mode: 0x%x\n",
prev_fp->saccess, curr_fp->daccess);
}
int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp)
{
int rc = 0;
struct ksmbd_file *prev_fp;
struct list_head *cur;
/*
* Lookup fp in master fp list, and check desired access and
* shared mode between previous open and current open.
*/
read_lock(&curr_fp->f_ci->m_lock);
list_for_each(cur, &curr_fp->f_ci->m_fp_list) {
prev_fp = list_entry(cur, struct ksmbd_file, node);
if (file_inode(filp) != FP_INODE(prev_fp))
continue;
if (filp == prev_fp->filp)
continue;
if (ksmbd_stream_fd(prev_fp) && ksmbd_stream_fd(curr_fp))
if (strcmp(prev_fp->stream.name, curr_fp->stream.name))
continue;
if (prev_fp->is_durable) {
prev_fp->is_durable = 0;
continue;
}
if (prev_fp->attrib_only != curr_fp->attrib_only)
continue;
if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE) &&
curr_fp->daccess & FILE_DELETE_LE) {
smb_shared_mode_error(SHARE_DELETE_ERROR,
prev_fp,
curr_fp);
rc = -EPERM;
break;
}
/*
* Only check FILE_SHARE_DELETE if stream opened and
* normal file opened.
*/
if (ksmbd_stream_fd(prev_fp) && !ksmbd_stream_fd(curr_fp))
continue;
if (!(prev_fp->saccess & FILE_SHARE_READ_LE) &&
curr_fp->daccess & (FILE_EXECUTE_LE |
FILE_READ_DATA_LE)) {
smb_shared_mode_error(SHARE_READ_ERROR,
prev_fp,
curr_fp);
rc = -EPERM;
break;
}
if (!(prev_fp->saccess & FILE_SHARE_WRITE_LE) &&
curr_fp->daccess & (FILE_WRITE_DATA_LE |
FILE_APPEND_DATA_LE)) {
smb_shared_mode_error(SHARE_WRITE_ERROR,
prev_fp,
curr_fp);
rc = -EPERM;
break;
}
if (prev_fp->daccess & (FILE_EXECUTE_LE |
FILE_READ_DATA_LE) &&
!(curr_fp->saccess & FILE_SHARE_READ_LE)) {
smb_shared_mode_error(FILE_READ_ERROR,
prev_fp,
curr_fp);
rc = -EPERM;
break;
}
if (prev_fp->daccess & (FILE_WRITE_DATA_LE |
FILE_APPEND_DATA_LE) &&
!(curr_fp->saccess & FILE_SHARE_WRITE_LE)) {
smb_shared_mode_error(FILE_WRITE_ERROR,
prev_fp,
curr_fp);
rc = -EPERM;
break;
}
if (prev_fp->daccess & FILE_DELETE_LE &&
!(curr_fp->saccess & FILE_SHARE_DELETE_LE)) {
smb_shared_mode_error(FILE_DELETE_ERROR,
prev_fp,
curr_fp);
rc = -EPERM;
break;
}
}
read_unlock(&curr_fp->f_ci->m_lock);
return rc;
}
bool is_asterisk(char *p)
{
return p && p[0] == '*';
}
int ksmbd_override_fsids(struct ksmbd_work *work)
{
struct ksmbd_session *sess = work->sess;
struct ksmbd_share_config *share = work->tcon->share_conf;
struct cred *cred;
struct group_info *gi;
unsigned int uid;
unsigned int gid;
uid = user_uid(sess->user);
gid = user_gid(sess->user);
if (share->force_uid != KSMBD_SHARE_INVALID_UID)
uid = share->force_uid;
if (share->force_gid != KSMBD_SHARE_INVALID_GID)
gid = share->force_gid;
cred = prepare_kernel_cred(NULL);
if (!cred)
return -ENOMEM;
cred->fsuid = make_kuid(current_user_ns(), uid);
cred->fsgid = make_kgid(current_user_ns(), gid);
gi = groups_alloc(0);
if (!gi) {
abort_creds(cred);
return -ENOMEM;
}
set_groups(cred, gi);
put_group_info(gi);
if (!uid_eq(cred->fsuid, GLOBAL_ROOT_UID))
cred->cap_effective = cap_drop_fs_set(cred->cap_effective);
WARN_ON(work->saved_cred != NULL);
work->saved_cred = override_creds(cred);
if (!work->saved_cred) {
abort_creds(cred);
return -EINVAL;
}
return 0;
}
void ksmbd_revert_fsids(struct ksmbd_work *work)
{
const struct cred *cred;
WARN_ON(work->saved_cred == NULL);
cred = current_cred();
revert_creds(work->saved_cred);
put_cred(cred);
work->saved_cred = NULL;
}
__le32 smb_map_generic_desired_access(__le32 daccess)
{
if (daccess & FILE_GENERIC_READ_LE) {
daccess |= cpu_to_le32(GENERIC_READ_FLAGS);
daccess &= ~FILE_GENERIC_READ_LE;
}
if (daccess & FILE_GENERIC_WRITE_LE) {
daccess |= cpu_to_le32(GENERIC_WRITE_FLAGS);
daccess &= ~FILE_GENERIC_WRITE_LE;
}
if (daccess & FILE_GENERIC_EXECUTE_LE) {
daccess |= cpu_to_le32(GENERIC_EXECUTE_FLAGS);
daccess &= ~FILE_GENERIC_EXECUTE_LE;
}
if (daccess & FILE_GENERIC_ALL_LE) {
daccess |= cpu_to_le32(GENERIC_ALL_FLAGS);
daccess &= ~FILE_GENERIC_ALL_LE;
}
return daccess;
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#ifndef __SMB_COMMON_H__
#define __SMB_COMMON_H__
#include <linux/kernel.h>
#include "glob.h"
#include "nterr.h"
#include "smb2pdu.h"
/* ksmbd's Specific ERRNO */
#define ESHARE 50000
#define SMB1_PROT 0
#define SMB2_PROT 1
#define SMB21_PROT 2
/* multi-protocol negotiate request */
#define SMB2X_PROT 3
#define SMB30_PROT 4
#define SMB302_PROT 5
#define SMB311_PROT 6
#define BAD_PROT 0xFFFF
#define SMB1_VERSION_STRING "1.0"
#define SMB20_VERSION_STRING "2.0"
#define SMB21_VERSION_STRING "2.1"
#define SMB30_VERSION_STRING "3.0"
#define SMB302_VERSION_STRING "3.02"
#define SMB311_VERSION_STRING "3.1.1"
/* Dialects */
#define SMB10_PROT_ID 0x00
#define SMB20_PROT_ID 0x0202
#define SMB21_PROT_ID 0x0210
/* multi-protocol negotiate request */
#define SMB2X_PROT_ID 0x02FF
#define SMB30_PROT_ID 0x0300
#define SMB302_PROT_ID 0x0302
#define SMB311_PROT_ID 0x0311
#define BAD_PROT_ID 0xFFFF
#define SMB_ECHO_INTERVAL (60*HZ)
#define CIFS_DEFAULT_IOSIZE (64 * 1024)
#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
extern struct list_head global_lock_list;
#define IS_SMB2(x) ((x)->vals->protocol_id != SMB10_PROT_ID)
#define HEADER_SIZE(conn) ((conn)->vals->header_size)
#define HEADER_SIZE_NO_BUF_LEN(conn) ((conn)->vals->header_size - 4)
#define MAX_HEADER_SIZE(conn) ((conn)->vals->max_header_size)
/* RFC 1002 session packet types */
#define RFC1002_SESSION_MESSAGE 0x00
#define RFC1002_SESSION_REQUEST 0x81
#define RFC1002_POSITIVE_SESSION_RESPONSE 0x82
#define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83
#define RFC1002_RETARGET_SESSION_RESPONSE 0x84
#define RFC1002_SESSION_KEEP_ALIVE 0x85
/* Responses when opening a file. */
#define F_SUPERSEDED 0
#define F_OPENED 1
#define F_CREATED 2
#define F_OVERWRITTEN 3
/*
* File Attribute flags
*/
#define ATTR_READONLY 0x0001
#define ATTR_HIDDEN 0x0002
#define ATTR_SYSTEM 0x0004
#define ATTR_VOLUME 0x0008
#define ATTR_DIRECTORY 0x0010
#define ATTR_ARCHIVE 0x0020
#define ATTR_DEVICE 0x0040
#define ATTR_NORMAL 0x0080
#define ATTR_TEMPORARY 0x0100
#define ATTR_SPARSE 0x0200
#define ATTR_REPARSE 0x0400
#define ATTR_COMPRESSED 0x0800
#define ATTR_OFFLINE 0x1000
#define ATTR_NOT_CONTENT_INDEXED 0x2000
#define ATTR_ENCRYPTED 0x4000
#define ATTR_POSIX_SEMANTICS 0x01000000
#define ATTR_BACKUP_SEMANTICS 0x02000000
#define ATTR_DELETE_ON_CLOSE 0x04000000
#define ATTR_SEQUENTIAL_SCAN 0x08000000
#define ATTR_RANDOM_ACCESS 0x10000000
#define ATTR_NO_BUFFERING 0x20000000
#define ATTR_WRITE_THROUGH 0x80000000
#define ATTR_READONLY_LE cpu_to_le32(ATTR_READONLY)
#define ATTR_HIDDEN_LE cpu_to_le32(ATTR_HIDDEN)
#define ATTR_SYSTEM_LE cpu_to_le32(ATTR_SYSTEM)
#define ATTR_DIRECTORY_LE cpu_to_le32(ATTR_DIRECTORY)
#define ATTR_ARCHIVE_LE cpu_to_le32(ATTR_ARCHIVE)
#define ATTR_NORMAL_LE cpu_to_le32(ATTR_NORMAL)
#define ATTR_TEMPORARY_LE cpu_to_le32(ATTR_TEMPORARY)
#define ATTR_SPARSE_FILE_LE cpu_to_le32(ATTR_SPARSE)
#define ATTR_REPARSE_POINT_LE cpu_to_le32(ATTR_REPARSE)
#define ATTR_COMPRESSED_LE cpu_to_le32(ATTR_COMPRESSED)
#define ATTR_OFFLINE_LE cpu_to_le32(ATTR_OFFLINE)
#define ATTR_NOT_CONTENT_INDEXED_LE cpu_to_le32(ATTR_NOT_CONTENT_INDEXED)
#define ATTR_ENCRYPTED_LE cpu_to_le32(ATTR_ENCRYPTED)
#define ATTR_INTEGRITY_STREAML_LE cpu_to_le32(0x00008000)
#define ATTR_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000)
#define ATTR_MASK_LE cpu_to_le32(0x00007FB7)
/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */
#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */
#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */
#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000
#define FILE_SUPPORTS_USN_JOURNAL 0x02000000
#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000
#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000
#define FILE_SUPPORTS_HARD_LINKS 0x00400000
#define FILE_SUPPORTS_TRANSACTIONS 0x00200000
#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000
#define FILE_READ_ONLY_VOLUME 0x00080000
#define FILE_NAMED_STREAMS 0x00040000
#define FILE_SUPPORTS_ENCRYPTION 0x00020000
#define FILE_SUPPORTS_OBJECT_IDS 0x00010000
#define FILE_VOLUME_IS_COMPRESSED 0x00008000
#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100
#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080
#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
#define FILE_VOLUME_QUOTAS 0x00000020
#define FILE_FILE_COMPRESSION 0x00000010
#define FILE_PERSISTENT_ACLS 0x00000008
#define FILE_UNICODE_ON_DISK 0x00000004
#define FILE_CASE_PRESERVED_NAMES 0x00000002
#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */
#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */
#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */
#define FILE_READ_EA 0x00000008 /* Extended attributes associated */
/* with the file can be read */
#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */
/* with the file can be written */
#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */
/* the file using system paging I/O */
#define FILE_DELETE_CHILD 0x00000040
#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */
/* file can be read */
#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */
/* file can be written */
#define DELETE 0x00010000 /* The file can be deleted */
#define READ_CONTROL 0x00020000 /* The access control list and */
/* ownership associated with the */
/* file can be read */
#define WRITE_DAC 0x00040000 /* The access control list and */
/* ownership associated with the */
/* file can be written. */
#define WRITE_OWNER 0x00080000 /* Ownership information associated */
/* with the file can be written */
#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */
/* synchronize with the completion */
/* of an input/output request */
#define GENERIC_ALL 0x10000000
#define GENERIC_EXECUTE 0x20000000
#define GENERIC_WRITE 0x40000000
#define GENERIC_READ 0x80000000
/* In summary - Relevant file */
/* access flags from CIFS are */
/* file_read_data, file_write_data */
/* file_execute, file_read_attributes*/
/* write_dac, and delete. */
#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES)
#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
| FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
#define FILE_EXEC_RIGHTS (FILE_EXECUTE)
#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \
| FILE_READ_ATTRIBUTES \
| DELETE | READ_CONTROL | WRITE_DAC \
| WRITE_OWNER | SYNCHRONIZE)
#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
| FILE_WRITE_EA \
| FILE_DELETE_CHILD \
| FILE_WRITE_ATTRIBUTES \
| DELETE | READ_CONTROL | WRITE_DAC \
| WRITE_OWNER | SYNCHRONIZE)
#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \
| FILE_READ_ATTRIBUTES \
| FILE_WRITE_ATTRIBUTES \
| DELETE | READ_CONTROL | WRITE_DAC \
| WRITE_OWNER | SYNCHRONIZE)
#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \
| READ_CONTROL | SYNCHRONIZE)
/* generic flags for file open */
#define GENERIC_READ_FLAGS (READ_CONTROL | FILE_READ_DATA | \
FILE_READ_ATTRIBUTES | \
FILE_READ_EA | SYNCHRONIZE)
#define GENERIC_WRITE_FLAGS (READ_CONTROL | FILE_WRITE_DATA | \
FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | \
FILE_APPEND_DATA | SYNCHRONIZE)
#define GENERIC_EXECUTE_FLAGS (READ_CONTROL | FILE_EXECUTE | \
FILE_READ_ATTRIBUTES | SYNCHRONIZE)
#define GENERIC_ALL_FLAGS (DELETE | READ_CONTROL | WRITE_DAC | \
WRITE_OWNER | SYNCHRONIZE | FILE_READ_DATA | \
FILE_WRITE_DATA | FILE_APPEND_DATA | \
FILE_READ_EA | FILE_WRITE_EA | \
FILE_EXECUTE | FILE_DELETE_CHILD | \
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES)
#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff)
#define SMB1_CLIENT_GUID_SIZE (16)
struct smb_hdr {
__be32 smb_buf_length;
__u8 Protocol[4];
__u8 Command;
union {
struct {
__u8 ErrorClass;
__u8 Reserved;
__le16 Error;
} __packed DosError;
__le32 CifsError;
} __packed Status;
__u8 Flags;
__le16 Flags2; /* note: le */
__le16 PidHigh;
union {
struct {
__le32 SequenceNumber; /* le */
__u32 Reserved; /* zero */
} __packed Sequence;
__u8 SecuritySignature[8]; /* le */
} __packed Signature;
__u8 pad[2];
__le16 Tid;
__le16 Pid;
__le16 Uid;
__le16 Mid;
__u8 WordCount;
} __packed;
struct smb_negotiate_req {
struct smb_hdr hdr; /* wct = 0 */
__le16 ByteCount;
unsigned char DialectsArray[1];
} __packed;
struct smb_negotiate_rsp {
struct smb_hdr hdr; /* wct = 17 */
__le16 DialectIndex; /* 0xFFFF = no dialect acceptable */
__u8 SecurityMode;
__le16 MaxMpxCount;
__le16 MaxNumberVcs;
__le32 MaxBufferSize;
__le32 MaxRawSize;
__le32 SessionKey;
__le32 Capabilities; /* see below */
__le32 SystemTimeLow;
__le32 SystemTimeHigh;
__le16 ServerTimeZone;
__u8 EncryptionKeyLength;
__le16 ByteCount;
union {
unsigned char EncryptionKey[8]; /* cap extended security off */
/* followed by Domain name - if extended security is off */
/* followed by 16 bytes of server GUID */
/* then security blob if cap_extended_security negotiated */
struct {
unsigned char GUID[SMB1_CLIENT_GUID_SIZE];
unsigned char SecurityBlob[1];
} __packed extended_response;
} __packed u;
} __packed;
struct filesystem_attribute_info {
__le32 Attributes;
__le32 MaxPathNameComponentLength;
__le32 FileSystemNameLen;
__le16 FileSystemName[1]; /* do not have to save this - get subset? */
} __packed;
struct filesystem_device_info {
__le32 DeviceType;
__le32 DeviceCharacteristics;
} __packed; /* device info level 0x104 */
struct filesystem_vol_info {
__le64 VolumeCreationTime;
__le32 SerialNumber;
__le32 VolumeLabelSize;
__le16 Reserved;
__le16 VolumeLabel[1];
} __packed;
struct filesystem_info {
__le64 TotalAllocationUnits;
__le64 FreeAllocationUnits;
__le32 SectorsPerAllocationUnit;
__le32 BytesPerSector;
} __packed; /* size info, level 0x103 */
#define EXTENDED_INFO_MAGIC 0x43667364 /* Cfsd */
#define STRING_LENGTH 28
struct fs_extended_info {
__le32 magic;
__le32 version;
__le32 release;
__u64 rel_date;
char version_string[STRING_LENGTH];
} __packed;
struct object_id_info {
char objid[16];
struct fs_extended_info extended_info;
} __packed;
struct file_directory_info {
__le32 NextEntryOffset;
__u32 FileIndex;
__le64 CreationTime;
__le64 LastAccessTime;
__le64 LastWriteTime;
__le64 ChangeTime;
__le64 EndOfFile;
__le64 AllocationSize;
__le32 ExtFileAttributes;
__le32 FileNameLength;
char FileName[1];
} __packed; /* level 0x101 FF resp data */
struct file_names_info {
__le32 NextEntryOffset;
__u32 FileIndex;
__le32 FileNameLength;
char FileName[1];
} __packed; /* level 0xc FF resp data */
struct file_full_directory_info {
__le32 NextEntryOffset;
__u32 FileIndex;
__le64 CreationTime;
__le64 LastAccessTime;
__le64 LastWriteTime;
__le64 ChangeTime;
__le64 EndOfFile;
__le64 AllocationSize;
__le32 ExtFileAttributes;
__le32 FileNameLength;
__le32 EaSize;
char FileName[1];
} __packed; /* level 0x102 FF resp */
struct file_both_directory_info {
__le32 NextEntryOffset;
__u32 FileIndex;
__le64 CreationTime;
__le64 LastAccessTime;
__le64 LastWriteTime;
__le64 ChangeTime;
__le64 EndOfFile;
__le64 AllocationSize;
__le32 ExtFileAttributes;
__le32 FileNameLength;
__le32 EaSize; /* length of the xattrs */
__u8 ShortNameLength;
__u8 Reserved;
__u8 ShortName[24];
char FileName[1];
} __packed; /* level 0x104 FFrsp data */
struct file_id_both_directory_info {
__le32 NextEntryOffset;
__u32 FileIndex;
__le64 CreationTime;
__le64 LastAccessTime;
__le64 LastWriteTime;
__le64 ChangeTime;
__le64 EndOfFile;
__le64 AllocationSize;
__le32 ExtFileAttributes;
__le32 FileNameLength;
__le32 EaSize; /* length of the xattrs */
__u8 ShortNameLength;
__u8 Reserved;
__u8 ShortName[24];
__le16 Reserved2;
__le64 UniqueId;
char FileName[1];
} __packed;
struct file_id_full_dir_info {
__le32 NextEntryOffset;
__u32 FileIndex;
__le64 CreationTime;
__le64 LastAccessTime;
__le64 LastWriteTime;
__le64 ChangeTime;
__le64 EndOfFile;
__le64 AllocationSize;
__le32 ExtFileAttributes;
__le32 FileNameLength;
__le32 EaSize; /* EA size */
__le32 Reserved;
__le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
char FileName[1];
} __packed; /* level 0x105 FF rsp data */
struct smb_version_values {
char *version_string;
__u16 protocol_id;
__le16 lock_cmd;
__u32 capabilities;
__u32 max_read_size;
__u32 max_write_size;
__u32 max_trans_size;
__u32 large_lock_type;
__u32 exclusive_lock_type;
__u32 shared_lock_type;
__u32 unlock_lock_type;
size_t header_size;
size_t max_header_size;
size_t read_rsp_size;
unsigned int cap_unix;
unsigned int cap_nt_find;
unsigned int cap_large_files;
__u16 signing_enabled;
__u16 signing_required;
size_t create_lease_size;
size_t create_durable_size;
size_t create_durable_v2_size;
size_t create_mxac_size;
size_t create_disk_id_size;
size_t create_posix_size;
};
struct filesystem_posix_info {
/* For undefined recommended transfer size return -1 in that field */
__le32 OptimalTransferSize; /* bsize on some os, iosize on other os */
__le32 BlockSize;
/* The next three fields are in terms of the block size.
* (above). If block size is unknown, 4096 would be a
* reasonable block size for a server to report.
* Note that returning the blocks/blocksavail removes need
* to make a second call (to QFSInfo level 0x103 to get this info.
* UserBlockAvail is typically less than or equal to BlocksAvail,
* if no distinction is made return the same value in each
*/
__le64 TotalBlocks;
__le64 BlocksAvail; /* bfree */
__le64 UserBlocksAvail; /* bavail */
/* For undefined Node fields or FSID return -1 */
__le64 TotalFileNodes;
__le64 FreeFileNodes;
__le64 FileSysIdentifier; /* fsid */
/* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */
/* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */
} __packed;
struct smb_version_ops {
uint16_t (*get_cmd_val)(struct ksmbd_work *swork);
int (*init_rsp_hdr)(struct ksmbd_work *swork);
void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err);
int (*allocate_rsp_buf)(struct ksmbd_work *work);
int (*set_rsp_credits)(struct ksmbd_work *work);
int (*check_user_session)(struct ksmbd_work *work);
int (*get_ksmbd_tcon)(struct ksmbd_work *work);
bool (*is_sign_req)(struct ksmbd_work *work, unsigned int command);
int (*check_sign_req)(struct ksmbd_work *work);
void (*set_sign_rsp)(struct ksmbd_work *work);
int (*generate_signingkey)(struct ksmbd_session *sess);
int (*generate_encryptionkey)(struct ksmbd_session *sess);
int (*is_transform_hdr)(void *buf);
int (*decrypt_req)(struct ksmbd_work *work);
int (*encrypt_resp)(struct ksmbd_work *work);
};
struct smb_version_cmds {
int (*proc)(struct ksmbd_work *swork);
};
int ksmbd_min_protocol(void);
int ksmbd_max_protocol(void);
int ksmbd_lookup_protocol_idx(char *str);
int ksmbd_verify_smb_message(struct ksmbd_work *work);
bool ksmbd_smb_request(struct ksmbd_conn *conn);
int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count);
int ksmbd_negotiate_smb_dialect(void *buf);
int ksmbd_init_smb_server(struct ksmbd_work *work);
bool ksmbd_pdu_size_has_room(unsigned int pdu);
struct ksmbd_kstat;
int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work,
int info_level,
struct ksmbd_file *dir,
struct ksmbd_dir_info *d_info,
char *search_pattern,
int (*fn)(struct ksmbd_conn *,
int,
struct ksmbd_dir_info *,
struct ksmbd_kstat *));
int ksmbd_extract_shortname(struct ksmbd_conn *conn,
const char *longname,
char *shortname);
int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command);
int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp);
int ksmbd_override_fsids(struct ksmbd_work *work);
void ksmbd_revert_fsids(struct ksmbd_work *work);
unsigned int ksmbd_server_side_copy_max_chunk_count(void);
unsigned int ksmbd_server_side_copy_max_chunk_size(void);
unsigned int ksmbd_server_side_copy_max_total_size(void);
bool is_asterisk(char *p);
__le32 smb_map_generic_desired_access(__le32 daccess);
static inline unsigned int get_rfc1002_len(void *buf)
{
return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
}
static inline void inc_rfc1001_len(void *buf, int count)
{
be32_add_cpu((__be32 *)buf, count);
}
#endif /* __SMB_COMMON_H__ */
// SPDX-License-Identifier: LGPL-2.1+
/*
* Copyright (C) International Business Machines Corp., 2007,2008
* Author(s): Steve French (sfrench@us.ibm.com)
* Copyright (C) 2020 Samsung Electronics Co., Ltd.
* Author(s): Namjae Jeon <linkinjeon@kernel.org>
*/
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "smbacl.h"
#include "smb_common.h"
#include "server.h"
#include "misc.h"
#include "ksmbd_server.h"
#include "mgmt/share_config.h"
static const struct smb_sid domain = {1, 4, {0, 0, 0, 0, 0, 5},
{cpu_to_le32(21), cpu_to_le32(1), cpu_to_le32(2), cpu_to_le32(3),
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
/* security id for everyone/world system group */
static const struct smb_sid creator_owner = {
1, 1, {0, 0, 0, 0, 0, 3}, {0} };
/* security id for everyone/world system group */
static const struct smb_sid creator_group = {
1, 1, {0, 0, 0, 0, 0, 3}, {cpu_to_le32(1)} };
/* security id for everyone/world system group */
static const struct smb_sid sid_everyone = {
1, 1, {0, 0, 0, 0, 0, 1}, {0} };
/* security id for Authenticated Users system group */
static const struct smb_sid sid_authusers = {
1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} };
/* S-1-22-1 Unmapped Unix users */
static const struct smb_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22},
{cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
/* S-1-22-2 Unmapped Unix groups */
static const struct smb_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22},
{cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
/*
* See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx
*/
/* S-1-5-88 MS NFS and Apple style UID/GID/mode */
/* S-1-5-88-1 Unix uid */
static const struct smb_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5},
{cpu_to_le32(88),
cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
/* S-1-5-88-2 Unix gid */
static const struct smb_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5},
{cpu_to_le32(88),
cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
/* S-1-5-88-3 Unix mode */
static const struct smb_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5},
{cpu_to_le32(88),
cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
/*
* if the two SIDs (roughly equivalent to a UUID for a user or group) are
* the same returns zero, if they do not match returns non-zero.
*/
int
compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid)
{
int i;
int num_subauth, num_sat, num_saw;
if ((!ctsid) || (!cwsid))
return 1;
/* compare the revision */
if (ctsid->revision != cwsid->revision) {
if (ctsid->revision > cwsid->revision)
return 1;
else
return -1;
}
/* compare all of the six auth values */
for (i = 0; i < NUM_AUTHS; ++i) {
if (ctsid->authority[i] != cwsid->authority[i]) {
if (ctsid->authority[i] > cwsid->authority[i])
return 1;
else
return -1;
}
}
/* compare all of the subauth values if any */
num_sat = ctsid->num_subauth;
num_saw = cwsid->num_subauth;
num_subauth = num_sat < num_saw ? num_sat : num_saw;
if (num_subauth) {
for (i = 0; i < num_subauth; ++i) {
if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
if (le32_to_cpu(ctsid->sub_auth[i]) >
le32_to_cpu(cwsid->sub_auth[i]))
return 1;
else
return -1;
}
}
}
return 0; /* sids compare/match */
}
static void
smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src)
{
int i;
dst->revision = src->revision;
dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES);
for (i = 0; i < NUM_AUTHS; ++i)
dst->authority[i] = src->authority[i];
for (i = 0; i < dst->num_subauth; ++i)
dst->sub_auth[i] = src->sub_auth[i];
}
/*
* change posix mode to reflect permissions
* pmode is the existing mode (we only want to overwrite part of this
* bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007
*/
static umode_t access_flags_to_mode(struct smb_fattr *fattr, __le32 ace_flags,
int type)
{
__u32 flags = le32_to_cpu(ace_flags);
umode_t mode = 0;
if (flags & GENERIC_ALL) {
mode = 0777;
ksmbd_debug(SMB, "all perms\n");
return mode;
}
if ((flags & GENERIC_READ) ||
(flags & FILE_READ_RIGHTS))
mode = 0444;
if ((flags & GENERIC_WRITE) ||
(flags & FILE_WRITE_RIGHTS)) {
mode |= 0222;
if (S_ISDIR(fattr->cf_mode))
mode |= 0111;
}
if ((flags & GENERIC_EXECUTE) ||
(flags & FILE_EXEC_RIGHTS))
mode |= 0111;
if (type == ACCESS_DENIED_ACE_TYPE ||
type == ACCESS_DENIED_OBJECT_ACE_TYPE)
mode = ~mode;
ksmbd_debug(SMB, "access flags 0x%x mode now %04o\n", flags, mode);
return mode;
}
/*
* Generate access flags to reflect permissions mode is the existing mode.
* This function is called for every ACE in the DACL whose SID matches
* with either owner or group or everyone.
*/
static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,
__u32 *pace_flags)
{
/* reset access mask */
*pace_flags = 0x0;
/* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */
mode &= bits_to_use;
/*
* check for R/W/X UGO since we do not know whose flags
* is this but we have cleared all the bits sans RWX for
* either user or group or other as per bits_to_use
*/
if (mode & 0444)
*pace_flags |= SET_FILE_READ_RIGHTS;
if (mode & 0222)
*pace_flags |= FILE_WRITE_RIGHTS;
if (mode & 0111)
*pace_flags |= SET_FILE_EXEC_RIGHTS;
ksmbd_debug(SMB, "mode: %o, access flags now 0x%x\n",
mode, *pace_flags);
}
static __u16 fill_ace_for_sid(struct smb_ace *pntace,
const struct smb_sid *psid, int type, int flags,
umode_t mode, umode_t bits)
{
int i;
__u16 size = 0;
__u32 access_req = 0;
pntace->type = type;
pntace->flags = flags;
mode_to_access_flags(mode, bits, &access_req);
if (!access_req)
access_req = SET_MINIMUM_RIGHTS;
pntace->access_req = cpu_to_le32(access_req);
pntace->sid.revision = psid->revision;
pntace->sid.num_subauth = psid->num_subauth;
for (i = 0; i < NUM_AUTHS; i++)
pntace->sid.authority[i] = psid->authority[i];
for (i = 0; i < psid->num_subauth; i++)
pntace->sid.sub_auth[i] = psid->sub_auth[i];
size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4);
pntace->size = cpu_to_le16(size);
return size;
}
void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid)
{
switch (sidtype) {
case SIDOWNER:
smb_copy_sid(ssid, &server_conf.domain_sid);
break;
case SIDUNIX_USER:
smb_copy_sid(ssid, &sid_unix_users);
break;
case SIDUNIX_GROUP:
smb_copy_sid(ssid, &sid_unix_groups);
break;
case SIDCREATOR_OWNER:
smb_copy_sid(ssid, &creator_owner);
return;
case SIDCREATOR_GROUP:
smb_copy_sid(ssid, &creator_group);
return;
case SIDNFS_USER:
smb_copy_sid(ssid, &sid_unix_NFS_users);
break;
case SIDNFS_GROUP:
smb_copy_sid(ssid, &sid_unix_NFS_groups);
break;
case SIDNFS_MODE:
smb_copy_sid(ssid, &sid_unix_NFS_mode);
break;
default:
return;
}
/* RID */
ssid->sub_auth[ssid->num_subauth] = cpu_to_le32(cid);
ssid->num_subauth++;
}
static int sid_to_id(struct smb_sid *psid, uint sidtype,
struct smb_fattr *fattr)
{
int rc = -EINVAL;
/*
* If we have too many subauthorities, then something is really wrong.
* Just return an error.
*/
if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) {
ksmbd_err("%s: %u subauthorities is too many!\n",
__func__, psid->num_subauth);
return -EIO;
}
if (sidtype == SIDOWNER) {
kuid_t uid;
uid_t id;
id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]);
if (id > 0) {
uid = make_kuid(&init_user_ns, id);
if (uid_valid(uid) &&
kuid_has_mapping(&init_user_ns, uid)) {
fattr->cf_uid = uid;
rc = 0;
}
}
} else {
kgid_t gid;
gid_t id;
id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]);
if (id > 0) {
gid = make_kgid(&init_user_ns, id);
if (gid_valid(gid) &&
kgid_has_mapping(&init_user_ns, gid)) {
fattr->cf_gid = gid;
rc = 0;
}
}
}
return rc;
}
void posix_state_to_acl(struct posix_acl_state *state,
struct posix_acl_entry *pace)
{
int i;
pace->e_tag = ACL_USER_OBJ;
pace->e_perm = state->owner.allow;
for (i = 0; i < state->users->n; i++) {
pace++;
pace->e_tag = ACL_USER;
pace->e_uid = state->users->aces[i].uid;
pace->e_perm = state->users->aces[i].perms.allow;
}
pace++;
pace->e_tag = ACL_GROUP_OBJ;
pace->e_perm = state->group.allow;
for (i = 0; i < state->groups->n; i++) {
pace++;
pace->e_tag = ACL_GROUP;
pace->e_gid = state->groups->aces[i].gid;
pace->e_perm = state->groups->aces[i].perms.allow;
}
if (state->users->n || state->groups->n) {
pace++;
pace->e_tag = ACL_MASK;
pace->e_perm = state->mask.allow;
}
pace++;
pace->e_tag = ACL_OTHER;
pace->e_perm = state->other.allow;
}
int init_acl_state(struct posix_acl_state *state, int cnt)
{
int alloc;
memset(state, 0, sizeof(struct posix_acl_state));
/*
* In the worst case, each individual acl could be for a distinct
* named user or group, but we don't know which, so we allocate
* enough space for either:
*/
alloc = sizeof(struct posix_ace_state_array)
+ cnt*sizeof(struct posix_user_ace_state);
state->users = kzalloc(alloc, GFP_KERNEL);
if (!state->users)
return -ENOMEM;
state->groups = kzalloc(alloc, GFP_KERNEL);
if (!state->groups) {
kfree(state->users);
return -ENOMEM;
}
return 0;
}
void free_acl_state(struct posix_acl_state *state)
{
kfree(state->users);
kfree(state->groups);
}
static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
struct smb_sid *pownersid, struct smb_sid *pgrpsid,
struct smb_fattr *fattr)
{
int i, ret;
int num_aces = 0;
int acl_size;
char *acl_base;
struct smb_ace **ppace;
struct posix_acl_entry *cf_pace, *cf_pdace;
struct posix_acl_state acl_state, default_acl_state;
umode_t mode = 0, acl_mode;
bool owner_found = false, group_found = false, others_found = false;
if (!pdacl)
return;
/* validate that we do not go past end of acl */
if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
ksmbd_err("ACL too small to parse DACL\n");
return;
}
ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n",
le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
le32_to_cpu(pdacl->num_aces));
acl_base = (char *)pdacl;
acl_size = sizeof(struct smb_acl);
num_aces = le32_to_cpu(pdacl->num_aces);
if (num_aces <= 0)
return;
if (num_aces > ULONG_MAX / sizeof(struct smb_ace *))
return;
ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *),
GFP_KERNEL);
if (!ppace)
return;
ret = init_acl_state(&acl_state, num_aces);
if (ret)
return;
ret = init_acl_state(&default_acl_state, num_aces);
if (ret) {
free_acl_state(&acl_state);
return;
}
/*
* reset rwx permissions for user/group/other.
* Also, if num_aces is 0 i.e. DACL has no ACEs,
* user/group/other have no permissions
*/
for (i = 0; i < num_aces; ++i) {
ppace[i] = (struct smb_ace *) (acl_base + acl_size);
acl_base = (char *)ppace[i];
acl_size = le16_to_cpu(ppace[i]->size);
ppace[i]->access_req =
smb_map_generic_desired_access(ppace[i]->access_req);
if (!(compare_sids(&(ppace[i]->sid), &sid_unix_NFS_mode))) {
fattr->cf_mode =
le32_to_cpu(ppace[i]->sid.sub_auth[2]);
break;
} else if (!compare_sids(&(ppace[i]->sid), pownersid)) {
acl_mode = access_flags_to_mode(fattr,
ppace[i]->access_req, ppace[i]->type);
acl_mode &= 0700;
if (!owner_found) {
mode &= ~(0700);
mode |= acl_mode;
}
owner_found = true;
} else if (!compare_sids(&(ppace[i]->sid), pgrpsid) ||
ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1] ==
DOMAIN_USER_RID_LE) {
acl_mode = access_flags_to_mode(fattr,
ppace[i]->access_req, ppace[i]->type);
acl_mode &= 0070;
if (!group_found) {
mode &= ~(0070);
mode |= acl_mode;
}
group_found = true;
} else if (!compare_sids(&(ppace[i]->sid), &sid_everyone)) {
acl_mode = access_flags_to_mode(fattr,
ppace[i]->access_req, ppace[i]->type);
acl_mode &= 0007;
if (!others_found) {
mode &= ~(0007);
mode |= acl_mode;
}
others_found = true;
} else if (!compare_sids(&(ppace[i]->sid), &creator_owner))
continue;
else if (!compare_sids(&(ppace[i]->sid), &creator_group))
continue;
else if (!compare_sids(&(ppace[i]->sid), &sid_authusers))
continue;
else {
struct smb_fattr temp_fattr;
acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req,
ppace[i]->type);
temp_fattr.cf_uid = INVALID_UID;
ret = sid_to_id(&ppace[i]->sid, SIDOWNER, &temp_fattr);
if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) {
ksmbd_err("%s: Error %d mapping Owner SID to uid\n",
__func__, ret);
continue;
}
acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004;
acl_state.users->aces[acl_state.users->n].uid =
temp_fattr.cf_uid;
acl_state.users->aces[acl_state.users->n++].perms.allow =
((acl_mode & 0700) >> 6) | 0004;
default_acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004;
default_acl_state.users->aces[default_acl_state.users->n].uid =
temp_fattr.cf_uid;
default_acl_state.users->aces[default_acl_state.users->n++].perms.allow =
((acl_mode & 0700) >> 6) | 0004;
}
}
kfree(ppace);
if (owner_found) {
/* The owner must be set to at least read-only. */
acl_state.owner.allow = ((mode & 0700) >> 6) | 0004;
acl_state.users->aces[acl_state.users->n].uid = fattr->cf_uid;
acl_state.users->aces[acl_state.users->n++].perms.allow =
((mode & 0700) >> 6) | 0004;
default_acl_state.owner.allow = ((mode & 0700) >> 6) | 0004;
default_acl_state.users->aces[default_acl_state.users->n].uid =
fattr->cf_uid;
default_acl_state.users->aces[default_acl_state.users->n++].perms.allow =
((mode & 0700) >> 6) | 0004;
}
if (group_found) {
acl_state.group.allow = (mode & 0070) >> 3;
acl_state.groups->aces[acl_state.groups->n].gid =
fattr->cf_gid;
acl_state.groups->aces[acl_state.groups->n++].perms.allow =
(mode & 0070) >> 3;
default_acl_state.group.allow = mode & 0070 >> 3;
default_acl_state.groups->aces[default_acl_state.groups->n].gid =
fattr->cf_gid;
default_acl_state.groups->aces[default_acl_state.groups->n++].perms.allow =
(mode & 0070) >> 3;
}
if (others_found) {
fattr->cf_mode &= ~(0007);
fattr->cf_mode |= mode & 0007;
acl_state.other.allow = mode & 0007;
default_acl_state.other.allow = mode & 0007;
}
if (acl_state.users->n || acl_state.groups->n) {
acl_state.mask.allow = 0x07;
fattr->cf_acls = ksmbd_vfs_posix_acl_alloc(acl_state.users->n +
acl_state.groups->n + 4, GFP_KERNEL);
if (fattr->cf_acls) {
cf_pace = fattr->cf_acls->a_entries;
posix_state_to_acl(&acl_state, cf_pace);
}
}
if (default_acl_state.users->n || default_acl_state.groups->n) {
default_acl_state.mask.allow = 0x07;
fattr->cf_dacls =
ksmbd_vfs_posix_acl_alloc(default_acl_state.users->n +
default_acl_state.groups->n + 4, GFP_KERNEL);
if (fattr->cf_dacls) {
cf_pdace = fattr->cf_dacls->a_entries;
posix_state_to_acl(&default_acl_state, cf_pdace);
}
}
free_acl_state(&acl_state);
free_acl_state(&default_acl_state);
}
static void set_posix_acl_entries_dacl(struct smb_ace *pndace,
struct smb_fattr *fattr, u32 *num_aces, u16 *size, u32 nt_aces_num)
{
struct posix_acl_entry *pace;
struct smb_sid *sid;
struct smb_ace *ntace;
int i, j;
if (!fattr->cf_acls)
goto posix_default_acl;
pace = fattr->cf_acls->a_entries;
for (i = 0; i < fattr->cf_acls->a_count; i++, pace++) {
int flags = 0;
sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL);
if (!sid)
break;
if (pace->e_tag == ACL_USER) {
uid_t uid;
unsigned int sid_type = SIDOWNER;
uid = from_kuid(&init_user_ns, pace->e_uid);
if (!uid)
sid_type = SIDUNIX_USER;
id_to_sid(uid, sid_type, sid);
} else if (pace->e_tag == ACL_GROUP) {
gid_t gid;
gid = from_kgid(&init_user_ns, pace->e_gid);
id_to_sid(gid, SIDUNIX_GROUP, sid);
} else if (pace->e_tag == ACL_OTHER && !nt_aces_num) {
smb_copy_sid(sid, &sid_everyone);
} else {
kfree(sid);
continue;
}
ntace = pndace;
for (j = 0; j < nt_aces_num; j++) {
if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] ==
sid->sub_auth[sid->num_subauth - 1])
goto pass_same_sid;
ntace = (struct smb_ace *)((char *)ntace +
le16_to_cpu(ntace->size));
}
if (S_ISDIR(fattr->cf_mode) && pace->e_tag == ACL_OTHER)
flags = 0x03;
ntace = (struct smb_ace *) ((char *)pndace + *size);
*size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags,
pace->e_perm, 0777);
(*num_aces)++;
if (pace->e_tag == ACL_USER)
ntace->access_req |=
FILE_DELETE_LE | FILE_DELETE_CHILD_LE;
if (S_ISDIR(fattr->cf_mode) &&
(pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) {
ntace = (struct smb_ace *) ((char *)pndace + *size);
*size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED,
0x03, pace->e_perm, 0777);
(*num_aces)++;
if (pace->e_tag == ACL_USER)
ntace->access_req |=
FILE_DELETE_LE | FILE_DELETE_CHILD_LE;
}
pass_same_sid:
kfree(sid);
}
if (nt_aces_num)
return;
posix_default_acl:
if (!fattr->cf_dacls)
return;
pace = fattr->cf_dacls->a_entries;
for (i = 0; i < fattr->cf_dacls->a_count; i++, pace++) {
sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL);
if (!sid)
break;
if (pace->e_tag == ACL_USER) {
uid_t uid;
uid = from_kuid(&init_user_ns, pace->e_uid);
id_to_sid(uid, SIDCREATOR_OWNER, sid);
} else if (pace->e_tag == ACL_GROUP) {
gid_t gid;
gid = from_kgid(&init_user_ns, pace->e_gid);
id_to_sid(gid, SIDCREATOR_GROUP, sid);
} else {
kfree(sid);
continue;
}
ntace = (struct smb_ace *) ((char *)pndace + *size);
*size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b,
pace->e_perm, 0777);
(*num_aces)++;
if (pace->e_tag == ACL_USER)
ntace->access_req |=
FILE_DELETE_LE | FILE_DELETE_CHILD_LE;
kfree(sid);
}
}
static void set_ntacl_dacl(struct smb_acl *pndacl, struct smb_acl *nt_dacl,
const struct smb_sid *pownersid, const struct smb_sid *pgrpsid,
struct smb_fattr *fattr)
{
struct smb_ace *ntace, *pndace;
int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0;
unsigned short size = 0;
int i;
pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl));
if (nt_num_aces) {
ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl));
for (i = 0; i < nt_num_aces; i++) {
memcpy((char *)pndace + size, ntace, le16_to_cpu(ntace->size));
size += le16_to_cpu(ntace->size);
ntace = (struct smb_ace *)((char *)ntace + le16_to_cpu(ntace->size));
num_aces++;
}
}
set_posix_acl_entries_dacl(pndace, fattr, &num_aces, &size, nt_num_aces);
pndacl->num_aces = cpu_to_le32(num_aces);
pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size);
}
static void set_mode_dacl(struct smb_acl *pndacl, struct smb_fattr *fattr)
{
struct smb_ace *pace, *pndace;
u32 num_aces = 0;
u16 size = 0, ace_size = 0;
uid_t uid;
const struct smb_sid *sid;
pace = pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl));
if (fattr->cf_acls) {
set_posix_acl_entries_dacl(pndace, fattr, &num_aces, &size, num_aces);
goto out;
}
/* owner RID */
uid = from_kuid(&init_user_ns, fattr->cf_uid);
if (uid)
sid = &server_conf.domain_sid;
else
sid = &sid_unix_users;
ace_size = fill_ace_for_sid(pace, sid, ACCESS_ALLOWED, 0,
fattr->cf_mode, 0700);
pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(uid);
pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE;
pace->size = cpu_to_le16(ace_size + 4);
size += le16_to_cpu(pace->size);
pace = (struct smb_ace *)((char *)pndace + size);
/* Group RID */
ace_size = fill_ace_for_sid(pace, &sid_unix_groups,
ACCESS_ALLOWED, 0, fattr->cf_mode, 0070);
pace->sid.sub_auth[pace->sid.num_subauth++] =
cpu_to_le32(from_kgid(&init_user_ns, fattr->cf_gid));
pace->size = cpu_to_le16(ace_size + 4);
size += le16_to_cpu(pace->size);
pace = (struct smb_ace *)((char *)pndace + size);
num_aces = 3;
if (S_ISDIR(fattr->cf_mode)) {
pace = (struct smb_ace *)((char *)pndace + size);
/* creator owner */
size += fill_ace_for_sid(pace, &creator_owner, ACCESS_ALLOWED,
0x0b, fattr->cf_mode, 0700);
pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE;
pace = (struct smb_ace *)((char *)pndace + size);
/* creator group */
size += fill_ace_for_sid(pace, &creator_group, ACCESS_ALLOWED,
0x0b, fattr->cf_mode, 0070);
pace = (struct smb_ace *)((char *)pndace + size);
num_aces = 5;
}
/* other */
size += fill_ace_for_sid(pace, &sid_everyone, ACCESS_ALLOWED, 0,
fattr->cf_mode, 0007);
out:
pndacl->num_aces = cpu_to_le32(num_aces);
pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size);
}
static int parse_sid(struct smb_sid *psid, char *end_of_acl)
{
/*
* validate that we do not go past end of ACL - sid must be at least 8
* bytes long (assuming no sub-auths - e.g. the null SID
*/
if (end_of_acl < (char *)psid + 8) {
ksmbd_err("ACL too small to parse SID %p\n", psid);
return -EINVAL;
}
return 0;
}
/* Convert CIFS ACL to POSIX form */
int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len,
struct smb_fattr *fattr)
{
int rc = 0;
struct smb_sid *owner_sid_ptr, *group_sid_ptr;
struct smb_acl *dacl_ptr; /* no need for SACL ptr */
char *end_of_acl = ((char *)pntsd) + acl_len;
__u32 dacloffset;
int total_ace_size = 0, pntsd_type;
if (pntsd == NULL)
return -EIO;
owner_sid_ptr = (struct smb_sid *)((char *)pntsd +
le32_to_cpu(pntsd->osidoffset));
group_sid_ptr = (struct smb_sid *)((char *)pntsd +
le32_to_cpu(pntsd->gsidoffset));
dacloffset = le32_to_cpu(pntsd->dacloffset);
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
ksmbd_debug(SMB,
"revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n",
pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
le32_to_cpu(pntsd->gsidoffset),
le32_to_cpu(pntsd->sacloffset), dacloffset);
if (dacloffset && dacl_ptr)
total_ace_size =
le16_to_cpu(dacl_ptr->size) - sizeof(struct smb_acl);
pntsd_type = le16_to_cpu(pntsd->type);
if (!(pntsd_type & DACL_PRESENT)) {
ksmbd_debug(SMB, "DACL_PRESENT in DACL type is not set\n");
return rc;
}
pntsd->type = cpu_to_le16(DACL_PRESENT);
if (pntsd->osidoffset) {
rc = parse_sid(owner_sid_ptr, end_of_acl);
if (rc) {
ksmbd_err("%s: Error %d parsing Owner SID\n", __func__, rc);
return rc;
}
rc = sid_to_id(owner_sid_ptr, SIDOWNER, fattr);
if (rc) {
ksmbd_err("%s: Error %d mapping Owner SID to uid\n",
__func__, rc);
owner_sid_ptr = NULL;
}
}
if (pntsd->gsidoffset) {
rc = parse_sid(group_sid_ptr, end_of_acl);
if (rc) {
ksmbd_err("%s: Error %d mapping Owner SID to gid\n",
__func__, rc);
return rc;
}
rc = sid_to_id(group_sid_ptr, SIDUNIX_GROUP, fattr);
if (rc) {
ksmbd_err("%s: Error %d mapping Group SID to gid\n",
__func__, rc);
group_sid_ptr = NULL;
}
}
if ((pntsd_type &
(DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) ==
(DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ))
pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED);
if (pntsd_type & DACL_PROTECTED)
pntsd->type |= cpu_to_le16(DACL_PROTECTED);
if (dacloffset) {
parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr,
fattr);
}
return 0;
}
/* Convert permission bits from mode to equivalent CIFS ACL */
int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd,
int addition_info, __u32 *secdesclen, struct smb_fattr *fattr)
{
int rc = 0;
__u32 offset;
struct smb_sid *owner_sid_ptr, *group_sid_ptr;
struct smb_sid *nowner_sid_ptr, *ngroup_sid_ptr;
struct smb_acl *dacl_ptr = NULL; /* no need for SACL ptr */
uid_t uid;
gid_t gid;
unsigned int sid_type = SIDOWNER;
nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL);
if (!nowner_sid_ptr)
return -ENOMEM;
uid = from_kuid(&init_user_ns, fattr->cf_uid);
if (!uid)
sid_type = SIDUNIX_USER;
id_to_sid(uid, sid_type, nowner_sid_ptr);
ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL);
if (!ngroup_sid_ptr) {
kfree(nowner_sid_ptr);
return -ENOMEM;
}
gid = from_kgid(&init_user_ns, fattr->cf_gid);
id_to_sid(gid, SIDUNIX_GROUP, ngroup_sid_ptr);
offset = sizeof(struct smb_ntsd);
pntsd->sacloffset = 0;
pntsd->revision = cpu_to_le16(1);
pntsd->type = cpu_to_le16(SELF_RELATIVE);
if (ppntsd)
pntsd->type |= ppntsd->type;
if (addition_info & OWNER_SECINFO) {
pntsd->osidoffset = cpu_to_le32(offset);
owner_sid_ptr = (struct smb_sid *)((char *)pntsd + offset);
smb_copy_sid(owner_sid_ptr, nowner_sid_ptr);
offset += 1 + 1 + 6 + (nowner_sid_ptr->num_subauth * 4);
}
if (addition_info & GROUP_SECINFO) {
pntsd->gsidoffset = cpu_to_le32(offset);
group_sid_ptr = (struct smb_sid *)((char *)pntsd + offset);
smb_copy_sid(group_sid_ptr, ngroup_sid_ptr);
offset += 1 + 1 + 6 + (ngroup_sid_ptr->num_subauth * 4);
}
if (addition_info & DACL_SECINFO) {
pntsd->type |= cpu_to_le16(DACL_PRESENT);
dacl_ptr = (struct smb_acl *)((char *)pntsd + offset);
dacl_ptr->revision = cpu_to_le16(2);
dacl_ptr->size = cpu_to_le16(sizeof(struct smb_acl));
dacl_ptr->num_aces = 0;
if (!ppntsd)
set_mode_dacl(dacl_ptr, fattr);
else if (!ppntsd->dacloffset)
goto out;
else {
struct smb_acl *ppdacl_ptr;
ppdacl_ptr = (struct smb_acl *)((char *)ppntsd +
le32_to_cpu(ppntsd->dacloffset));
set_ntacl_dacl(dacl_ptr, ppdacl_ptr, nowner_sid_ptr,
ngroup_sid_ptr, fattr);
}
pntsd->dacloffset = cpu_to_le32(offset);
offset += le16_to_cpu(dacl_ptr->size);
}
out:
kfree(nowner_sid_ptr);
kfree(ngroup_sid_ptr);
*secdesclen = offset;
return rc;
}
static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type,
u8 flags, __le32 access_req)
{
ace->type = type;
ace->flags = flags;
ace->access_req = access_req;
smb_copy_sid(&ace->sid, sid);
ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4));
}
int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry,
unsigned int uid, unsigned int gid)
{
const struct smb_sid *psid, *creator = NULL;
struct smb_ace *parent_aces, *aces;
struct smb_acl *parent_pdacl;
struct smb_ntsd *parent_pntsd = NULL;
struct smb_sid owner_sid, group_sid;
struct dentry *parent = dentry->d_parent;
int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0;
int rc = -ENOENT, num_aces, dacloffset, pntsd_type, acl_len;
char *aces_base;
bool is_dir = S_ISDIR(dentry->d_inode->i_mode);
acl_len = ksmbd_vfs_get_sd_xattr(conn, parent, &parent_pntsd);
if (acl_len <= 0)
return rc;
dacloffset = le32_to_cpu(parent_pntsd->dacloffset);
if (!dacloffset)
goto out;
parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset);
num_aces = le32_to_cpu(parent_pdacl->num_aces);
pntsd_type = le16_to_cpu(parent_pntsd->type);
aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL);
if (!aces_base)
goto out;
aces = (struct smb_ace *)aces_base;
parent_aces = (struct smb_ace *)((char *)parent_pdacl +
sizeof(struct smb_acl));
if (pntsd_type & DACL_AUTO_INHERITED)
inherited_flags = INHERITED_ACE;
for (i = 0; i < num_aces; i++) {
flags = parent_aces->flags;
if (!smb_inherit_flags(flags, is_dir))
goto pass;
if (is_dir) {
flags &= ~(INHERIT_ONLY_ACE | INHERITED_ACE);
if (!(flags & CONTAINER_INHERIT_ACE))
flags |= INHERIT_ONLY_ACE;
if (flags & NO_PROPAGATE_INHERIT_ACE)
flags = 0;
} else
flags = 0;
if (!compare_sids(&creator_owner, &parent_aces->sid)) {
creator = &creator_owner;
id_to_sid(uid, SIDOWNER, &owner_sid);
psid = &owner_sid;
} else if (!compare_sids(&creator_group, &parent_aces->sid)) {
creator = &creator_group;
id_to_sid(gid, SIDUNIX_GROUP, &group_sid);
psid = &group_sid;
} else {
creator = NULL;
psid = &parent_aces->sid;
}
if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) {
smb_set_ace(aces, psid, parent_aces->type, inherited_flags,
parent_aces->access_req);
nt_size += le16_to_cpu(aces->size);
ace_cnt++;
aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size));
flags |= INHERIT_ONLY_ACE;
psid = creator;
} else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE))
psid = &parent_aces->sid;
smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags,
parent_aces->access_req);
nt_size += le16_to_cpu(aces->size);
aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size));
ace_cnt++;
pass:
parent_aces =
(struct smb_ace *)((char *)parent_aces + le16_to_cpu(parent_aces->size));
}
if (nt_size > 0) {
struct smb_ntsd *pntsd;
struct smb_acl *pdacl;
struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL;
int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size;
if (parent_pntsd->osidoffset) {
powner_sid = (struct smb_sid *)((char *)parent_pntsd +
le32_to_cpu(parent_pntsd->osidoffset));
powner_sid_size = 1 + 1 + 6 + (powner_sid->num_subauth * 4);
}
if (parent_pntsd->gsidoffset) {
pgroup_sid = (struct smb_sid *)((char *)parent_pntsd +
le32_to_cpu(parent_pntsd->gsidoffset));
pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4);
}
pntsd = kzalloc(sizeof(struct smb_ntsd) + powner_sid_size +
pgroup_sid_size + sizeof(struct smb_acl) +
nt_size, GFP_KERNEL);
if (!pntsd) {
rc = -ENOMEM;
goto out;
}
pntsd->revision = cpu_to_le16(1);
pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PRESENT);
if (le16_to_cpu(parent_pntsd->type) & DACL_AUTO_INHERITED)
pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED);
pntsd_size = sizeof(struct smb_ntsd);
pntsd->osidoffset = parent_pntsd->osidoffset;
pntsd->gsidoffset = parent_pntsd->gsidoffset;
pntsd->dacloffset = parent_pntsd->dacloffset;
if (pntsd->osidoffset) {
struct smb_sid *owner_sid = (struct smb_sid *)((char *)pntsd +
le32_to_cpu(pntsd->osidoffset));
memcpy(owner_sid, powner_sid, powner_sid_size);
pntsd_size += powner_sid_size;
}
if (pntsd->gsidoffset) {
struct smb_sid *group_sid = (struct smb_sid *)((char *)pntsd +
le32_to_cpu(pntsd->gsidoffset));
memcpy(group_sid, pgroup_sid, pgroup_sid_size);
pntsd_size += pgroup_sid_size;
}
if (pntsd->dacloffset) {
struct smb_ace *pace;
pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));
pdacl->revision = cpu_to_le16(2);
pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size);
pdacl->num_aces = cpu_to_le32(ace_cnt);
pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
memcpy(pace, aces_base, nt_size);
pntsd_size += sizeof(struct smb_acl) + nt_size;
}
ksmbd_vfs_set_sd_xattr(conn, dentry, pntsd, pntsd_size);
kfree(pntsd);
rc = 0;
}
kfree(aces_base);
out:
return rc;
}
bool smb_inherit_flags(int flags, bool is_dir)
{
if (!is_dir)
return (flags & OBJECT_INHERIT_ACE) != 0;
if (flags & OBJECT_INHERIT_ACE && !(flags & NO_PROPAGATE_INHERIT_ACE))
return true;
if (flags & CONTAINER_INHERIT_ACE)
return true;
return false;
}
int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry,
__le32 *pdaccess, int uid)
{
struct smb_ntsd *pntsd = NULL;
struct smb_acl *pdacl;
struct posix_acl *posix_acls;
int rc = 0, acl_size;
struct smb_sid sid;
int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE);
struct smb_ace *ace;
int i, found = 0;
unsigned int access_bits = 0;
struct smb_ace *others_ace = NULL;
struct posix_acl_entry *pa_entry;
unsigned int sid_type = SIDOWNER;
ksmbd_debug(SMB, "check permission using windows acl\n");
acl_size = ksmbd_vfs_get_sd_xattr(conn, dentry, &pntsd);
if (acl_size <= 0 || (pntsd && !pntsd->dacloffset))
return 0;
pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));
if (!pdacl->num_aces) {
if (!(le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) &&
*pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) {
rc = -EACCES;
goto err_out;
}
kfree(pntsd);
return 0;
}
if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) {
granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES |
DELETE;
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) {
granted |= le32_to_cpu(ace->access_req);
ace = (struct smb_ace *) ((char *)ace + le16_to_cpu(ace->size));
}
if (!pdacl->num_aces)
granted = GENERIC_ALL_FLAGS;
}
if (!uid)
sid_type = SIDUNIX_USER;
id_to_sid(uid, sid_type, &sid);
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) {
if (!compare_sids(&sid, &ace->sid) ||
!compare_sids(&sid_unix_NFS_mode, &ace->sid)) {
found = 1;
break;
}
if (!compare_sids(&sid_everyone, &ace->sid))
others_ace = ace;
ace = (struct smb_ace *) ((char *)ace + le16_to_cpu(ace->size));
}
if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) {
granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES |
DELETE;
granted |= le32_to_cpu(ace->access_req);
if (!pdacl->num_aces)
granted = GENERIC_ALL_FLAGS;
}
posix_acls = ksmbd_vfs_get_acl(dentry->d_inode, ACL_TYPE_ACCESS);
if (posix_acls && !found) {
unsigned int id = -1;
pa_entry = posix_acls->a_entries;
for (i = 0; i < posix_acls->a_count; i++, pa_entry++) {
if (pa_entry->e_tag == ACL_USER)
id = from_kuid(&init_user_ns, pa_entry->e_uid);
else if (pa_entry->e_tag == ACL_GROUP)
id = from_kgid(&init_user_ns, pa_entry->e_gid);
else
continue;
if (id == uid) {
mode_to_access_flags(pa_entry->e_perm, 0777, &access_bits);
if (!access_bits)
access_bits = SET_MINIMUM_RIGHTS;
goto check_access_bits;
}
}
}
if (posix_acls)
posix_acl_release(posix_acls);
if (!found) {
if (others_ace)
ace = others_ace;
else {
ksmbd_debug(SMB, "Can't find corresponding sid\n");
rc = -EACCES;
goto err_out;
}
}
switch (ace->type) {
case ACCESS_ALLOWED_ACE_TYPE:
access_bits = le32_to_cpu(ace->access_req);
break;
case ACCESS_DENIED_ACE_TYPE:
case ACCESS_DENIED_CALLBACK_ACE_TYPE:
access_bits = le32_to_cpu(~ace->access_req);
break;
}
check_access_bits:
if (granted & ~(access_bits | FILE_READ_ATTRIBUTES |
READ_CONTROL | WRITE_DAC | DELETE)) {
ksmbd_debug(SMB, "Access denied with winACL, granted : %x, access_req : %x\n",
granted, le32_to_cpu(ace->access_req));
rc = -EACCES;
goto err_out;
}
*pdaccess = cpu_to_le32(granted);
err_out:
kfree(pntsd);
return rc;
}
int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
struct dentry *dentry, struct smb_ntsd *pntsd, int ntsd_len,
bool type_check)
{
int rc;
struct smb_fattr fattr = {{0}};
struct inode *inode = dentry->d_inode;
fattr.cf_uid = INVALID_UID;
fattr.cf_gid = INVALID_GID;
fattr.cf_mode = inode->i_mode;
rc = parse_sec_desc(pntsd, ntsd_len, &fattr);
if (rc)
goto out;
inode->i_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777);
if (!uid_eq(fattr.cf_uid, INVALID_UID))
inode->i_uid = fattr.cf_uid;
if (!gid_eq(fattr.cf_gid, INVALID_GID))
inode->i_gid = fattr.cf_gid;
mark_inode_dirty(inode);
ksmbd_vfs_remove_acl_xattrs(dentry);
/* Update posix acls */
if (fattr.cf_dacls) {
rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS,
fattr.cf_acls);
if (S_ISDIR(inode->i_mode) && fattr.cf_dacls)
rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT,
fattr.cf_dacls);
}
/* Check it only calling from SD BUFFER context */
if (type_check && !(le16_to_cpu(pntsd->type) & DACL_PRESENT))
goto out;
if (test_share_config_flag(tcon->share_conf,
KSMBD_SHARE_FLAG_ACL_XATTR)) {
/* Update WinACL in xattr */
ksmbd_vfs_remove_sd_xattrs(dentry);
ksmbd_vfs_set_sd_xattr(conn, dentry, pntsd, ntsd_len);
}
out:
posix_acl_release(fattr.cf_acls);
posix_acl_release(fattr.cf_dacls);
mark_inode_dirty(inode);
return rc;
}
void ksmbd_init_domain(u32 *sub_auth)
{
int i;
memcpy(&server_conf.domain_sid, &domain, sizeof(struct smb_sid));
for (i = 0; i < 3; ++i)
server_conf.domain_sid.sub_auth[i + 1] = cpu_to_le32(sub_auth[i]);
}
/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* Copyright (c) International Business Machines Corp., 2007
* Author(s): Steve French (sfrench@us.ibm.com)
* Modified by Namjae Jeon (linkinjeon@kernel.org)
*/
#ifndef _SMBACL_H
#define _SMBACL_H
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/posix_acl.h>
#include "mgmt/tree_connect.h"
#define NUM_AUTHS (6) /* number of authority fields */
#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */
#define ACCESS_ALLOWED 0
#define ACCESS_DENIED 1
#define SIDOWNER 1
#define SIDGROUP 2
#define SIDCREATOR_OWNER 3
#define SIDCREATOR_GROUP 4
#define SIDUNIX_USER 5
#define SIDUNIX_GROUP 6
#define SIDNFS_USER 7
#define SIDNFS_GROUP 8
#define SIDNFS_MODE 9
/* Revision for ACLs */
#define SD_REVISION 1
/* Control flags for Security Descriptor */
#define OWNER_DEFAULTED 0x0001
#define GROUP_DEFAULTED 0x0002
#define DACL_PRESENT 0x0004
#define DACL_DEFAULTED 0x0008
#define SACL_PRESENT 0x0010
#define SACL_DEFAULTED 0x0020
#define DACL_TRUSTED 0x0040
#define SERVER_SECURITY 0x0080
#define DACL_AUTO_INHERIT_REQ 0x0100
#define SACL_AUTO_INHERIT_REQ 0x0200
#define DACL_AUTO_INHERITED 0x0400
#define SACL_AUTO_INHERITED 0x0800
#define DACL_PROTECTED 0x1000
#define SACL_PROTECTED 0x2000
#define RM_CONTROL_VALID 0x4000
#define SELF_RELATIVE 0x8000
/* ACE types - see MS-DTYP 2.4.4.1 */
#define ACCESS_ALLOWED_ACE_TYPE 0x00
#define ACCESS_DENIED_ACE_TYPE 0x01
#define SYSTEM_AUDIT_ACE_TYPE 0x02
#define SYSTEM_ALARM_ACE_TYPE 0x03
#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04
#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05
#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06
#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07
#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08
#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09
#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A
#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B
#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C
#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D
#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */
#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F
#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */
#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11
#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12
#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13
/* ACE flags */
#define OBJECT_INHERIT_ACE 0x01
#define CONTAINER_INHERIT_ACE 0x02
#define NO_PROPAGATE_INHERIT_ACE 0x04
#define INHERIT_ONLY_ACE 0x08
#define INHERITED_ACE 0x10
#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40
#define FAILED_ACCESS_ACE_FLAG 0x80
/*
* Maximum size of a string representation of a SID:
*
* The fields are unsigned values in decimal. So:
*
* u8: max 3 bytes in decimal
* u32: max 10 bytes in decimal
*
* "S-" + 3 bytes for version field + 15 for authority field + NULL terminator
*
* For authority field, max is when all 6 values are non-zero and it must be
* represented in hex. So "-0x" + 12 hex digits.
*
* Add 11 bytes for each subauthority field (10 bytes each + 1 for '-')
*/
#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1)
#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */
#define DOMAIN_USER_RID_LE cpu_to_le32(513)
struct ksmbd_conn;
struct smb_ntsd {
__le16 revision; /* revision level */
__le16 type;
__le32 osidoffset;
__le32 gsidoffset;
__le32 sacloffset;
__le32 dacloffset;
} __packed;
struct smb_sid {
__u8 revision; /* revision level */
__u8 num_subauth;
__u8 authority[NUM_AUTHS];
__le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */
} __packed;
/* size of a struct cifs_sid, sans sub_auth array */
#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS)
struct smb_acl {
__le16 revision; /* revision level */
__le16 size;
__le32 num_aces;
} __packed;
struct smb_ace {
__u8 type;
__u8 flags;
__le16 size;
__le32 access_req;
struct smb_sid sid; /* ie UUID of user or group who gets these perms */
} __packed;
struct smb_fattr {
kuid_t cf_uid;
kgid_t cf_gid;
umode_t cf_mode;
__le32 daccess;
struct posix_acl *cf_acls;
struct posix_acl *cf_dacls;
};
struct posix_ace_state {
u32 allow;
u32 deny;
};
struct posix_user_ace_state {
union {
kuid_t uid;
kgid_t gid;
};
struct posix_ace_state perms;
};
struct posix_ace_state_array {
int n;
struct posix_user_ace_state aces[];
};
/*
* while processing the nfsv4 ace, this maintains the partial permissions
* calculated so far:
*/
struct posix_acl_state {
struct posix_ace_state owner;
struct posix_ace_state group;
struct posix_ace_state other;
struct posix_ace_state everyone;
struct posix_ace_state mask; /* deny unused in this case */
struct posix_ace_state_array *users;
struct posix_ace_state_array *groups;
};
int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len,
struct smb_fattr *fattr);
int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd,
int addition_info, __u32 *secdesclen, struct smb_fattr *fattr);
int init_acl_state(struct posix_acl_state *state, int cnt);
void free_acl_state(struct posix_acl_state *state);
void posix_state_to_acl(struct posix_acl_state *state,
struct posix_acl_entry *pace);
int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid);
bool smb_inherit_flags(int flags, bool is_dir);
int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry,
unsigned int uid, unsigned int gid);
int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry,
__le32 *pdaccess, int uid);
int store_init_posix_acl(struct inode *inode, umode_t perm);
int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
struct dentry *dentry, struct smb_ntsd *pntsd, int ntsd_len,
bool type_check);
void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid);
void ksmbd_init_domain(u32 *sub_auth);
#endif /* _SMBACL_H */
/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* Copyright (c) International Business Machines Corp., 2002,2004
* Author(s): Steve French (sfrench@us.ibm.com)
*
* See Error Codes section of the SNIA CIFS Specification
* for more information
*/
#ifndef __KSMBD_SMBERR_H
#define __KSMBD_SMBERR_H
#define SUCCESS 0x00 /* The request was successful. */
#define ERRDOS 0x01 /* Error is from the core DOS operating system set */
#define ERRSRV 0x02 /* Error is generated by the file server daemon */
#define ERRHRD 0x03 /* Error is a hardware error. */
#define ERRCMD 0xFF /* Command was not in the "SMB" format. */
/* The following error codes may be generated with the SUCCESS error class.*/
/*#define SUCCESS 0 The request was successful. */
/* The following error codes may be generated with the ERRDOS error class.*/
#define ERRbadfunc 1 /*
* Invalid function. The server did not
* recognize or could not perform a
* system call generated by the server,
* e.g., set the DIRECTORY attribute on
* a data file, invalid seek mode.
*/
#define ERRbadfile 2 /*
* File not found. The last component
* of a file's pathname could not be
* found.
*/
#define ERRbadpath 3 /*
* Directory invalid. A directory
* component in a pathname could not be
* found.
*/
#define ERRnofids 4 /*
* Too many open files. The server has
* no file handles available.
*/
#define ERRnoaccess 5 /*
* Access denied, the client's context
* does not permit the requested
* function. This includes the
* following conditions: invalid rename
* command, write to Fid open for read
* only, read on Fid open for write
* only, attempt to delete a non-empty
* directory
*/
#define ERRbadfid 6 /*
* Invalid file handle. The file handle
* specified was not recognized by the
* server.
*/
#define ERRbadmcb 7 /* Memory control blocks destroyed. */
#define ERRnomem 8 /*
* Insufficient server memory to
* perform the requested function.
*/
#define ERRbadmem 9 /* Invalid memory block address. */
#define ERRbadenv 10 /* Invalid environment. */
#define ERRbadformat 11 /* Invalid format. */
#define ERRbadaccess 12 /* Invalid open mode. */
#define ERRbaddata 13 /*
* Invalid data (generated only by
* IOCTL calls within the server).
*/
#define ERRbaddrive 15 /* Invalid drive specified. */
#define ERRremcd 16 /*
* A Delete Directory request attempted
* to remove the server's current
* directory.
*/
#define ERRdiffdevice 17 /*
* Not same device (e.g., a cross
* volume rename was attempted
*/
#define ERRnofiles 18 /*
* A File Search command can find no
* more files matching the specified
* criteria.
*/
#define ERRwriteprot 19 /* media is write protected */
#define ERRgeneral 31
#define ERRbadshare 32 /*
* The sharing mode specified for an
* Open conflicts with existing FIDs on
* the file.
*/
#define ERRlock 33 /*
* A Lock request conflicted with an
* existing lock or specified an
* invalid mode, or an Unlock requested
* attempted to remove a lock held by
* another process.
*/
#define ERRunsup 50
#define ERRnosuchshare 67
#define ERRfilexists 80 /*
* The file named in the request
* already exists.
*/
#define ERRinvparm 87
#define ERRdiskfull 112
#define ERRinvname 123
#define ERRinvlevel 124
#define ERRdirnotempty 145
#define ERRnotlocked 158
#define ERRcancelviolation 173
#define ERRnoatomiclocks 174
#define ERRalreadyexists 183
#define ERRbadpipe 230
#define ERRpipebusy 231
#define ERRpipeclosing 232
#define ERRnotconnected 233
#define ERRmoredata 234
#define ERReasnotsupported 282
#define ErrQuota 0x200 /*
* The operation would cause a quota
* limit to be exceeded.
*/
#define ErrNotALink 0x201 /*
* A link operation was performed on a
* pathname that was not a link.
*/
/*
* Below errors are used internally (do not come over the wire) for passthrough
* from STATUS codes to POSIX only
*/
#define ERRsymlink 0xFFFD
#define ErrTooManyLinks 0xFFFE
/* Following error codes may be generated with the ERRSRV error class.*/
#define ERRerror 1 /*
* Non-specific error code. It is
* returned under the following
* conditions: resource other than disk
* space exhausted (e.g. TIDs), first
* SMB command was not negotiate,
* multiple negotiates attempted, and
* internal server error.
*/
#define ERRbadpw 2 /*
* Bad password - name/password pair in
* a TreeConnect or Session Setup are
* invalid.
*/
#define ERRbadtype 3 /*
* used for indicating DFS referral
* needed
*/
#define ERRaccess 4 /*
* The client does not have the
* necessary access rights within the
* specified context for requested
* function.
*/
#define ERRinvtid 5 /*
* The Tid specified in a command was
* invalid.
*/
#define ERRinvnetname 6 /*
* Invalid network name in tree
* connect.
*/
#define ERRinvdevice 7 /*
* Invalid device - printer request
* made to non-printer connection or
* non-printer request made to printer
* connection.
*/
#define ERRqfull 49 /*
* Print queue full (files) -- returned
* by open print file.
*/
#define ERRqtoobig 50 /* Print queue full -- no space. */
#define ERRqeof 51 /* EOF on print queue dump */
#define ERRinvpfid 52 /* Invalid print file FID. */
#define ERRsmbcmd 64 /*
* The server did not recognize the
* command received.
*/
#define ERRsrverror 65 /*
* The server encountered an internal
* error, e.g., system file
* unavailable.
*/
#define ERRbadBID 66 /* (obsolete) */
#define ERRfilespecs 67 /*
* The Fid and pathname parameters
* contained an invalid combination of
* values.
*/
#define ERRbadLink 68 /* (obsolete) */
#define ERRbadpermits 69 /*
* The access permissions specified for
* a file or directory are not a valid
* combination.
*/
#define ERRbadPID 70
#define ERRsetattrmode 71 /* attribute (mode) is invalid */
#define ERRpaused 81 /* Server is paused */
#define ERRmsgoff 82 /* reserved - messaging off */
#define ERRnoroom 83 /* reserved - no room for message */
#define ERRrmuns 87 /* reserved - too many remote names */
#define ERRtimeout 88 /* operation timed out */
#define ERRnoresource 89 /* No resources available for request */
#define ERRtoomanyuids 90 /*
* Too many UIDs active on this session
*/
#define ERRbaduid 91 /*
* The UID is not known as a valid user
*/
#define ERRusempx 250 /* temporarily unable to use raw */
#define ERRusestd 251 /*
* temporarily unable to use either raw
* or mpx
*/
#define ERR_NOTIFY_ENUM_DIR 1024
#define ERRnoSuchUser 2238 /* user account does not exist */
#define ERRaccountexpired 2239
#define ERRbadclient 2240 /* can not logon from this client */
#define ERRbadLogonTime 2241 /* logon hours do not allow this */
#define ERRpasswordExpired 2242
#define ERRnetlogonNotStarted 2455
#define ERRnosupport 0xFFFF
#endif /* __KSMBD_SMBERR_H */
/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* fs/cifs/smbfsctl.h: SMB, CIFS, SMB2 FSCTL definitions
*
* Copyright (c) International Business Machines Corp., 2002,2009
* Author(s): Steve French (sfrench@us.ibm.com)
*/
/* IOCTL information */
/*
* List of ioctl/fsctl function codes that are or could be useful in the
* future to remote clients like cifs or SMB2 client. There is probably
* a slightly larger set of fsctls that NTFS local filesystem could handle,
* including the seven below that we do not have struct definitions for.
* Even with protocol definitions for most of these now available, we still
* need to do some experimentation to identify which are practical to do
* remotely. Some of the following, such as the encryption/compression ones
* could be invoked from tools via a specialized hook into the VFS rather
* than via the standard vfs entry points
*/
#ifndef __KSMBD_SMBFSCTL_H
#define __KSMBD_SMBFSCTL_H
#define FSCTL_DFS_GET_REFERRALS 0x00060194
#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0
#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000
#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004
#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008
#define FSCTL_LOCK_VOLUME 0x00090018
#define FSCTL_UNLOCK_VOLUME 0x0009001C
#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */
#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */
#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */
#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */
/* Verify the next FSCTL number, we had it as 0x00090090 before */
#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */
#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */
#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */
#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */
#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */
#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C
#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */
#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */
#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */
#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */
#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */
#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */
#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */
#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */
#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */
#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */
#define FSCTL_SET_ZERO_DATA 0x000980C8 /* BB add struct */
#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */
#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */
#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */
#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */
#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */
#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */
#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */
#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */
#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */
#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */
#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */
#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */
#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */
#define FSCTL_SIS_LINK_FILES 0x0009C104
#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */
#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */
/* strange that the number for this op is not sequential with previous op */
#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */
#define FSCTL_REQUEST_RESUME_KEY 0x00140078
#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */
#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */
#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204
#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC
#define FSCTL_COPYCHUNK 0x001440F2
#define FSCTL_COPYCHUNK_WRITE 0x001480F2
#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003
#define IO_REPARSE_TAG_HSM 0xC0000004
#define IO_REPARSE_TAG_SIS 0x80000007
/* WSL reparse tags */
#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D)
#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023)
#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024)
#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025)
#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026)
#endif /* __KSMBD_SMBFSCTL_H */
This source diff could not be displayed because it is too large. You can view the blob instead.
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
*/
#ifndef __KSMBD_TIME_WRAPPERS_H
#define __KSMBD_TIME_WRAPPERS_H
/*
* A bunch of ugly hacks to workaoround all the API differences
* between different kernel versions.
*/
#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000)
/* Convert the Unix UTC into NT UTC. */
static inline u64 ksmbd_UnixTimeToNT(struct timespec64 t)
{
/* Convert to 100ns intervals and then add the NTFS time offset. */
return (u64) t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET;
}
struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc);
#define KSMBD_TIME_TO_TM time64_to_tm
static inline long long ksmbd_systime(void)
{
struct timespec64 ts;
ktime_get_real_ts64(&ts);
return ksmbd_UnixTimeToNT(ts);
}
#endif /* __KSMBD_TIME_WRAPPERS_H */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Some of the source code in this file came from fs/cifs/cifs_unicode.c
*
* Copyright (c) International Business Machines Corp., 2000,2009
* Modified by Steve French (sfrench@us.ibm.com)
* Modified by Namjae Jeon (linkinjeon@kernel.org)
*/
#include <linux/fs.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include "glob.h"
#include "unicode.h"
#include "uniupr.h"
#include "smb_common.h"
/*
* smb_utf16_bytes() - how long will a string be after conversion?
* @from: pointer to input string
* @maxbytes: don't go past this many bytes of input string
* @codepage: destination codepage
*
* Walk a utf16le string and return the number of bytes that the string will
* be after being converted to the given charset, not including any null
* termination required. Don't walk past maxbytes in the source buffer.
*
* Return: string length after conversion
*/
static int smb_utf16_bytes(const __le16 *from,
int maxbytes,
const struct nls_table *codepage)
{
int i;
int charlen, outlen = 0;
int maxwords = maxbytes / 2;
char tmp[NLS_MAX_CHARSET_SIZE];
__u16 ftmp;
for (i = 0; i < maxwords; i++) {
ftmp = get_unaligned_le16(&from[i]);
if (ftmp == 0)
break;
charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE);
if (charlen > 0)
outlen += charlen;
else
outlen++;
}
return outlen;
}
/*
* cifs_mapchar() - convert a host-endian char to proper char in codepage
* @target: where converted character should be copied
* @src_char: 2 byte host-endian source character
* @cp: codepage to which character should be converted
* @mapchar: should character be mapped according to mapchars mount option?
*
* This function handles the conversion of a single character. It is the
* responsibility of the caller to ensure that the target buffer is large
* enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE).
*
* Return: string length after conversion
*/
static int
cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
bool mapchar)
{
int len = 1;
if (!mapchar)
goto cp_convert;
/*
* BB: Cannot handle remapping UNI_SLASH until all the calls to
* build_path_from_dentry are modified, as they use slash as
* separator.
*/
switch (src_char) {
case UNI_COLON:
*target = ':';
break;
case UNI_ASTERISK:
*target = '*';
break;
case UNI_QUESTION:
*target = '?';
break;
case UNI_PIPE:
*target = '|';
break;
case UNI_GRTRTHAN:
*target = '>';
break;
case UNI_LESSTHAN:
*target = '<';
break;
default:
goto cp_convert;
}
out:
return len;
cp_convert:
len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);
if (len <= 0) {
*target = '?';
len = 1;
}
goto out;
}
/*
* is_char_allowed() - check for valid character
* @ch: input character to be checked
*
* Return: 1 if char is allowed, otherwise 0
*/
static inline int is_char_allowed(char *ch)
{
/* check for control chars, wildcards etc. */
if (!(*ch & 0x80) &&
(*ch <= 0x1f ||
*ch == '?' || *ch == '"' || *ch == '<' ||
*ch == '>' || *ch == '|'))
return 0;
return 1;
}
/*
* smb_from_utf16() - convert utf16le string to local charset
* @to: destination buffer
* @from: source buffer
* @tolen: destination buffer size (in bytes)
* @fromlen: source buffer size (in bytes)
* @codepage: codepage to which characters should be converted
* @mapchar: should characters be remapped according to the mapchars option?
*
* Convert a little-endian utf16le string (as sent by the server) to a string
* in the provided codepage. The tolen and fromlen parameters are to ensure
* that the code doesn't walk off of the end of the buffer (which is always
* a danger if the alignment of the source buffer is off). The destination
* string is always properly null terminated and fits in the destination
* buffer. Returns the length of the destination string in bytes (including
* null terminator).
*
* Note that some windows versions actually send multiword UTF-16 characters
* instead of straight UTF16-2. The linux nls routines however aren't able to
* deal with those characters properly. In the event that we get some of
* those characters, they won't be translated properly.
*
* Return: string length after conversion
*/
static int smb_from_utf16(char *to,
const __le16 *from,
int tolen,
int fromlen,
const struct nls_table *codepage,
bool mapchar)
{
int i, charlen, safelen;
int outlen = 0;
int nullsize = nls_nullsize(codepage);
int fromwords = fromlen / 2;
char tmp[NLS_MAX_CHARSET_SIZE];
__u16 ftmp;
/*
* because the chars can be of varying widths, we need to take care
* not to overflow the destination buffer when we get close to the
* end of it. Until we get to this offset, we don't need to check
* for overflow however.
*/
safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize);
for (i = 0; i < fromwords; i++) {
ftmp = get_unaligned_le16(&from[i]);
if (ftmp == 0)
break;
/*
* check to see if converting this character might make the
* conversion bleed into the null terminator
*/
if (outlen >= safelen) {
charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar);
if ((outlen + charlen) > (tolen - nullsize))
break;
}
/* put converted char into 'to' buffer */
charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar);
outlen += charlen;
}
/* properly null-terminate string */
for (i = 0; i < nullsize; i++)
to[outlen++] = 0;
return outlen;
}
/*
* smb_strtoUTF16() - Convert character string to unicode string
* @to: destination buffer
* @from: source buffer
* @len: destination buffer size (in bytes)
* @codepage: codepage to which characters should be converted
*
* Return: string length after conversion
*/
int
smb_strtoUTF16(__le16 *to, const char *from, int len,
const struct nls_table *codepage)
{
int charlen;
int i;
wchar_t wchar_to; /* needed to quiet sparse */
/* special case for utf8 to handle no plane0 chars */
if (!strcmp(codepage->charset, "utf8")) {
/*
* convert utf8 -> utf16, we assume we have enough space
* as caller should have assumed conversion does not overflow
* in destination len is length in wchar_t units (16bits)
*/
i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN,
(wchar_t *) to, len);
/* if success terminate and exit */
if (i >= 0)
goto success;
/*
* if fails fall back to UCS encoding as this
* function should not return negative values
* currently can fail only if source contains
* invalid encoded characters
*/
}
for (i = 0; len > 0 && *from; i++, from += charlen, len -= charlen) {
charlen = codepage->char2uni(from, len, &wchar_to);
if (charlen < 1) {
/* A question mark */
wchar_to = 0x003f;
charlen = 1;
}
put_unaligned_le16(wchar_to, &to[i]);
}
success:
put_unaligned_le16(0, &to[i]);
return i;
}
/*
* smb_strndup_from_utf16() - copy a string from wire format to the local
* codepage
* @src: source string
* @maxlen: don't walk past this many bytes in the source string
* @is_unicode: is this a unicode string?
* @codepage: destination codepage
*
* Take a string given by the server, convert it to the local codepage and
* put it in a new buffer. Returns a pointer to the new string or NULL on
* error.
*
* Return: destination string buffer or error ptr
*/
char *
smb_strndup_from_utf16(const char *src, const int maxlen,
const bool is_unicode, const struct nls_table *codepage)
{
int len, ret;
char *dst;
if (is_unicode) {
len = smb_utf16_bytes((__le16 *) src, maxlen, codepage);
len += nls_nullsize(codepage);
dst = kmalloc(len, GFP_KERNEL);
if (!dst)
return ERR_PTR(-ENOMEM);
ret = smb_from_utf16(dst, (__le16 *) src, len, maxlen, codepage,
false);
if (ret < 0) {
kfree(dst);
return ERR_PTR(-EINVAL);
}
} else {
len = strnlen(src, maxlen);
len++;
dst = kmalloc(len, GFP_KERNEL);
if (!dst)
return ERR_PTR(-ENOMEM);
strscpy(dst, src, len);
}
return dst;
}
/*
* Convert 16 bit Unicode pathname to wire format from string in current code
* page. Conversion may involve remapping up the six characters that are
* only legal in POSIX-like OS (if they are present in the string). Path
* names are little endian 16 bit Unicode on the wire
*/
/*
* smbConvertToUTF16() - convert string from local charset to utf16
* @target: destination buffer
* @source: source buffer
* @srclen: source buffer size (in bytes)
* @cp: codepage to which characters should be converted
* @mapchar: should characters be remapped according to the mapchars option?
*
* Convert 16 bit Unicode pathname to wire format from string in current code
* page. Conversion may involve remapping up the six characters that are
* only legal in POSIX-like OS (if they are present in the string). Path
* names are little endian 16 bit Unicode on the wire
*
* Return: char length after conversion
*/
int
smbConvertToUTF16(__le16 *target, const char *source, int srclen,
const struct nls_table *cp, int mapchars)
{
int i, j, charlen;
char src_char;
__le16 dst_char;
wchar_t tmp;
if (!mapchars)
return smb_strtoUTF16(target, source, srclen, cp);
for (i = 0, j = 0; i < srclen; j++) {
src_char = source[i];
charlen = 1;
switch (src_char) {
case 0:
put_unaligned(0, &target[j]);
return j;
case ':':
dst_char = cpu_to_le16(UNI_COLON);
break;
case '*':
dst_char = cpu_to_le16(UNI_ASTERISK);
break;
case '?':
dst_char = cpu_to_le16(UNI_QUESTION);
break;
case '<':
dst_char = cpu_to_le16(UNI_LESSTHAN);
break;
case '>':
dst_char = cpu_to_le16(UNI_GRTRTHAN);
break;
case '|':
dst_char = cpu_to_le16(UNI_PIPE);
break;
/*
* FIXME: We can not handle remapping backslash (UNI_SLASH)
* until all the calls to build_path_from_dentry are modified,
* as they use backslash as separator.
*/
default:
charlen = cp->char2uni(source + i, srclen - i, &tmp);
dst_char = cpu_to_le16(tmp);
/*
* if no match, use question mark, which at least in
* some cases serves as wild card
*/
if (charlen < 1) {
dst_char = cpu_to_le16(0x003f);
charlen = 1;
}
}
/*
* character may take more than one byte in the source string,
* but will take exactly two bytes in the target string
*/
i += charlen;
put_unaligned(dst_char, &target[j]);
}
return j;
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Some of the source code in this file came from fs/cifs/cifs_unicode.c
* cifs_unicode: Unicode kernel case support
*
* Function:
* Convert a unicode character to upper or lower case using
* compressed tables.
*
* Copyright (c) International Business Machines Corp., 2000,2009
*
*
* Notes:
* These APIs are based on the C library functions. The semantics
* should match the C functions but with expanded size operands.
*
* The upper/lower functions are based on a table created by mkupr.
* This is a compressed table of upper and lower case conversion.
*
*/
#ifndef _CIFS_UNICODE_H
#define _CIFS_UNICODE_H
#include <asm/byteorder.h>
#include <linux/types.h>
#include <linux/nls.h>
#define UNIUPR_NOLOWER /* Example to not expand lower case tables */
/*
* Windows maps these to the user defined 16 bit Unicode range since they are
* reserved symbols (along with \ and /), otherwise illegal to store
* in filenames in NTFS
*/
#define UNI_ASTERISK ((__u16) ('*' + 0xF000))
#define UNI_QUESTION ((__u16) ('?' + 0xF000))
#define UNI_COLON ((__u16) (':' + 0xF000))
#define UNI_GRTRTHAN ((__u16) ('>' + 0xF000))
#define UNI_LESSTHAN ((__u16) ('<' + 0xF000))
#define UNI_PIPE ((__u16) ('|' + 0xF000))
#define UNI_SLASH ((__u16) ('\\' + 0xF000))
/* Just define what we want from uniupr.h. We don't want to define the tables
* in each source file.
*/
#ifndef UNICASERANGE_DEFINED
struct UniCaseRange {
wchar_t start;
wchar_t end;
signed char *table;
};
#endif /* UNICASERANGE_DEFINED */
#ifndef UNIUPR_NOUPPER
extern signed char SmbUniUpperTable[512];
extern const struct UniCaseRange SmbUniUpperRange[];
#endif /* UNIUPR_NOUPPER */
#ifndef UNIUPR_NOLOWER
extern signed char CifsUniLowerTable[512];
extern const struct UniCaseRange CifsUniLowerRange[];
#endif /* UNIUPR_NOLOWER */
#ifdef __KERNEL__
int smb_strtoUTF16(__le16 *to, const char *from, int len,
const struct nls_table *codepage);
char *smb_strndup_from_utf16(const char *src, const int maxlen,
const bool is_unicode,
const struct nls_table *codepage);
extern int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
const struct nls_table *cp, int mapchars);
extern char *extract_sharename(char *treename);
#endif
wchar_t cifs_toupper(wchar_t in);
/*
* UniStrcat: Concatenate the second string to the first
*
* Returns:
* Address of the first string
*/
static inline wchar_t *
UniStrcat(wchar_t *ucs1, const wchar_t *ucs2)
{
wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */
while (*ucs1++)
/*NULL*/; /* To end of first string */
ucs1--; /* Return to the null */
while ((*ucs1++ = *ucs2++))
/*NULL*/; /* copy string 2 over */
return anchor;
}
/*
* UniStrchr: Find a character in a string
*
* Returns:
* Address of first occurrence of character in string
* or NULL if the character is not in the string
*/
static inline wchar_t *
UniStrchr(const wchar_t *ucs, wchar_t uc)
{
while ((*ucs != uc) && *ucs)
ucs++;
if (*ucs == uc)
return (wchar_t *) ucs;
return NULL;
}
/*
* UniStrcmp: Compare two strings
*
* Returns:
* < 0: First string is less than second
* = 0: Strings are equal
* > 0: First string is greater than second
*/
static inline int
UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2)
{
while ((*ucs1 == *ucs2) && *ucs1) {
ucs1++;
ucs2++;
}
return (int) *ucs1 - (int) *ucs2;
}
/*
* UniStrcpy: Copy a string
*/
static inline wchar_t *
UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2)
{
wchar_t *anchor = ucs1; /* save the start of result string */
while ((*ucs1++ = *ucs2++))
/*NULL*/;
return anchor;
}
/*
* UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes)
*/
static inline size_t
UniStrlen(const wchar_t *ucs1)
{
int i = 0;
while (*ucs1++)
i++;
return i;
}
/*
* UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a
* string (length limited)
*/
static inline size_t
UniStrnlen(const wchar_t *ucs1, int maxlen)
{
int i = 0;
while (*ucs1++) {
i++;
if (i >= maxlen)
break;
}
return i;
}
/*
* UniStrncat: Concatenate length limited string
*/
static inline wchar_t *
UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
wchar_t *anchor = ucs1; /* save pointer to string 1 */
while (*ucs1++)
/*NULL*/;
ucs1--; /* point to null terminator of s1 */
while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */
ucs1++;
ucs2++;
}
*ucs1 = 0; /* Null terminate the result */
return anchor;
}
/*
* UniStrncmp: Compare length limited string
*/
static inline int
UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
if (!n)
return 0; /* Null strings are equal */
while ((*ucs1 == *ucs2) && *ucs1 && --n) {
ucs1++;
ucs2++;
}
return (int) *ucs1 - (int) *ucs2;
}
/*
* UniStrncmp_le: Compare length limited string - native to little-endian
*/
static inline int
UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
if (!n)
return 0; /* Null strings are equal */
while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) {
ucs1++;
ucs2++;
}
return (int) *ucs1 - (int) __le16_to_cpu(*ucs2);
}
/*
* UniStrncpy: Copy length limited string with pad
*/
static inline wchar_t *
UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
wchar_t *anchor = ucs1;
while (n-- && *ucs2) /* Copy the strings */
*ucs1++ = *ucs2++;
n++;
while (n--) /* Pad with nulls */
*ucs1++ = 0;
return anchor;
}
/*
* UniStrncpy_le: Copy length limited string with pad to little-endian
*/
static inline wchar_t *
UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
{
wchar_t *anchor = ucs1;
while (n-- && *ucs2) /* Copy the strings */
*ucs1++ = __le16_to_cpu(*ucs2++);
n++;
while (n--) /* Pad with nulls */
*ucs1++ = 0;
return anchor;
}
/*
* UniStrstr: Find a string in a string
*
* Returns:
* Address of first match found
* NULL if no matching string is found
*/
static inline wchar_t *
UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2)
{
const wchar_t *anchor1 = ucs1;
const wchar_t *anchor2 = ucs2;
while (*ucs1) {
if (*ucs1 == *ucs2) {
/* Partial match found */
ucs1++;
ucs2++;
} else {
if (!*ucs2) /* Match found */
return (wchar_t *) anchor1;
ucs1 = ++anchor1; /* No match */
ucs2 = anchor2;
}
}
if (!*ucs2) /* Both end together */
return (wchar_t *) anchor1; /* Match found */
return NULL; /* No match */
}
#ifndef UNIUPR_NOUPPER
/*
* UniToupper: Convert a unicode character to upper case
*/
static inline wchar_t
UniToupper(register wchar_t uc)
{
register const struct UniCaseRange *rp;
if (uc < sizeof(SmbUniUpperTable)) {
/* Latin characters */
return uc + SmbUniUpperTable[uc]; /* Use base tables */
}
rp = SmbUniUpperRange; /* Use range tables */
while (rp->start) {
if (uc < rp->start) /* Before start of range */
return uc; /* Uppercase = input */
if (uc <= rp->end) /* In range */
return uc + rp->table[uc - rp->start];
rp++; /* Try next range */
}
return uc; /* Past last range */
}
/*
* UniStrupr: Upper case a unicode string
*/
static inline __le16 *
UniStrupr(register __le16 *upin)
{
register __le16 *up;
up = upin;
while (*up) { /* For all characters */
*up = cpu_to_le16(UniToupper(le16_to_cpu(*up)));
up++;
}
return upin; /* Return input pointer */
}
#endif /* UNIUPR_NOUPPER */
#ifndef UNIUPR_NOLOWER
/*
* UniTolower: Convert a unicode character to lower case
*/
static inline wchar_t
UniTolower(register wchar_t uc)
{
register const struct UniCaseRange *rp;
if (uc < sizeof(CifsUniLowerTable)) {
/* Latin characters */
return uc + CifsUniLowerTable[uc]; /* Use base tables */
}
rp = CifsUniLowerRange; /* Use range tables */
while (rp->start) {
if (uc < rp->start) /* Before start of range */
return uc; /* Uppercase = input */
if (uc <= rp->end) /* In range */
return uc + rp->table[uc - rp->start];
rp++; /* Try next range */
}
return uc; /* Past last range */
}
/*
* UniStrlwr: Lower case a unicode string
*/
static inline wchar_t *
UniStrlwr(register wchar_t *upin)
{
register wchar_t *up;
up = upin;
while (*up) { /* For all characters */
*up = UniTolower(*up);
up++;
}
return upin; /* Return input pointer */
}
#endif
#endif /* _CIFS_UNICODE_H */
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Some of the source code in this file came from fs/cifs/uniupr.h
* Copyright (c) International Business Machines Corp., 2000,2002
*
* uniupr.h - Unicode compressed case ranges
*
*/
#ifndef __KSMBD_UNIUPR_H
#define __KSMBD_UNIUPR_H
#ifndef UNIUPR_NOUPPER
/*
* Latin upper case
*/
signed char SmbUniUpperTable[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */
0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, -32, /* 060-06f */
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, 0, 0, 0, 0, 0, /* 070-07f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, -32, -32, /* 0e0-0ef */
-32, -32, -32, -32, -32, -32, -32, 0, -32, -32,
-32, -32, -32, -32, -32, 121, /* 0f0-0ff */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */
0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */
-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */
0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */
0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */
0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */
0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */
-1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */
0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */
-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */
0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */
};
/* Upper case range - Greek */
static signed char UniCaseRangeU03a0[47] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */
0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, /* 3b0-3bf */
-32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64,
-63, -63,
};
/* Upper case range - Cyrillic */
static signed char UniCaseRangeU0430[48] = {
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, /* 430-43f */
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, /* 440-44f */
0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
-80, -80, 0, -80, -80, /* 450-45f */
};
/* Upper case range - Extended cyrillic */
static signed char UniCaseRangeU0490[61] = {
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */
0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1,
};
/* Upper case range - Extended latin and greek */
static signed char UniCaseRangeU1e00[509] = {
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */
0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */
8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */
8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */
0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */
74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112,
126, 126, 0, 0, /* 1f70-1f7f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */
8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */
0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */
8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */
8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */
0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
/* Upper case range - Wide latin */
static signed char UniCaseRangeUff40[27] = {
0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, -32, /* ff40-ff4f */
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
};
/*
* Upper Case Range
*/
const struct UniCaseRange SmbUniUpperRange[] = {
{0x03a0, 0x03ce, UniCaseRangeU03a0},
{0x0430, 0x045f, UniCaseRangeU0430},
{0x0490, 0x04cc, UniCaseRangeU0490},
{0x1e00, 0x1ffc, UniCaseRangeU1e00},
{0xff40, 0xff5a, UniCaseRangeUff40},
{0}
};
#endif
#ifndef UNIUPR_NOLOWER
/*
* Latin lower case
*/
signed char CifsUniLowerTable[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */
0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, /* 040-04f */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0,
0, 0, 0, /* 050-05f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, /* 0c0-0cf */
32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32,
32, 32, 32, 0, /* 0d0-0df */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */
0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */
0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */
1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0,
0, /* 170-17f */
0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79,
0, /* 180-18f */
0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */
1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */
0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */
0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */
0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */
};
/* Lower case range - Greek */
static signed char UniCaseRangeL0380[44] = {
0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */
0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, /* 390-39f */
32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32,
};
/* Lower case range - Cyrillic */
static signed char UniCaseRangeL0400[48] = {
0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
0, 80, 80, /* 400-40f */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, /* 410-41f */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, /* 420-42f */
};
/* Lower case range - Extended cyrillic */
static signed char UniCaseRangeL0490[60] = {
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */
0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1,
};
/* Lower case range - Extended latin and greek */
static signed char UniCaseRangeL1e00[504] = {
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */
1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */
0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */
0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0,
0, 0, /* 1fc0-1fcf */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0,
0, 0, /* 1fe0-1fef */
0, 0, 0, 0, 0, 0, 0, 0,
};
/* Lower case range - Wide latin */
static signed char UniCaseRangeLff20[27] = {
0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, /* ff20-ff2f */
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
};
/*
* Lower Case Range
*/
const struct UniCaseRange CifsUniLowerRange[] = {
{0x0380, 0x03ab, UniCaseRangeL0380},
{0x0400, 0x042f, UniCaseRangeL0400},
{0x0490, 0x04cb, UniCaseRangeL0490},
{0x1e00, 0x1ff7, UniCaseRangeL1e00},
{0xff20, 0xff3a, UniCaseRangeLff20},
{0}
};
#endif
#endif /* __KSMBD_UNIUPR_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment