Print this page
12513 SMB 3.1.1 support for server

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c
          +++ new/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c
↓ open down ↓ 3 lines elided ↑ open up ↑
   4    4   * You may only use this file in accordance with the terms of version
   5    5   * 1.0 of the CDDL.
   6    6   *
   7    7   * A full copy of the text of the CDDL should have accompanied this
   8    8   * source.  A copy of the CDDL is also available via the Internet at
   9    9   * http://www.illumos.org/license/CDDL.
  10   10   */
  11   11  
  12   12  /*
  13   13   * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
  14      - * Copyright 2019 RackTop Systems.
       14 + * Copyright 2020 RackTop Systems, Inc.
  15   15   */
  16   16  
  17   17  /*
  18   18   * Dispatch function for SMB2_NEGOTIATE
  19   19   */
  20   20  
  21   21  #include <smbsrv/smb2_kproto.h>
  22   22  #include <smbsrv/smb2.h>
       23 +#include <sys/random.h>
  23   24  
  24   25  /*
  25   26   * Note from [MS-SMB2] Sec. 2.2.3:  Windows servers return
  26   27   * invalid parameter if the dialect count is greater than 64
  27   28   * This is here (and not in smb2.h) because this is technically
  28   29   * an implementation detail, not protocol specification.
  29   30   */
  30   31  #define SMB2_NEGOTIATE_MAX_DIALECTS     64
  31   32  
  32   33  static int smb2_negotiate_common(smb_request_t *, uint16_t);
↓ open down ↓ 45 lines elided ↑ open up ↑
  78   79  /*
  79   80   * List of all SMB2 versions we implement.  Note that the
  80   81   * versions we support may be limited by the
  81   82   * _cfg.skc_max_protocol and min_protocol settings.
  82   83   */
  83   84  static uint16_t smb2_versions[] = {
  84   85          0x202,  /* SMB 2.002 */
  85   86          0x210,  /* SMB 2.1 */
  86   87          0x300,  /* SMB 3.0 */
  87   88          0x302,  /* SMB 3.02 */
       89 +        0x311,  /* SMB 3.11 */
  88   90  };
  89   91  static uint16_t smb2_nversions =
  90   92      sizeof (smb2_versions) / sizeof (smb2_versions[0]);
  91   93  
  92   94  static boolean_t
  93   95  smb2_supported_version(smb_session_t *s, uint16_t version)
  94   96  {
  95   97          int i;
  96   98  
  97   99          if (version > s->s_cfg.skc_max_protocol ||
↓ open down ↓ 105 lines elided ↑ open up ↑
 203  205   * Since this is called directly from the reader thread, we
 204  206   * know this is the only thread currently using this session.
 205  207   * This has to duplicate some of what smb2sr_work does as a
 206  208   * result of bypassing the normal dispatch mechanism.
 207  209   *
 208  210   * The caller always frees this request.
 209  211   *
 210  212   * Return value is 0 for success, and anything else will
 211  213   * terminate the reader thread (drop the connection).
 212  214   */
      215 +typedef struct smb31_preauth_integrity_caps_ctx {
      216 +        uint16_t        hash_count;
      217 +        uint16_t        salt_len;
      218 +        char            *salt;
      219 +} smb31_preauth_integrity_caps_ctx_t;
      220 +
      221 +typedef struct smb31_encryption_caps_ctx {
      222 +        uint16_t        cipher_count;
      223 +} smb31_encrypt_caps_ctx_t;
      224 +
      225 +enum smb3_neg_ctx_type {
      226 +        SMB31_PREAUTH_INTEGRITY_CAPS = 1,
      227 +        SMB31_ENCRYPTION_CAPS = 2
      228 +};
      229 +
      230 +typedef struct smb3_negotiate_context {
      231 +        uint16_t        type;
      232 +        uint16_t        datalen;
      233 +        uint32_t        _resrvd;
      234 +        char            data[];
      235 +} smb3_neg_ctx_t;
      236 +
      237 +typedef struct smb3_negotiate_context_info {
      238 +        uint32_t        offset;
      239 +        uint16_t        count;
      240 +        uint16_t        _resrvd;
      241 +} smb3_neg_ctx_info_t;
      242 +
      243 +#define NEG_CTX_INFO_OFFSET     (SMB2_HDR_SIZE + 28)
      244 +#define STATUS_SMB_NO_PREAUTH_INEGRITY_HASH_OVERLAP     (0xC05D0000)
      245 +
      246 +#define STATUS_PREAUTH_HASH_OVERLAP \
      247 +    STATUS_SMB_NO_PREAUTH_INEGRITY_HASH_OVERLAP
      248 +
      249 +/*
      250 + * This function should be called only for dialect >= 0x311
      251 + * Negotiate context list should contain exactly one
      252 + * smb31_preauth_INTEGRITY_CAPS context.
      253 + * Otherwise STATUS_INVALID_PARAMETER.
      254 + * It should contain at least 1 hash algorith what server does support.
      255 + * Otehrwise STATUS_SMB_NO_PREAUTH_INEGRITY_HASH_OVERLAP.
      256 + */
      257 +static uint32_t
      258 +smb31_decode_neg_ctxs(smb_request_t *sr, smb_session_t *s)
      259 +{
      260 +        uint32_t status = 0;
      261 +        int found_preauth_ctx = 0;
      262 +        boolean_t preauth_sha512_enabled = B_FALSE;
      263 +        boolean_t encrypt_ccm_enabled = B_FALSE;
      264 +        boolean_t encrypt_gcm_enabled = B_FALSE;
      265 +        uint16_t cipher = sr->sr_server->sv_cfg.skc_encrypt_cipher;
      266 +        smb3_neg_ctx_info_t     neg_ctx_info;
      267 +        int cnt, i;
      268 +        int rc;
      269 +
      270 +        sr->command.chain_offset = NEG_CTX_INFO_OFFSET;
      271 +
      272 +        rc = smb_mbc_decodef(&sr->command, "lw2.",
      273 +            &neg_ctx_info.offset,       /* l */
      274 +            &neg_ctx_info.count);       /* w */
      275 +        if (rc != 0) {
      276 +                status = NT_STATUS_INVALID_PARAMETER;
      277 +                goto errout;
      278 +        }
      279 +
      280 +        /*
      281 +         * There should be exactly 1 SMB31_PREAUTH_INTEGRITY_CAPS negotiate ctx.
      282 +         * SMB31_ENCRYPTION_CAPS is optional one.
      283 +         */
      284 +        cnt = neg_ctx_info.count;
      285 +        if (cnt < 1) {
      286 +                status = NT_STATUS_INVALID_PARAMETER;
      287 +                goto errout;
      288 +        }
      289 +
      290 +        sr->command.chain_offset = neg_ctx_info.offset;
      291 +
      292 +        /*
      293 +         * Parse negotiate contexts.
      294 +         */
      295 +        for (i = 0; i < cnt; i++) {
      296 +                smb3_neg_ctx_t neg_ctx;
      297 +
      298 +                sr->command.chain_offset =
      299 +                    P2ROUNDUP(sr->command.chain_offset, 8);
      300 +
      301 +                rc = smb_mbc_decodef(
      302 +                    &sr->command, "ww4.",
      303 +                    &neg_ctx.type,      /* w */
      304 +                    &neg_ctx.datalen);  /* w */
      305 +                if (rc != 0) {
      306 +                        status = NT_STATUS_INVALID_PARAMETER;
      307 +                        goto errout;
      308 +                }
      309 +
      310 +                if (neg_ctx.type == SMB31_PREAUTH_INTEGRITY_CAPS) {
      311 +                        smb31_preauth_integrity_caps_ctx_t pic_ctx;
      312 +
      313 +                        if (found_preauth_ctx++ != 0) {
      314 +                                status = NT_STATUS_INVALID_PARAMETER;
      315 +                                goto errout;
      316 +                        }
      317 +
      318 +                        rc = smb_mbc_decodef(
      319 +                            &sr->command, "ww",
      320 +                            &pic_ctx.hash_count,        /* w */
      321 +                            &pic_ctx.salt_len);         /* w */
      322 +                        if (rc != 0) {
      323 +                                status = NT_STATUS_INVALID_PARAMETER;
      324 +                                goto errout;
      325 +                        }
      326 +
      327 +                        /*
      328 +                         * In SMB 0x311 there should be exactly 1 preauth
      329 +                         * negotiate context, and there should be exactly 1
      330 +                         * hash value in the list - SHA512.
      331 +                         */
      332 +                        if (pic_ctx.hash_count != 1) {
      333 +                                status = NT_STATUS_INVALID_PARAMETER;
      334 +                                goto errout;
      335 +                        }
      336 +
      337 +                        for (int k = 0; k < pic_ctx.hash_count; k++) {
      338 +                                uint16_t hash_id;
      339 +
      340 +                                rc = smb_mbc_decodef(
      341 +                                    &sr->command, "w",
      342 +                                    &hash_id);    /* w */
      343 +
      344 +                                switch (hash_id) {
      345 +                                case SMB3_HASH_SHA512:
      346 +                                        preauth_sha512_enabled = B_TRUE;
      347 +                                        break;
      348 +                                default:
      349 +                                        ;
      350 +                                }
      351 +                        }
      352 +
      353 +                        rc = smb_mbc_decodef(
      354 +                            &sr->command, "#.",
      355 +                            pic_ctx.salt_len);
      356 +
      357 +                } else if (neg_ctx.type == SMB31_ENCRYPTION_CAPS) {
      358 +                        smb31_encrypt_caps_ctx_t enc_ctx;
      359 +
      360 +                        rc = smb_mbc_decodef(
      361 +                            &sr->command, "w",
      362 +                            &enc_ctx.cipher_count);     /* w */
      363 +                        if (rc != 0) {
      364 +                                status = NT_STATUS_INVALID_PARAMETER;
      365 +                                goto errout;
      366 +                        }
      367 +
      368 +                        for (int k = 0; k < enc_ctx.cipher_count; k++) {
      369 +                                uint16_t cipher_id;
      370 +
      371 +                                rc = smb_mbc_decodef(
      372 +                                    &sr->command, "w",
      373 +                                    &cipher_id);        /* w */
      374 +
      375 +                                switch (cipher_id) {
      376 +                                case SMB3_CIPHER_AES128_CCM:
      377 +                                        encrypt_ccm_enabled = B_TRUE;
      378 +                                        break;
      379 +                                case SMB3_CIPHER_AES128_GCM:
      380 +                                        encrypt_gcm_enabled = B_TRUE;
      381 +                                        break;
      382 +                                default:
      383 +                                        ;
      384 +                                }
      385 +                        }
      386 +                } else {
      387 +                        /* Skip unsupported context */
      388 +                        ASSERT0(smb_mbc_decodef(
      389 +                            &sr->command, "#.",
      390 +                            neg_ctx.datalen));
      391 +                }
      392 +        }
      393 +
      394 +        /* Not found mandatory SMB31_PREAUTH_INTEGRITY_CAPS ctx */
      395 +        if (!found_preauth_ctx)
      396 +                status = NT_STATUS_INVALID_PARAMETER;
      397 +
      398 +        if (!preauth_sha512_enabled) {
      399 +                status = STATUS_PREAUTH_HASH_OVERLAP;
      400 +                goto errout;
      401 +        }
      402 +
      403 +        s->smb31_preauth_hashid = SMB3_HASH_SHA512;
      404 +
      405 +        switch (cipher) {
      406 +        case SMB3_CIPHER_AES128_GCM:
      407 +                if (encrypt_gcm_enabled) {
      408 +                        s->smb31_enc_cipherid = cipher;
      409 +                        break;
      410 +                }
      411 +                /* FALLTHROUGH */
      412 +        case SMB3_CIPHER_AES128_CCM:
      413 +                if (encrypt_ccm_enabled) {
      414 +                        s->smb31_enc_cipherid = cipher;
      415 +                        break;
      416 +                }
      417 +                /* FALLTHROUGH */
      418 +        default:
      419 +                s->smb31_enc_cipherid = 0;
      420 +        }
      421 +
      422 +errout:
      423 +        return (status);
      424 +}
      425 +
      426 +#define SMB31_PREAUTH_CTX_SALT_LEN      32
      427 +
      428 +static int
      429 +smb31_encode_neg_ctxs(smb_request_t *sr, smb_session_t *s)
      430 +{
      431 +        uint8_t salt[SMB31_PREAUTH_CTX_SALT_LEN];
      432 +        uint16_t salt_len = SMB31_PREAUTH_CTX_SALT_LEN;
      433 +        uint32_t preauth_ctx_len = 6 + salt_len;
      434 +        uint32_t enc_ctx_len = 4;
      435 +        uint32_t neg_ctx_off = SMB2_HDR_SIZE + 64 +
      436 +            P2ROUNDUP(sr->sr_cfg->skc_negtok_len, 8);
      437 +        uint32_t rc;
      438 +
      439 +        sr->reply.chain_offset = P2ROUNDUP(sr->reply.chain_offset, 8);
      440 +
      441 +        ASSERT3S(neg_ctx_off, ==, sr->reply.chain_offset);
      442 +
      443 +        (void) random_get_pseudo_bytes(salt, sizeof (salt));
      444 +
      445 +        rc = smb_mbc_encodef(
      446 +            &sr->reply, "ww4.",
      447 +            SMB31_PREAUTH_INTEGRITY_CAPS,
      448 +            preauth_ctx_len
      449 +            /* 4. */); /* reserverd */
      450 +
      451 +        ASSERT0(rc);
      452 +        rc = smb_mbc_encodef(
      453 +            &sr->reply, "www#c",
      454 +            1,                          /* hash algo count */
      455 +            salt_len,                   /* salt length */
      456 +            s->smb31_preauth_hashid,    /* hash id */
      457 +            salt_len,                   /* salt length */
      458 +            salt);
      459 +
      460 +        /* aligned on 8-bytes boundary */
      461 +        if (rc != 0 || s->smb31_enc_cipherid == 0) {
      462 +                cmn_err(CE_NOTE, "Encryption is not supported");
      463 +                return (rc);
      464 +        }
      465 +
      466 +        if (sr->reply.chain_offset % 8 != 0) {
      467 +                sr->reply.chain_offset = P2ROUNDUP(sr->reply.chain_offset, 8);
      468 +        }
      469 +
      470 +        rc = smb_mbc_encodef(
      471 +            &sr->reply, "ww4.",
      472 +            SMB31_ENCRYPTION_CAPS,
      473 +            enc_ctx_len
      474 +            /* 4. */); /* reserverd */
      475 +
      476 +        rc = smb_mbc_encodef(
      477 +            &sr->reply, "ww",
      478 +            1,                          /* cipher count */
      479 +            s->smb31_enc_cipherid);     /* encrypt. cipher id */
      480 +
      481 +        return (rc);
      482 +}
      483 +
 213  484  int
 214  485  smb2_newrq_negotiate(smb_request_t *sr)
 215  486  {
 216  487          smb_session_t *s = sr->session;
 217  488          int rc;
 218  489          uint32_t status = 0;
 219  490          uint16_t struct_size;
 220  491          uint16_t best_version;
 221  492          uint16_t version_cnt;
 222  493          uint16_t cl_versions[SMB2_NEGOTIATE_MAX_DIALECTS];
↓ open down ↓ 33 lines elided ↑ open up ↑
 256  527           * Be somewhat tolerant while decoding the variable part
 257  528           * so we can return errors instead of dropping the client.
 258  529           * Will limit decoding to the size of cl_versions here,
 259  530           * and do the error checks on version_cnt after the
 260  531           * dtrace start probe.
 261  532           */
 262  533          if (version_cnt > 0 &&
 263  534              version_cnt <= SMB2_NEGOTIATE_MAX_DIALECTS &&
 264  535              smb_mbc_decodef(&sr->command, "#w", version_cnt,
 265  536              cl_versions) != 0) {
 266      -            /* decode error; force an error below */
 267      -            version_cnt = 0;
      537 +                /* decode error; force an error below */
      538 +                version_cnt = 0;
 268  539          }
 269  540  
 270  541          DTRACE_SMB2_START(op__Negotiate, smb_request_t *, sr);
 271  542  
 272  543          sr->smb2_hdr_flags |= SMB2_FLAGS_SERVER_TO_REDIR;
 273  544          (void) smb2_encode_header(sr, B_FALSE);
 274  545  
 275  546          /*
 276  547           * [MS-SMB2] 3.3.5.2.4 Verifying the Signature
 277  548           * "If the SMB2 header of the SMB2 NEGOTIATE request has the
↓ open down ↓ 24 lines elided ↑ open up ↑
 302  573           *
 303  574           * [MS-SMB2] 3.3.5.4 Receiving an SMB2 NEGOTIATE Request
 304  575           * "If a common dialect is not found, the server MUST fail
 305  576           * the request with STATUS_NOT_SUPPORTED."
 306  577           */
 307  578          best_version = smb2_find_best_dialect(s, cl_versions, version_cnt);
 308  579          if (best_version == 0) {
 309  580                  status = NT_STATUS_NOT_SUPPORTED;
 310  581                  goto errout;
 311  582          }
      583 +        if (best_version >= 0x311) {
      584 +                if ((status = smb31_decode_neg_ctxs(sr, s)) != 0)
      585 +                        goto errout;
      586 +        }
      587 +
 312  588          s->dialect = best_version;
 313  589  
 314  590          /* Allow normal SMB2 requests now. */
 315  591          s->s_state = SMB_SESSION_STATE_NEGOTIATED;
 316  592          s->newrq_func = smb2sr_newrq;
 317  593  
 318  594          if (smb2_negotiate_common(sr, best_version) != 0)
 319  595                  status = NT_STATUS_INTERNAL_ERROR;
 320  596  
 321  597  errout:
 322  598          sr->smb2_status = status;
 323  599          DTRACE_SMB2_DONE(op__Negotiate, smb_request_t *, sr);
 324  600  
 325  601          if (sr->smb2_status != 0)
 326  602                  smb2sr_put_error(sr, sr->smb2_status);
 327  603          (void) smb2_encode_header(sr, B_TRUE);
 328  604  
      605 +        if (s->dialect >= SMB_VERS_3_11) {
      606 +                ASSERT3U(s->smb31_preauth_hashid, !=, 0);
      607 +                (void) smb31_preauth_sha512_calc(sr, &sr->reply,
      608 +                    s->smb31_preauth_hashval);
      609 +        }
      610 +
 329  611          smb2_send_reply(sr);
 330  612  
 331  613          return (rc);
 332  614  }
 333  615  
 334  616  /*
 335  617   * Common parts of SMB2 Negotiate, used for both the
 336  618   * SMB1-to-SMB2 style, and straight SMB2 style.
 337  619   * Do negotiation decisions and encode the reply.
 338  620   * The caller does the network send.
↓ open down ↓ 1 lines elided ↑ open up ↑
 340  622   * Return value is 0 for success, else error.
 341  623   */
 342  624  static int
 343  625  smb2_negotiate_common(smb_request_t *sr, uint16_t version)
 344  626  {
 345  627          timestruc_t boot_tv, now_tv;
 346  628          smb_session_t *s = sr->session;
 347  629          int rc;
 348  630          uint32_t max_rwsize;
 349  631          uint16_t secmode;
      632 +        uint16_t neg_ctx_cnt = 0;
      633 +        uint32_t neg_ctx_off = 0;
 350  634  
 351  635          /*
 352  636           * Negotiation itself.  First the Security Mode.
 353  637           */
 354  638          secmode = SMB2_NEGOTIATE_SIGNING_ENABLED;
 355  639          if (sr->sr_cfg->skc_signing_required)
 356  640                  secmode |= SMB2_NEGOTIATE_SIGNING_REQUIRED;
 357  641          s->srv_secmode = secmode;
 358  642  
 359  643          s->cmd_max_bytes = smb2_tcp_bufsize;
↓ open down ↓ 12 lines elided ↑ open up ↑
 372  656          boot_tv.tv_nsec = 0;
 373  657          now_tv.tv_sec = gethrestime_sec();
 374  658          now_tv.tv_nsec = 0;
 375  659  
 376  660          /*
 377  661           * If the version is 0x2FF, we haven't completed negotiate.
 378  662           * Don't initialize until we have our final request.
 379  663           */
 380  664          if (version != 0x2FF)
 381  665                  smb2_sign_init_mech(s);
      666 +        if (version >= 0x311)
      667 +                smb31_preauth_init_mech(s);
 382  668  
 383  669          /*
 384  670           * [MS-SMB2] 3.3.5.4 Receiving an SMB2 NEGOTIATE Request
 385  671           *
 386  672           * The SMB2.x capabilities are returned without regard for
 387  673           * what capabilities the client provided in the request.
 388  674           * The SMB3.x capabilities returned are the traditional
 389  675           * logical AND of server and client capabilities.
 390  676           *
 391  677           * One additional check: If KCF is missing something we
↓ open down ↓ 5 lines elided ↑ open up ↑
 397  683          } else if (s->dialect < SMB_VERS_3_0) {
 398  684                  /* SMB 2.x */
 399  685                  s->srv_cap = smb2srv_capabilities & SMB_2X_CAPS;
 400  686          } else {
 401  687                  /* SMB 3.0 or later */
 402  688                  s->srv_cap = smb2srv_capabilities &
 403  689                      (SMB_2X_CAPS | s->capabilities);
 404  690                  if ((s->srv_cap & SMB2_CAP_ENCRYPTION) != 0 &&
 405  691                      smb3_encrypt_init_mech(s) != 0) {
 406  692                          s->srv_cap &= ~SMB2_CAP_ENCRYPTION;
      693 +                        s->smb31_enc_cipherid = 0;
      694 +                }
      695 +
      696 +                if (s->dialect >= SMB_VERS_3_11) {
      697 +                        neg_ctx_cnt = s->smb31_enc_cipherid == 0 ? 1 : 2;
      698 +                        neg_ctx_off = SMB2_HDR_SIZE + 64 +
      699 +                            P2ROUNDUP(sr->sr_cfg->skc_negtok_len, 8);
      700 +
      701 +                        ASSERT3U(s->smb31_preauth_hashid, !=, 0);
      702 +                        (void) smb31_preauth_sha512_calc(sr, &sr->command,
      703 +                            s->smb31_preauth_hashval);
 407  704                  }
 408  705          }
 409  706  
 410  707          /*
 411  708           * See notes above smb2_max_rwsize, smb2_old_rwsize
 412  709           */
 413  710          if (s->capabilities & SMB2_CAP_LARGE_MTU)
 414  711                  max_rwsize = smb2_max_rwsize;
 415  712          else
 416  713                  max_rwsize = smb2_old_rwsize;
 417  714  
 418  715          rc = smb_mbc_encodef(
 419  716              &sr->reply,
 420  717              "wwww#cllllTTwwl#c",
 421  718              65, /* StructSize */        /* w */
 422  719              s->srv_secmode,             /* w */
 423  720              version,                    /* w */
 424      -            0, /* reserved */           /* w */
      721 +            neg_ctx_cnt,                /* w */
 425  722              UUID_LEN,                   /* # */
 426  723              &s->s_cfg.skc_machine_uuid, /* c */
 427  724              s->srv_cap,                 /* l */
 428  725              smb2_max_trans,             /* l */
 429  726              max_rwsize,                 /* l */
 430  727              max_rwsize,                 /* l */
 431  728              &now_tv,                    /* T */
 432  729              &boot_tv,                   /* T */
 433  730              128, /* SecBufOff */        /* w */
 434  731              sr->sr_cfg->skc_negtok_len, /* w */
 435      -            0,  /* reserved */          /* l */
      732 +            neg_ctx_off,                /* l */
 436  733              sr->sr_cfg->skc_negtok_len, /* # */
 437  734              sr->sr_cfg->skc_negtok);    /* c */
      735 +
      736 +
      737 +
      738 +        if (neg_ctx_cnt) {
      739 +                rc = smb31_encode_neg_ctxs(sr, s);
      740 +        }
 438  741  
 439  742          /* smb2_send_reply(sr); in caller */
 440  743  
 441  744          (void) ksocket_setsockopt(s->sock, SOL_SOCKET,
 442  745              SO_SNDBUF, (const void *)&smb2_tcp_bufsize,
 443  746              sizeof (smb2_tcp_bufsize), CRED());
 444  747          (void) ksocket_setsockopt(s->sock, SOL_SOCKET,
 445  748              SO_RCVBUF, (const void *)&smb2_tcp_bufsize,
 446  749              sizeof (smb2_tcp_bufsize), CRED());
 447  750  
↓ open down ↓ 85 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX