LCOV - code coverage report
Current view: top level - source4/torture/unix - whoami.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 134 187 71.7 %
Date: 2024-04-21 15:09:00 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :    Test the SMB_WHOAMI Unix extension.
       3             : 
       4             :    Copyright (C) 2007 James Peach
       5             : 
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : #include "includes.h"
      21             : #include "libcli/libcli.h"
      22             : #include "libcli/raw/raw_proto.h"
      23             : #include "torture/torture.h"
      24             : #include "torture/unix/proto.h"
      25             : #include "lib/cmdline/cmdline.h"
      26             : #include "auth/credentials/credentials.h"
      27             : #include "param/param.h"
      28             : #include "libcli/resolve/resolve.h"
      29             : #include <ldb.h>
      30             : #include "lib/util/util_ldb.h"
      31             : #include "ldb_wrap.h"
      32             : #include "dsdb/samdb/samdb.h"
      33             : #include "../libcli/security/security.h"
      34             : 
      35             : #undef strcasecmp
      36             : 
      37             : /* Size (in bytes) of the required fields in the SMBwhoami response. */
      38             : #define WHOAMI_REQUIRED_SIZE    40
      39             : 
      40             : /*
      41             :    SMBWhoami - Query the user mapping performed by the server for the
      42             :    connected tree. This is a subcommand of the TRANS2_QFSINFO.
      43             : 
      44             :    Returns:
      45             :        4 bytes unsigned -      mapping flags (smb_whoami_flags)
      46             :        4 bytes unsigned -      flags mask
      47             : 
      48             :        8 bytes unsigned -      primary UID
      49             :        8 bytes unsigned -      primary GID
      50             :        4 bytes unsigned -      number of supplementary GIDs
      51             :        4 bytes unsigned -      number of SIDs
      52             :        4 bytes unsigned -      SID list byte count
      53             :        4 bytes -               pad / reserved (must be zero)
      54             : 
      55             :        8 bytes unsigned[] -    list of GIDs (may be empty)
      56             :        struct dom_sid[] -             list of SIDs (may be empty)
      57             : */
      58             : 
      59             : struct smb_whoami
      60             : {
      61             :         uint32_t        mapping_flags;
      62             :         uint32_t        mapping_mask;
      63             :         uint64_t        server_uid;
      64             :         uint64_t        server_gid;
      65             :         uint32_t        num_gids;
      66             :         uint32_t        num_sids;
      67             :         uint32_t        num_sid_bytes;
      68             :         uint32_t        reserved; /* Must be zero */
      69             :         uint64_t *      gid_list;
      70             :         struct dom_sid ** sid_list;
      71             : };
      72             : 
      73          32 : static struct smbcli_state *connect_to_server(struct torture_context *tctx,
      74             :                 struct cli_credentials *creds)
      75             : {
      76           0 :         NTSTATUS status;
      77           0 :         struct smbcli_state *cli;
      78             : 
      79          32 :         const char *host = torture_setting_string(tctx, "host", NULL);
      80          32 :         const char *share = torture_setting_string(tctx, "share", NULL);
      81           0 :         struct smbcli_options options;
      82           0 :         struct smbcli_session_options session_options;
      83             : 
      84          32 :         lpcfg_smbcli_options(tctx->lp_ctx, &options);
      85          32 :         lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options);
      86             : 
      87          32 :         status = smbcli_full_connection(tctx, &cli, host, 
      88             :                                         lpcfg_smb_ports(tctx->lp_ctx),
      89             :                                         share, NULL, lpcfg_socket_options(tctx->lp_ctx),
      90             :                                         creds, lpcfg_resolve_context(tctx->lp_ctx),
      91             :                                         tctx->ev, &options, &session_options,
      92             :                                         lpcfg_gensec_settings(tctx, tctx->lp_ctx));
      93             : 
      94          32 :         if (!NT_STATUS_IS_OK(status)) {
      95          16 :                 torture_comment(tctx,
      96             :                                 "FATAL: Failed to connect to //%s/%s "
      97             :                                 "with %s - %s\n",
      98             :                                 host,
      99             :                                 share,
     100             :                                 cli_credentials_get_username(creds),
     101             :                                 nt_errstr(status));
     102          16 :                 return NULL;
     103             :         }
     104             : 
     105          16 :         return cli;
     106             : }
     107             : 
     108         180 : static bool whoami_sid_parse(void *mem_ctx,
     109             :                 struct torture_context *torture,
     110             :                 DATA_BLOB *data, size_t *offset,
     111             :                 struct dom_sid **psid)
     112             : {
     113         180 :         size_t remain = data->length - *offset;
     114           0 :         int i;
     115             : 
     116         180 :         *psid = talloc_zero(mem_ctx, struct dom_sid);
     117         180 :         torture_assert(torture, *psid != NULL, "out of memory");
     118             : 
     119         180 :         torture_assert(torture, remain >= 8,
     120             :                         "invalid SID format");
     121             : 
     122         180 :         (*psid)->sid_rev_num = CVAL(data->data, *offset);
     123         180 :         (*psid)->num_auths = CVAL(data->data, *offset + 1);
     124         180 :         memcpy((*psid)->id_auth, data->data + *offset + 2, 6);
     125             : 
     126         180 :         (*offset) += 8;
     127         180 :         remain = data->length - *offset;
     128             : 
     129         180 :         torture_assert(torture, remain >= ((*psid)->num_auths * 4),
     130             :                         "invalid sub_auth byte count");
     131         180 :         torture_assert(torture, (*psid)->num_auths >= 0,
     132             :                         "invalid sub_auth value");
     133         180 :         torture_assert(torture, (*psid)->num_auths <= 15,
     134             :                         "invalid sub_auth value");
     135             : 
     136         678 :         for (i = 0; i < (*psid)->num_auths; i++) {
     137         498 :                 (*psid)->sub_auths[i] = IVAL(data->data, *offset);
     138         498 :                 (*offset) += 4;
     139             :         }
     140             : 
     141         180 :         return true;
     142             : }
     143             : 
     144          30 : static bool smb_raw_query_posix_whoami(void *mem_ctx,
     145             :                                 struct torture_context *torture,
     146             :                                 struct smbcli_state *cli,
     147             :                                 struct smb_whoami *whoami,
     148             :                                 unsigned max_data)
     149             : {
     150           0 :         struct smb_trans2 tp;
     151           0 :         NTSTATUS status;
     152           0 :         size_t offset;
     153           0 :         int i;
     154             : 
     155          30 :         uint16_t setup = TRANSACT2_QFSINFO;
     156           0 :         uint16_t info_level;
     157             : 
     158          30 :         ZERO_STRUCTP(whoami);
     159             : 
     160          30 :         tp.in.max_setup = 0;
     161          30 :         tp.in.flags = 0;
     162          30 :         tp.in.timeout = 0;
     163          30 :         tp.in.setup_count = 1;
     164          30 :         tp.in.max_param = 10;
     165          30 :         tp.in.max_data = (uint16_t)max_data;
     166          30 :         tp.in.setup = &setup;
     167          30 :         tp.in.trans_name = NULL;
     168          30 :         SSVAL(&info_level, 0, SMB_QFS_POSIX_WHOAMI);
     169          30 :         tp.in.params = data_blob_talloc(mem_ctx, &info_level, 2);
     170          30 :         tp.in.data = data_blob_talloc(mem_ctx, NULL, 0);
     171             : 
     172          30 :         status = smb_raw_trans2(cli->tree, mem_ctx, &tp);
     173          30 :         torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
     174             :                         "doing SMB_QFS_POSIX_WHOAMI");
     175             : 
     176             :         /* Make sure we got back all the required fields. */
     177          30 :         torture_assert(torture, tp.out.params.length == 0,
     178             :                         "trans2 params should be empty");
     179          30 :         torture_assert(torture, tp.out.data.length >= WHOAMI_REQUIRED_SIZE,
     180             :                         "checking for required response fields");
     181             : 
     182          30 :         whoami->mapping_flags = IVAL(tp.out.data.data, 0);
     183          30 :         whoami->mapping_mask = IVAL(tp.out.data.data, 4);
     184          30 :         whoami->server_uid = BVAL(tp.out.data.data, 8);
     185          30 :         whoami->server_gid = BVAL(tp.out.data.data, 16);
     186          30 :         whoami->num_gids = IVAL(tp.out.data.data, 24);
     187          30 :         whoami->num_sids = IVAL(tp.out.data.data, 28);
     188          30 :         whoami->num_sid_bytes = IVAL(tp.out.data.data, 32);
     189          30 :         whoami->reserved = IVAL(tp.out.data.data, 36);
     190             : 
     191             :         /* The GID list and SID list are optional, depending on the count
     192             :          * and length fields.
     193             :          */
     194          30 :         if (whoami->num_sids != 0) {
     195          16 :                 torture_assert(torture, whoami->num_sid_bytes != 0,
     196             :                                 "SID count does not match byte count");
     197             :         }
     198             : 
     199          30 :         printf("\tmapping_flags=0x%08x mapping_mask=0x%08x\n",
     200             :                         whoami->mapping_flags, whoami->mapping_mask);
     201          30 :         printf("\tserver UID=%llu GID=%llu\n",
     202          30 :                (unsigned long long)whoami->server_uid, (unsigned long long)whoami->server_gid);
     203          30 :         printf("\t%u GIDs, %u SIDs, %u SID bytes\n",
     204             :                         whoami->num_gids, whoami->num_sids,
     205             :                         whoami->num_sid_bytes);
     206             : 
     207          30 :         offset = WHOAMI_REQUIRED_SIZE;
     208             : 
     209          30 :         torture_assert_int_equal(torture, whoami->reserved, 0,
     210             :                         "invalid reserved field");
     211             : 
     212          30 :         if (tp.out.data.length == offset) {
     213             :                 /* No SIDs or GIDs returned */
     214          14 :                 torture_assert_int_equal(torture, whoami->num_gids, 0,
     215             :                                 "invalid GID count");
     216          14 :                 torture_assert_int_equal(torture, whoami->num_sids, 0,
     217             :                                 "invalid SID count");
     218          14 :                 torture_assert_int_equal(torture, whoami->num_sid_bytes, 0,
     219             :                                 "invalid SID byte count");
     220          14 :                 return true;
     221             :         }
     222             : 
     223          16 :         if (whoami->num_gids != 0) {
     224          16 :                 int remain = tp.out.data.length - offset;
     225          16 :                 int gid_bytes = whoami->num_gids * 8;
     226             : 
     227          16 :                 if (whoami->num_sids == 0) {
     228           0 :                         torture_assert_int_equal(torture, remain, gid_bytes,
     229             :                                         "GID count does not match data length");
     230             :                 } else {
     231          16 :                         torture_assert(torture, remain > gid_bytes,
     232             :                                                 "invalid GID count");
     233             :                 }
     234             : 
     235          16 :                 whoami->gid_list = talloc_array(mem_ctx, uint64_t, whoami->num_gids);
     236          16 :                 torture_assert(torture, whoami->gid_list != NULL, "out of memory");
     237             : 
     238          16 :                 torture_comment(torture, "\tGIDs:\n");
     239             :                 
     240         168 :                 for (i = 0; i < whoami->num_gids; ++i) {
     241         152 :                         whoami->gid_list[i] = BVAL(tp.out.data.data, offset);
     242         152 :                         offset += 8;
     243         152 :                         torture_comment(torture, "\t\t%u\n", (unsigned int)whoami->gid_list[i]);
     244             :                 }
     245             :         }
     246             : 
     247             :         /* Check if there should be data left for the SID list. */
     248          16 :         if (tp.out.data.length == offset) {
     249           0 :                 torture_assert_int_equal(torture, whoami->num_sids, 0,
     250             :                                 "invalid SID count");
     251           0 :                 return true;
     252             :         }
     253             : 
     254             :         /* All the remaining bytes must be the SID list. */
     255          16 :         torture_assert_int_equal(torture,
     256             :                 whoami->num_sid_bytes, (tp.out.data.length - offset),
     257             :                 "invalid SID byte count");
     258             : 
     259          16 :         if (whoami->num_sids != 0) {
     260             : 
     261          16 :                 whoami->sid_list = talloc_array(mem_ctx, struct dom_sid *,
     262             :                                 whoami->num_sids);
     263          16 :                 torture_assert(torture, whoami->sid_list != NULL,
     264             :                                 "out of memory");
     265             : 
     266          16 :                 torture_comment(torture, "\tSIDs:\n");
     267             : 
     268         196 :                 for (i = 0; i < whoami->num_sids; ++i) {
     269         180 :                         if (!whoami_sid_parse(mem_ctx, torture,
     270             :                                         &tp.out.data, &offset,
     271         180 :                                         &whoami->sid_list[i])) {
     272           0 :                                 return false;
     273             :                         }
     274             : 
     275         180 :                         torture_comment(torture, "\t\t%s\n",
     276         180 :                                         dom_sid_string(torture, whoami->sid_list[i]));
     277             :                 }
     278             :         }
     279             : 
     280             :         /* We should be at the end of the response now. */
     281          16 :         torture_assert_int_equal(torture, tp.out.data.length, offset,
     282             :                         "trailing garbage bytes");
     283             : 
     284          16 :         return true;
     285             : }
     286             : 
     287           8 : static bool test_against_ldap(struct torture_context *torture, struct ldb_context *ldb, bool is_dc, 
     288             :                               struct smb_whoami *whoami)
     289             : {
     290           0 :         struct ldb_message *msg;
     291           0 :         struct ldb_message_element *el;
     292             : 
     293           8 :         const char *attrs[] = { "tokenGroups", NULL };
     294           0 :         int i;
     295             : 
     296           8 :         torture_assert_int_equal(torture, dsdb_search_one(ldb, torture, &msg, NULL, LDB_SCOPE_BASE, attrs, 0, NULL), LDB_SUCCESS, "searching for tokenGroups");
     297           8 :         el = ldb_msg_find_element(msg, "tokenGroups");
     298           8 :         torture_assert(torture, el, "obtaining tokenGroups");
     299           8 :         torture_assert(torture, el->num_values > 0, "Number of SIDs from LDAP needs to be more than 0");
     300           8 :         torture_assert(torture, whoami->num_sids > 0, "Number of SIDs from LDAP needs to be more than 0");
     301             :         
     302           8 :         if (is_dc) {
     303           8 :                 torture_assert_int_equal(torture, el->num_values, whoami->num_sids, "Number of SIDs from LDAP and number of SIDs from CIFS does not match!");
     304             :                 
     305          90 :                 for (i = 0; i < el->num_values; i++) {
     306          84 :                         struct dom_sid *sid = talloc(torture, struct dom_sid);
     307           0 :                         ssize_t ret;
     308          84 :                         torture_assert(torture, sid != NULL, "talloc failed");
     309             : 
     310          84 :                         ret = sid_parse(el->values[i].data,
     311          84 :                                         el->values[i].length, sid);
     312          84 :                         torture_assert(torture,
     313             :                                        ret != -1,
     314             :                                        "sid parse failed");
     315          84 :                         torture_assert_str_equal(torture, dom_sid_string(sid, sid), dom_sid_string(sid, whoami->sid_list[i]), "SID from LDAP and SID from CIFS does not match!");
     316          84 :                         talloc_free(sid);
     317             :                 }
     318             :         } else {
     319           0 :                 unsigned int num_domain_sids_dc = 0, num_domain_sids_member = 0;
     320           0 :                 struct dom_sid *user_sid = talloc(torture, struct dom_sid);
     321           0 :                 struct dom_sid *dom_sid = talloc(torture, struct dom_sid);
     322           0 :                 struct dom_sid *dc_sids = talloc_array(torture, struct dom_sid, el->num_values);
     323           0 :                 struct dom_sid *member_sids = talloc_array(torture, struct dom_sid, whoami->num_sids);
     324           0 :                 ssize_t ret;
     325           0 :                 torture_assert(torture, user_sid != NULL, "talloc failed");
     326           0 :                 ret = sid_parse(el->values[0].data,
     327           0 :                                 el->values[0].length,
     328             :                                 user_sid);
     329           0 :                 torture_assert(torture,
     330             :                                ret != -1,
     331             :                                "sid parse failed");
     332           0 :                 torture_assert_ntstatus_equal(torture, dom_sid_split_rid(torture, user_sid, &dom_sid, NULL), NT_STATUS_OK, "failed to split domain SID from user SID");
     333           0 :                 for (i = 0; i < el->num_values; i++) {
     334           0 :                         struct dom_sid *sid = talloc(dc_sids, struct dom_sid);
     335           0 :                         torture_assert(torture, sid != NULL, "talloc failed");
     336             : 
     337           0 :                         ret = sid_parse(el->values[i].data,
     338           0 :                                         el->values[i].length,
     339             :                                         sid);
     340           0 :                         torture_assert(torture,
     341             :                                        ret != -1,
     342             :                                        "sid parse failed");
     343           0 :                         if (dom_sid_in_domain(dom_sid, sid)) {
     344           0 :                                 dc_sids[num_domain_sids_dc] = *sid;
     345           0 :                                 num_domain_sids_dc++;
     346             :                         }
     347           0 :                         talloc_free(sid);
     348             :                 }
     349             : 
     350           0 :                 for (i = 0; i < whoami->num_sids; i++) {
     351           0 :                         if (dom_sid_in_domain(dom_sid, whoami->sid_list[i])) {
     352           0 :                                 member_sids[num_domain_sids_member] = *whoami->sid_list[i];
     353           0 :                                 num_domain_sids_member++;
     354             :                         }
     355             :                 }
     356             : 
     357           0 :                 torture_assert_int_equal(torture, num_domain_sids_dc, num_domain_sids_member, "Number of Domain SIDs from LDAP DC and number of SIDs from CIFS member does not match!");
     358           0 :                 for (i = 0; i < num_domain_sids_dc; i++) {
     359           0 :                         torture_assert_str_equal(torture, dom_sid_string(dc_sids, &dc_sids[i]), dom_sid_string(member_sids, &member_sids[i]), "Domain SID from LDAP DC and SID from CIFS member server does not match!");
     360             :                 }
     361           0 :                 talloc_free(dc_sids);
     362           0 :                 talloc_free(member_sids);
     363             :         }
     364           6 :         return true;
     365             : }
     366             : 
     367          32 : bool torture_unix_whoami(struct torture_context *torture)
     368             : {
     369           0 :         struct smbcli_state *cli;
     370           0 :         struct smb_whoami whoami;
     371          32 :         bool ret = false;
     372           0 :         struct ldb_context *ldb;
     373           0 :         const char *addc, *host;
     374             : 
     375          32 :         cli = connect_to_server(torture, samba_cmdline_get_creds());
     376          32 :         torture_assert(torture, cli, "connecting to server with authenticated credentials");
     377             : 
     378             :         /* Test basic authenticated mapping. */
     379          16 :         torture_assert_goto(torture, smb_raw_query_posix_whoami(torture, torture,
     380             :                                                        cli, &whoami, 0xFFFF), ret, fail,
     381             :                             "calling SMB_QFS_POSIX_WHOAMI on an authenticated connection");
     382             : 
     383             :         /* Check that our anonymous login mapped us to guest on the server, but
     384             :          * only if the server supports this.
     385             :          */
     386          16 :         if (whoami.mapping_mask & SMB_WHOAMI_GUEST) {
     387          16 :                 bool guest = whoami.mapping_flags & SMB_WHOAMI_GUEST;
     388          16 :                 torture_comment(torture, "checking whether we were logged in as guest... %s\n",
     389             :                         guest ? "YES" : "NO");
     390          16 :                 torture_assert(torture,
     391             :                         cli_credentials_is_anonymous(
     392             :                                 samba_cmdline_get_creds()) == guest,
     393             :                                "login did not credentials map to guest");
     394             :         } else {
     395           0 :                 torture_comment(torture, "server does not support SMB_WHOAMI_GUEST flag\n");
     396             :         }
     397             : 
     398          16 :         addc = torture_setting_string(torture, "addc", NULL);
     399          16 :         host = torture_setting_string(torture, "host", NULL);
     400             :         
     401          16 :         if (addc) {
     402           8 :                 ldb = ldb_wrap_connect(torture, torture->ev, torture->lp_ctx, talloc_asprintf(torture, "ldap://%s", addc),
     403             :                                        NULL, samba_cmdline_get_creds(), 0);
     404           8 :                 torture_assert(torture, ldb, "ldb connect failed");
     405             : 
     406             :                 /* We skip this testing if we could not contact the LDAP server */
     407           8 :                 if (!test_against_ldap(torture, ldb, strcasecmp(addc, host) == 0, &whoami)) {
     408           2 :                         goto fail;
     409             :                 }
     410             :         }
     411             : 
     412             :         /* Test that the server drops the UID and GID list. */
     413          14 :         torture_assert_goto(torture, smb_raw_query_posix_whoami(torture, torture,
     414             :                                                   cli, &whoami, 0x40), ret, fail,
     415             :                        "calling SMB_QFS_POSIX_WHOAMI with a small buffer\n");
     416             : 
     417          14 :         torture_assert_int_equal(torture, whoami.num_gids, 0,
     418             :                         "invalid GID count");
     419          14 :         torture_assert_int_equal(torture, whoami.num_sids, 0,
     420             :                         "invalid SID count");
     421          14 :         torture_assert_int_equal(torture, whoami.num_sid_bytes, 0,
     422             :                         "invalid SID bytes count");
     423             : 
     424          14 :         smbcli_tdis(cli);
     425             : 
     426          14 :         return true;
     427           2 : fail:
     428             : 
     429           2 :         smbcli_tdis(cli);
     430           2 :         return ret;
     431             : }
     432             : 
     433             : /* vim: set sts=8 sw=8 : */

Generated by: LCOV version 1.14