Coverage Report

Created: 2026-04-01 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/lib/util_namearray.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   Samba utility functions
4
   Copyright (C) Ralph Boehme 2024
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 "lib/util/strv.h"
22
#include "libcli/security/security.h"
23
#include "source3/lib/substitute.h"
24
#include "passdb/lookup_sid.h"
25
#include "auth.h"
26
27
/*
28
 * No prefix means direct username
29
 * @name means netgroup first, then unix group
30
 * &name means netgroup
31
 * +name means unix group
32
 * + and & may be combined
33
 */
34
35
static bool do_group_checks(const char **name, const char **pattern)
36
0
{
37
0
  if ((*name)[0] == '@') {
38
0
    *pattern = "&+";
39
0
    *name += 1;
40
0
    return True;
41
0
  }
42
43
0
  if (((*name)[0] == '+') && ((*name)[1] == '&')) {
44
0
    *pattern = "+&";
45
0
    *name += 2;
46
0
    return True;
47
0
  }
48
49
0
  if ((*name)[0] == '+') {
50
0
    *pattern = "+";
51
0
    *name += 1;
52
0
    return True;
53
0
  }
54
55
0
  if (((*name)[0] == '&') && ((*name)[1] == '+')) {
56
0
    *pattern = "&+";
57
0
    *name += 2;
58
0
    return True;
59
0
  }
60
61
0
  if ((*name)[0] == '&') {
62
0
    *pattern = "&";
63
0
    *name += 1;
64
0
    return True;
65
0
  }
66
67
0
  return False;
68
0
}
69
70
bool token_contains_name(TALLOC_CTX *mem_ctx,
71
       const char *username,
72
       const char *domain,
73
       const char *sharename,
74
       const struct security_token *token,
75
       const char *name,
76
       bool *match)
77
0
{
78
0
  const char *prefix;
79
0
  struct dom_sid sid;
80
0
  enum lsa_SidType type;
81
0
  NTSTATUS status;
82
83
0
  *match = false;
84
85
0
  if (username != NULL) {
86
0
    size_t domain_len = domain != NULL ? strlen(domain) : 0;
87
88
    /* Check if username starts with domain name */
89
0
    if (domain_len > 0) {
90
0
      const char *sep = lp_winbind_separator();
91
0
      int cmp = strncasecmp_m(username, domain, domain_len);
92
0
      if (cmp == 0 && sep[0] == username[domain_len]) {
93
        /* Move after the winbind separator */
94
0
        domain_len += 1;
95
0
      } else {
96
0
        domain_len = 0;
97
0
      }
98
0
    }
99
0
    name = talloc_sub_basic(mem_ctx,
100
0
          username + domain_len,
101
0
          domain,
102
0
          name);
103
0
  }
104
0
  if (sharename != NULL) {
105
0
    name = talloc_string_sub(mem_ctx, name, "%S", sharename);
106
0
  }
107
108
0
  if (name == NULL) {
109
0
    return false;
110
0
  }
111
112
0
  if ( string_to_sid( &sid, name ) ) {
113
0
    DEBUG(5,("token_contains_name: Checking for SID [%s] in token\n", name));
114
0
    *match = nt_token_check_sid( &sid, token );
115
0
    return true;
116
0
  }
117
118
0
  if (!do_group_checks(&name, &prefix)) {
119
0
    status = lookup_name_smbconf_ex(mem_ctx,
120
0
            name,
121
0
            LOOKUP_NAME_ALL,
122
0
            NULL,
123
0
            NULL,
124
0
            &sid,
125
0
            &type);
126
0
    if (!NT_STATUS_IS_OK(status)) {
127
0
      DBG_ERR("lookup_name '%s' failed %s\n",
128
0
        name, nt_errstr(status));
129
0
      return false;
130
0
    }
131
0
    if (type != SID_NAME_USER) {
132
0
      DBG_WARNING("%s is a %s, expected a user\n",
133
0
            name, sid_type_lookup(type));
134
0
      return true;
135
0
    }
136
0
    *match = nt_token_check_sid(&sid, token);
137
0
    return true;
138
0
  }
139
140
0
  for (/* initialized above */ ; *prefix != '\0'; prefix++) {
141
0
    if (*prefix == '+') {
142
0
      status = lookup_name_smbconf_ex(
143
0
          mem_ctx,
144
0
          name,
145
0
          LOOKUP_NAME_ALL|LOOKUP_NAME_GROUP,
146
0
          NULL,
147
0
          NULL,
148
0
          &sid,
149
0
          &type);
150
0
      if (!NT_STATUS_IS_OK(status)) {
151
0
        DBG_ERR("lookup_name '%s' failed %s\n",
152
0
          name, nt_errstr(status));
153
0
        return false;
154
0
      }
155
0
      if ((type != SID_NAME_DOM_GRP) &&
156
0
          (type != SID_NAME_ALIAS) &&
157
0
          (type != SID_NAME_WKN_GRP)) {
158
0
        DBG_WARNING("%s is a %s, expected a group\n",
159
0
              name, sid_type_lookup(type));
160
0
        return true;
161
0
      }
162
0
      if (nt_token_check_sid(&sid, token)) {
163
0
        *match = true;
164
0
        return True;
165
0
      }
166
0
      continue;
167
0
    }
168
0
    if (*prefix == '&') {
169
0
      if (username) {
170
0
        if (user_in_netgroup(mem_ctx, username, name)) {
171
0
          *match = true;
172
0
          return True;
173
0
        }
174
0
      }
175
0
      continue;
176
0
    }
177
0
    smb_panic("got invalid prefix from do_groups_check");
178
0
  }
179
0
  return true;
180
0
}
181
182
static size_t namearray_len(const struct name_compare_entry *array)
183
0
{
184
0
  size_t i = 0;
185
186
0
  while (array[i].name != NULL) {
187
0
    i += 1;
188
0
  }
189
190
0
  return i;
191
0
}
192
193
/*******************************************************************
194
 Strip a '/' separated list into an array of
195
 name_compare_entry structures suitable for
196
 passing to is_in_path(). We do this for
197
 speed so we can pre-parse all the names in the list
198
 and don't do it for each call to is_in_path().
199
 We also check if the entry contains a wildcard to
200
 remove a potentially expensive call to mask_match
201
 if possible.
202
********************************************************************/
203
204
bool append_to_namearray(TALLOC_CTX *mem_ctx,
205
       const char *namelist_in,
206
       struct name_compare_entry **_name_array)
207
0
{
208
0
  struct name_compare_entry *name_array = *_name_array;
209
0
  size_t len;
210
0
  char *namelist = NULL;
211
0
  const char *p = NULL;
212
213
0
  if ((namelist_in == NULL) || (namelist_in[0] == '\0')) {
214
0
    return true;
215
0
  }
216
217
0
  if (name_array == NULL) {
218
0
    name_array = talloc_zero(mem_ctx, struct name_compare_entry);
219
0
    if (name_array == NULL) {
220
0
      return false;
221
0
    }
222
0
  }
223
0
  len = namearray_len(name_array);
224
225
0
  namelist = path_to_strv(name_array, namelist_in);
226
0
  if (namelist == NULL) {
227
0
    DBG_ERR("path_to_strv failed\n");
228
0
    return false;
229
0
  }
230
231
0
  while ((p = strv_next(namelist, p)) != NULL) {
232
0
    struct name_compare_entry *tmp = NULL;
233
234
0
    if (*p == '\0') {
235
      /* cope with multiple (useless) /s) */
236
0
      continue;
237
0
    }
238
239
0
    tmp = talloc_realloc(mem_ctx,
240
0
             name_array,
241
0
             struct name_compare_entry,
242
0
             len + 2);
243
0
    if (tmp == NULL) {
244
0
      return false;
245
0
    }
246
0
    name_array = tmp;
247
248
0
    name_array[len] = (struct name_compare_entry){
249
0
      .name = p,
250
0
      .is_wild = ms_has_wild(p),
251
0
    };
252
0
    name_array[len + 1] = (struct name_compare_entry){};
253
0
    len += 1;
254
0
  }
255
256
0
  *_name_array = name_array;
257
0
  return true;
258
0
}
259
260
bool set_namearray(TALLOC_CTX *mem_ctx,
261
       const char *namelist_in,
262
       struct name_compare_entry **_name_array)
263
0
{
264
0
  bool ret;
265
266
0
  *_name_array = NULL;
267
268
0
  ret = append_to_namearray(mem_ctx, namelist_in, _name_array);
269
0
  return ret;
270
0
}