Coverage Report

Created: 2026-01-16 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proftpd/src/class.c
Line
Count
Source
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2003-2020 The ProFTPD Project team
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18
 *
19
 * As a special exemption, the ProFTPD Project team and other respective
20
 * copyright holders give permission to link this program with OpenSSL, and
21
 * distribute the resulting executable, without including the source code for
22
 * OpenSSL in the source distribution.
23
 */
24
25
/* Class routines */
26
27
#include "conf.h"
28
29
static const char *trace_channel = "class";
30
31
/* Store the defined Classes in a linked list.  If many Classes are defined,
32
 * this may need to be redefined to be a collision-chained hash.
33
 */
34
static pr_class_t *class_list = NULL;
35
static pr_class_t *curr_cls = NULL;
36
37
0
const pr_class_t *pr_class_get(const pr_class_t *prev) {
38
0
  if (prev != NULL) {
39
0
    return prev->cls_next;
40
0
  }
41
42
0
  if (class_list == NULL) {
43
0
    errno = ENOENT;
44
0
  }
45
46
0
  return class_list;
47
0
}
48
49
int pr_class_satisfied(pool *p, const pr_class_t *cls,
50
0
    const pr_netaddr_t *addr) {
51
0
  register unsigned int i;
52
0
  array_header *acl_list;
53
0
  const pr_netacl_t **acls;
54
0
  int next_class = FALSE;
55
56
0
  if (cls == NULL ||
57
0
      addr == NULL) {
58
0
    errno = EINVAL;
59
0
    return -1;
60
0
  }
61
62
0
  acl_list = cls->cls_acls;
63
0
  acls = acl_list->elts;
64
65
  /* For each ACL rule in this class, compare the rule against the given
66
   * address.  The address matches the given class depending on the
67
   * Satisfy setting: if "any", the class matches if any rule matches;
68
   * if "all", the class matches only if _all_ rules match.
69
   */
70
0
  for (i = 0; i < acl_list->nelts; i++) {
71
0
    int res;
72
73
0
    pr_signals_handle();
74
75
0
    if (next_class) {
76
0
      break;
77
0
    }
78
79
0
    if (acls[i] == NULL) {
80
0
      continue;
81
0
    }
82
83
0
    switch (cls->cls_satisfy) {
84
0
      case PR_CLASS_SATISFY_ANY:
85
0
        pr_trace_msg(trace_channel, 6,
86
0
          "checking addr '%s' (%s) against class '%s' rule: %s "
87
0
          "(requires any ACL matching)", pr_netaddr_get_ipstr(addr),
88
0
          pr_netaddr_get_dnsstr(addr), cls->cls_name,
89
0
          pr_netacl_get_str(p, acls[i]));
90
91
0
        res = pr_netacl_match(acls[i], addr);
92
0
        if (res == 1) {
93
0
          return TRUE;
94
0
        }
95
0
        break;
96
97
0
      case PR_CLASS_SATISFY_ALL:
98
0
        pr_trace_msg(trace_channel, 6,
99
0
          "checking addr '%s' (%s) against class '%s' ACL: %s "
100
0
          "(requires all ACLs matching)", pr_netaddr_get_ipstr(addr),
101
0
          pr_netaddr_get_dnsstr(addr), cls->cls_name,
102
0
          pr_netacl_get_str(p, acls[i]));
103
104
0
        res = pr_netacl_match(acls[i], addr);
105
0
        if (res <= 0) {
106
0
          next_class = TRUE;
107
0
        }
108
0
        break;
109
0
    }
110
0
  }
111
112
  /* If this is a "Satisfy all" class, and all rules have matched
113
   * (positively or negatively), then it matches the address.
114
   */
115
0
  if (next_class == FALSE &&
116
0
      cls->cls_satisfy == PR_CLASS_SATISFY_ALL &&
117
0
      i == acl_list->nelts) {
118
0
    return TRUE;
119
0
  }
120
121
0
  return FALSE;
122
0
}
123
124
0
const pr_class_t *pr_class_match_addr(const pr_netaddr_t *addr) {
125
0
  pr_class_t *cls = NULL, *iter;
126
0
  pool *tmp_pool;
127
128
0
  if (addr == NULL) {
129
0
    errno = EINVAL;
130
0
    return NULL;
131
0
  }
132
133
0
  tmp_pool = make_sub_pool(permanent_pool);
134
135
0
  for (iter = class_list; iter; iter = iter->cls_next) {
136
0
    int res;
137
138
0
    res = pr_class_satisfied(tmp_pool, iter, addr);
139
0
    if (res == TRUE) {
140
0
      cls = iter;
141
0
      break;
142
0
    }
143
0
  }
144
145
0
  destroy_pool(tmp_pool);
146
147
0
  if (cls == NULL) {
148
0
    errno = ENOENT;
149
0
  }
150
151
0
  return cls;
152
0
}
153
154
0
const pr_class_t *pr_class_find(const char *name) {
155
0
  pr_class_t *cls;
156
157
0
  if (name == NULL) {
158
0
    errno = EINVAL;
159
0
    return NULL;
160
0
  }
161
162
0
  for (cls = class_list; cls; cls = cls->cls_next) {
163
0
    pr_signals_handle();
164
0
    if (strcmp(cls->cls_name, name) == 0) {
165
0
      return cls;
166
0
    }
167
0
  }
168
169
0
  errno = ENOENT;
170
0
  return NULL;
171
0
}
172
173
0
int pr_class_add_acl(const pr_netacl_t *acl) {
174
175
0
  if (acl == NULL) {
176
0
    errno = EINVAL;
177
0
    return -1;
178
0
  }
179
180
0
  if (curr_cls == NULL) {
181
0
    errno = EPERM;
182
0
    return -1;
183
0
  }
184
185
  /* Add this ACL rule to the current Class. */
186
0
  if (curr_cls->cls_acls == NULL) {
187
0
    curr_cls->cls_acls = make_array(curr_cls->cls_pool, 1,
188
0
      sizeof(pr_netacl_t *));
189
0
  }
190
191
0
  *((pr_netacl_t **) push_array(curr_cls->cls_acls)) =
192
0
    pr_netacl_dup(curr_cls->cls_pool, acl);
193
194
0
  return 0;
195
0
}
196
197
0
int pr_class_set_satisfy(int satisfy) {
198
0
  if (curr_cls == NULL) {
199
0
    errno = EPERM;
200
0
    return -1;
201
0
  }
202
203
0
  if (satisfy != PR_CLASS_SATISFY_ANY &&
204
0
      satisfy != PR_CLASS_SATISFY_ALL) {
205
0
    errno = EINVAL;
206
0
    return -1;
207
0
  }
208
209
  /* Set the Satisfy flag on the current Class. */
210
0
  curr_cls->cls_satisfy = satisfy;
211
212
0
  return 0;
213
0
}
214
215
0
int pr_class_add_note(const char *key, void *value, size_t valuesz) {
216
0
  int res;
217
218
0
  if (key == NULL) {
219
0
    errno = EINVAL;
220
0
    return -1;
221
0
  }
222
223
0
  if (curr_cls == NULL) {
224
0
    errno = EPERM;
225
0
    return -1;
226
0
  }
227
228
0
  res = pr_table_add(curr_cls->cls_notes, key, value, valuesz);
229
0
  return res;
230
0
}
231
232
0
int pr_class_open(pool *p, const char *name) {
233
0
  pr_class_t *cls;
234
0
  pool *cls_pool;
235
236
0
  if (p == NULL ||
237
0
      name == NULL) {
238
0
    errno = EINVAL;
239
0
    return -1;
240
0
  }
241
242
  /* Allocate a sub pool from the given pool, from which a new Class will
243
   * be allocated.
244
   */
245
0
  cls_pool = make_sub_pool(p);
246
0
  pr_pool_tag(cls_pool, "<Class> Pool");
247
248
0
  cls = pcalloc(cls_pool, sizeof(pr_class_t));
249
0
  cls->cls_pool = cls_pool;
250
0
  cls->cls_name = pstrdup(cls->cls_pool, name);
251
0
  cls->cls_satisfy = PR_CLASS_SATISFY_ANY;
252
0
  cls->cls_notes = pr_table_nalloc(cls_pool, 0, 1);
253
254
  /* Change the configuration context type. */
255
0
  main_server->config_type = CONF_CLASS;
256
257
0
  curr_cls = cls;
258
0
  return 0;
259
0
}
260
261
0
int pr_class_close(void) {
262
263
  /* If there is no current Class, there is nothing to do. */
264
0
  if (curr_cls == NULL) {
265
0
    return 0;
266
0
  }
267
268
  /* If there are no client rules in this class, simply remove it.  No need
269
   * to waste space.
270
   */
271
0
  if (curr_cls->cls_acls == NULL) {
272
0
    destroy_pool(curr_cls->cls_pool);
273
0
    curr_cls = NULL;
274
275
    /* Restore the configuration context type. */
276
0
    main_server->config_type = CONF_ROOT;
277
278
0
    errno = EINVAL;
279
0
    return -1;
280
0
  }
281
282
  /* Make sure the list of clients is NULL-terminated. */
283
0
  push_array(curr_cls->cls_acls);
284
285
  /* Now add the current Class to the end of the list. */
286
0
  if (class_list) {
287
0
    pr_class_t *ci;
288
289
0
    ci = class_list;
290
0
    while (ci != NULL &&
291
0
           ci->cls_next != NULL) {
292
0
      ci = ci->cls_next;
293
0
    }
294
295
0
    ci->cls_next = curr_cls;
296
297
0
  } else {
298
0
    class_list = curr_cls;
299
0
  }
300
301
0
  curr_cls = NULL;
302
303
  /* Restore the configuration context type. */
304
0
  main_server->config_type = CONF_ROOT;
305
306
0
  return 0;
307
0
}
308
309
0
void init_class(void) {
310
  class_list = NULL;
311
0
}