515 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2021 Samsung Electronics Co., Ltd.4 * Author(s): Namjae Jeon <linkinjeon@kernel.org>5 */6 7#include <linux/fs.h>8 9#include "glob.h"10#include "ndr.h"11 12static inline char *ndr_get_field(struct ndr *n)13{14 return n->data + n->offset;15}16 17static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz)18{19 char *data;20 21 data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL);22 if (!data)23 return -ENOMEM;24 25 n->data = data;26 n->length += 1024;27 memset(n->data + n->offset, 0, 1024);28 return 0;29}30 31static int ndr_write_int16(struct ndr *n, __u16 value)32{33 if (n->length <= n->offset + sizeof(value)) {34 int ret;35 36 ret = try_to_realloc_ndr_blob(n, sizeof(value));37 if (ret)38 return ret;39 }40 41 *(__le16 *)ndr_get_field(n) = cpu_to_le16(value);42 n->offset += sizeof(value);43 return 0;44}45 46static int ndr_write_int32(struct ndr *n, __u32 value)47{48 if (n->length <= n->offset + sizeof(value)) {49 int ret;50 51 ret = try_to_realloc_ndr_blob(n, sizeof(value));52 if (ret)53 return ret;54 }55 56 *(__le32 *)ndr_get_field(n) = cpu_to_le32(value);57 n->offset += sizeof(value);58 return 0;59}60 61static int ndr_write_int64(struct ndr *n, __u64 value)62{63 if (n->length <= n->offset + sizeof(value)) {64 int ret;65 66 ret = try_to_realloc_ndr_blob(n, sizeof(value));67 if (ret)68 return ret;69 }70 71 *(__le64 *)ndr_get_field(n) = cpu_to_le64(value);72 n->offset += sizeof(value);73 return 0;74}75 76static int ndr_write_bytes(struct ndr *n, void *value, size_t sz)77{78 if (n->length <= n->offset + sz) {79 int ret;80 81 ret = try_to_realloc_ndr_blob(n, sz);82 if (ret)83 return ret;84 }85 86 memcpy(ndr_get_field(n), value, sz);87 n->offset += sz;88 return 0;89}90 91static int ndr_write_string(struct ndr *n, char *value)92{93 size_t sz;94 95 sz = strlen(value) + 1;96 if (n->length <= n->offset + sz) {97 int ret;98 99 ret = try_to_realloc_ndr_blob(n, sz);100 if (ret)101 return ret;102 }103 104 memcpy(ndr_get_field(n), value, sz);105 n->offset += sz;106 n->offset = ALIGN(n->offset, 2);107 return 0;108}109 110static int ndr_read_string(struct ndr *n, void *value, size_t sz)111{112 int len;113 114 if (n->offset + sz > n->length)115 return -EINVAL;116 117 len = strnlen(ndr_get_field(n), sz);118 if (value)119 memcpy(value, ndr_get_field(n), len);120 len++;121 n->offset += len;122 n->offset = ALIGN(n->offset, 2);123 return 0;124}125 126static int ndr_read_bytes(struct ndr *n, void *value, size_t sz)127{128 if (n->offset + sz > n->length)129 return -EINVAL;130 131 if (value)132 memcpy(value, ndr_get_field(n), sz);133 n->offset += sz;134 return 0;135}136 137static int ndr_read_int16(struct ndr *n, __u16 *value)138{139 if (n->offset + sizeof(__u16) > n->length)140 return -EINVAL;141 142 if (value)143 *value = le16_to_cpu(*(__le16 *)ndr_get_field(n));144 n->offset += sizeof(__u16);145 return 0;146}147 148static int ndr_read_int32(struct ndr *n, __u32 *value)149{150 if (n->offset + sizeof(__u32) > n->length)151 return -EINVAL;152 153 if (value)154 *value = le32_to_cpu(*(__le32 *)ndr_get_field(n));155 n->offset += sizeof(__u32);156 return 0;157}158 159static int ndr_read_int64(struct ndr *n, __u64 *value)160{161 if (n->offset + sizeof(__u64) > n->length)162 return -EINVAL;163 164 if (value)165 *value = le64_to_cpu(*(__le64 *)ndr_get_field(n));166 n->offset += sizeof(__u64);167 return 0;168}169 170int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da)171{172 char hex_attr[12] = {0};173 int ret;174 175 n->offset = 0;176 n->length = 1024;177 n->data = kzalloc(n->length, GFP_KERNEL);178 if (!n->data)179 return -ENOMEM;180 181 if (da->version == 3) {182 snprintf(hex_attr, 10, "0x%x", da->attr);183 ret = ndr_write_string(n, hex_attr);184 } else {185 ret = ndr_write_string(n, "");186 }187 if (ret)188 return ret;189 190 ret = ndr_write_int16(n, da->version);191 if (ret)192 return ret;193 194 ret = ndr_write_int32(n, da->version);195 if (ret)196 return ret;197 198 ret = ndr_write_int32(n, da->flags);199 if (ret)200 return ret;201 202 ret = ndr_write_int32(n, da->attr);203 if (ret)204 return ret;205 206 if (da->version == 3) {207 ret = ndr_write_int32(n, da->ea_size);208 if (ret)209 return ret;210 ret = ndr_write_int64(n, da->size);211 if (ret)212 return ret;213 ret = ndr_write_int64(n, da->alloc_size);214 } else {215 ret = ndr_write_int64(n, da->itime);216 }217 if (ret)218 return ret;219 220 ret = ndr_write_int64(n, da->create_time);221 if (ret)222 return ret;223 224 if (da->version == 3)225 ret = ndr_write_int64(n, da->change_time);226 return ret;227}228 229int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da)230{231 char hex_attr[12];232 unsigned int version2;233 int ret;234 235 n->offset = 0;236 ret = ndr_read_string(n, hex_attr, sizeof(hex_attr));237 if (ret)238 return ret;239 240 ret = ndr_read_int16(n, &da->version);241 if (ret)242 return ret;243 244 if (da->version != 3 && da->version != 4) {245 ksmbd_debug(VFS, "v%d version is not supported\n", da->version);246 return -EINVAL;247 }248 249 ret = ndr_read_int32(n, &version2);250 if (ret)251 return ret;252 253 if (da->version != version2) {254 ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n",255 da->version, version2);256 return -EINVAL;257 }258 259 ret = ndr_read_int32(n, NULL);260 if (ret)261 return ret;262 263 ret = ndr_read_int32(n, &da->attr);264 if (ret)265 return ret;266 267 if (da->version == 4) {268 ret = ndr_read_int64(n, &da->itime);269 if (ret)270 return ret;271 272 ret = ndr_read_int64(n, &da->create_time);273 } else {274 ret = ndr_read_int32(n, NULL);275 if (ret)276 return ret;277 278 ret = ndr_read_int64(n, NULL);279 if (ret)280 return ret;281 282 ret = ndr_read_int64(n, NULL);283 if (ret)284 return ret;285 286 ret = ndr_read_int64(n, &da->create_time);287 if (ret)288 return ret;289 290 ret = ndr_read_int64(n, NULL);291 }292 293 return ret;294}295 296static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl)297{298 int i, ret;299 300 ret = ndr_write_int32(n, acl->count);301 if (ret)302 return ret;303 304 n->offset = ALIGN(n->offset, 8);305 ret = ndr_write_int32(n, acl->count);306 if (ret)307 return ret;308 309 ret = ndr_write_int32(n, 0);310 if (ret)311 return ret;312 313 for (i = 0; i < acl->count; i++) {314 n->offset = ALIGN(n->offset, 8);315 ret = ndr_write_int16(n, acl->entries[i].type);316 if (ret)317 return ret;318 319 ret = ndr_write_int16(n, acl->entries[i].type);320 if (ret)321 return ret;322 323 if (acl->entries[i].type == SMB_ACL_USER) {324 n->offset = ALIGN(n->offset, 8);325 ret = ndr_write_int64(n, acl->entries[i].uid);326 } else if (acl->entries[i].type == SMB_ACL_GROUP) {327 n->offset = ALIGN(n->offset, 8);328 ret = ndr_write_int64(n, acl->entries[i].gid);329 }330 if (ret)331 return ret;332 333 /* push permission */334 ret = ndr_write_int32(n, acl->entries[i].perm);335 }336 337 return ret;338}339 340int ndr_encode_posix_acl(struct ndr *n,341 struct mnt_idmap *idmap,342 struct inode *inode,343 struct xattr_smb_acl *acl,344 struct xattr_smb_acl *def_acl)345{346 unsigned int ref_id = 0x00020000;347 int ret;348 vfsuid_t vfsuid;349 vfsgid_t vfsgid;350 351 n->offset = 0;352 n->length = 1024;353 n->data = kzalloc(n->length, GFP_KERNEL);354 if (!n->data)355 return -ENOMEM;356 357 if (acl) {358 /* ACL ACCESS */359 ret = ndr_write_int32(n, ref_id);360 ref_id += 4;361 } else {362 ret = ndr_write_int32(n, 0);363 }364 if (ret)365 return ret;366 367 if (def_acl) {368 /* DEFAULT ACL ACCESS */369 ret = ndr_write_int32(n, ref_id);370 ref_id += 4;371 } else {372 ret = ndr_write_int32(n, 0);373 }374 if (ret)375 return ret;376 377 vfsuid = i_uid_into_vfsuid(idmap, inode);378 ret = ndr_write_int64(n, from_kuid(&init_user_ns, vfsuid_into_kuid(vfsuid)));379 if (ret)380 return ret;381 vfsgid = i_gid_into_vfsgid(idmap, inode);382 ret = ndr_write_int64(n, from_kgid(&init_user_ns, vfsgid_into_kgid(vfsgid)));383 if (ret)384 return ret;385 ret = ndr_write_int32(n, inode->i_mode);386 if (ret)387 return ret;388 389 if (acl) {390 ret = ndr_encode_posix_acl_entry(n, acl);391 if (def_acl && !ret)392 ret = ndr_encode_posix_acl_entry(n, def_acl);393 }394 return ret;395}396 397int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)398{399 unsigned int ref_id = 0x00020004;400 int ret;401 402 n->offset = 0;403 n->length = 2048;404 n->data = kzalloc(n->length, GFP_KERNEL);405 if (!n->data)406 return -ENOMEM;407 408 ret = ndr_write_int16(n, acl->version);409 if (ret)410 return ret;411 412 ret = ndr_write_int32(n, acl->version);413 if (ret)414 return ret;415 416 ret = ndr_write_int16(n, 2);417 if (ret)418 return ret;419 420 ret = ndr_write_int32(n, ref_id);421 if (ret)422 return ret;423 424 /* push hash type and hash 64bytes */425 ret = ndr_write_int16(n, acl->hash_type);426 if (ret)427 return ret;428 429 ret = ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE);430 if (ret)431 return ret;432 433 ret = ndr_write_bytes(n, acl->desc, acl->desc_len);434 if (ret)435 return ret;436 437 ret = ndr_write_int64(n, acl->current_time);438 if (ret)439 return ret;440 441 ret = ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE);442 if (ret)443 return ret;444 445 /* push ndr for security descriptor */446 ret = ndr_write_bytes(n, acl->sd_buf, acl->sd_size);447 return ret;448}449 450int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)451{452 unsigned int version2;453 int ret;454 455 n->offset = 0;456 ret = ndr_read_int16(n, &acl->version);457 if (ret)458 return ret;459 if (acl->version != 4) {460 ksmbd_debug(VFS, "v%d version is not supported\n", acl->version);461 return -EINVAL;462 }463 464 ret = ndr_read_int32(n, &version2);465 if (ret)466 return ret;467 if (acl->version != version2) {468 ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n",469 acl->version, version2);470 return -EINVAL;471 }472 473 /* Read Level */474 ret = ndr_read_int16(n, NULL);475 if (ret)476 return ret;477 478 /* Read Ref Id */479 ret = ndr_read_int32(n, NULL);480 if (ret)481 return ret;482 483 ret = ndr_read_int16(n, &acl->hash_type);484 if (ret)485 return ret;486 487 ret = ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE);488 if (ret)489 return ret;490 491 ndr_read_bytes(n, acl->desc, 10);492 if (strncmp(acl->desc, "posix_acl", 9)) {493 pr_err("Invalid acl description : %s\n", acl->desc);494 return -EINVAL;495 }496 497 /* Read Time */498 ret = ndr_read_int64(n, NULL);499 if (ret)500 return ret;501 502 /* Read Posix ACL hash */503 ret = ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE);504 if (ret)505 return ret;506 507 acl->sd_size = n->length - n->offset;508 acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL);509 if (!acl->sd_buf)510 return -ENOMEM;511 512 ret = ndr_read_bytes(n, acl->sd_buf, acl->sd_size);513 return ret;514}515