• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 28531645955

01 Jul 2026 04:14PM UTC coverage: 92.468% (-0.6%) from 93.032%
28531645955

push

github

web-flow
Exercise caution when reading the client-provided file size for SCP uploads, as it could possibly overflow our size type.

Thanks to Fabian Wahle of Hap Security for reporting this issue.

48701 of 52668 relevant lines covered (92.47%)

227.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.45
/src/modules.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 1997, 1998 Public Flood Software
4
 * Copyright (c) 2001-2026 The ProFTPD Project team
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 2 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 <https://www.gnu.org/licenses/>.
18
 *
19
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
20
 * and other respective copyright holders give permission to link this program
21
 * with OpenSSL, and distribute the resulting executable, without including
22
 * the source code for OpenSSL in the source distribution.
23
 */
24

25
/* Module handling routines */
26

27
#include "conf.h"
28

29
extern module *static_modules[];
30
extern module *loaded_modules;
31

32
/* Currently running module */
33
module *curr_module = NULL;
34

35
/* Used to track the priority for loaded modules. */
36
static unsigned int curr_module_pri = 0;
37

38
static const char *trace_channel = "module";
39

40
modret_t *pr_module_call(module *m, modret_t *(*func)(cmd_rec *),
86✔
41
    cmd_rec *cmd) {
42
  modret_t *res;
43
  module *prev_module = curr_module;
86✔
44

45
  if (m == NULL ||
172✔
46
      func == NULL ||
166✔
47
      cmd == NULL) {
48
    errno = EINVAL;
7✔
49
    return NULL;
7✔
50
  }
51

52
  if (cmd->tmp_pool == NULL) {
79✔
53
    cmd->tmp_pool = make_sub_pool(cmd->pool);
9✔
54
    pr_pool_tag(cmd->tmp_pool, "Module call tmp_pool");
9✔
55
  }
56

57
  curr_module = m;
79✔
58
  res = func(cmd);
79✔
59
  curr_module = prev_module;
79✔
60

61
  /* Note that we don't clear the pool here because the function may
62
   * return data which resides in this pool.
63
   */
64
  return res;
79✔
65
}
66

67
modret_t *mod_create_data(cmd_rec *cmd, void *d) {
29✔
68
  modret_t *res;
69

70
  if (cmd == NULL) {
29✔
71
    errno = EINVAL;
1✔
72
    return NULL;
1✔
73
  }
74

75
  res = pcalloc(cmd->tmp_pool, sizeof(modret_t));
28✔
76
  res->data = d;
28✔
77

78
  return res;
28✔
79
}
80

81
modret_t *mod_create_ret(cmd_rec *cmd, unsigned char err, const char *n,
75✔
82
    const char *m) {
83
  modret_t *res;
84

85
  if (cmd == NULL) {
75✔
86
    errno = EINVAL;
1✔
87
    return NULL;
1✔
88
  }
89

90
  res = pcalloc(cmd->tmp_pool, sizeof(modret_t));
74✔
91
  res->mr_handler_module = curr_module;
74✔
92
  res->mr_error = err;
74✔
93

94
  if (n != NULL) {
74✔
95
    res->mr_numeric = pstrdup(cmd->tmp_pool, n);
1✔
96
  }
97

98
  if (m != NULL) {
74✔
99
    res->mr_message = pstrdup(cmd->tmp_pool, m);
54✔
100
  }
101

102
  return res;
103
}
104

105
modret_t *mod_create_error(cmd_rec *cmd, int mr_errno) {
20✔
106
  modret_t *res;
107

108
  if (cmd == NULL) {
20✔
109
    errno = EINVAL;
1✔
110
    return NULL;
1✔
111
  }
112

113
  res = pcalloc(cmd->tmp_pool, sizeof(modret_t));
19✔
114
  res->mr_handler_module = curr_module;
19✔
115
  res->mr_error = mr_errno;
19✔
116

117
  return res;
19✔
118
}
119

120
/* Called after forking in order to inform/initialize modules
121
 * need to know we are a child and have a connection.
122
 */
123
int modules_session_init(void) {
4✔
124
  module *prev_module = curr_module, *m;
4✔
125

126
  for (m = loaded_modules; m; m = m->next) {
6✔
127
    if (m->sess_init) {
3✔
128
      curr_module = m;
2✔
129

130
      pr_trace_msg(trace_channel, 12,
2✔
131
        "invoking sess_init callback on mod_%s.c", m->name);
132
      if (m->sess_init() < 0) {
2✔
133
        int xerrno = errno;
1✔
134

135
        pr_log_pri(PR_LOG_WARNING,
1✔
136
          "mod_%s.c: error initializing session (%s), check module logs "
137
          "for details", m->name, strerror(xerrno));
138

139
        errno = xerrno;
1✔
140
        return -1;
1✔
141
      }
142
    }
143
  }
144

145
  curr_module = prev_module;
3✔
146
  return 0;
3✔
147
}
148

149
unsigned char command_exists(const char *name) {
1✔
150
  int idx = -1;
1✔
151
  unsigned int hash = 0;
1✔
152
  cmdtable *cmdtab;
153

154
  cmdtab = pr_stash_get_symbol2(PR_SYM_CMD, name, NULL, &idx, &hash);
1✔
155
  while (cmdtab && cmdtab->cmd_type != CMD) {
2✔
156
    pr_signals_handle();
×
157
    cmdtab = pr_stash_get_symbol2(PR_SYM_CMD, name, cmdtab, &idx, &hash);
×
158
  }
159

160
  return (cmdtab ? TRUE : FALSE);
1✔
161
}
162

163
unsigned char pr_module_exists(const char *name) {
5✔
164
  return pr_module_get(name) != NULL ? TRUE : FALSE;
5✔
165
}
166

167
module *pr_module_get(const char *name) {
52✔
168
  char buf[80] = {'\0'};
52✔
169
  module *m;
170

171
  if (name == NULL) {
52✔
172
    errno = EINVAL;
2✔
173
    return NULL;
2✔
174
  }
175

176
  /* Check the list of compiled-in modules. */
177
  for (m = loaded_modules; m; m = m->next) {
58✔
178
    memset(buf, '\0', sizeof(buf));
32✔
179
    pr_snprintf(buf, sizeof(buf), "mod_%s.c", m->name);
32✔
180
    buf[sizeof(buf)-1] = '\0';
32✔
181

182
    if (strcmp(buf, name) == 0) {
32✔
183
      return m;
184
    }
185
  }
186

187
  errno = ENOENT;
26✔
188
  return NULL;
26✔
189
}
190

191
void modules_list2(int (*listf)(const char *, ...), int flags) {
4✔
192
  if (listf == NULL) {
4✔
193
    listf = printf;
1✔
194
  }
195

196
  if (flags & PR_MODULES_LIST_FL_SHOW_STATIC) {
4✔
197
    register unsigned int i = 0;
2✔
198

199
    listf("Compiled-in modules:\n");
2✔
200
    for (i = 0; static_modules[i]; i++) {
2✔
201
      module *m = static_modules[i];
×
202

203
      if (flags & PR_MODULES_LIST_FL_SHOW_VERSION) {
×
204
        const char *version;
205

206
        version = m->module_version;
×
207
        if (version != NULL) {
×
208
          listf("  %s\n", version);
×
209

210
        } else {
211
          listf("  mod_%s.c\n", m->name);
×
212
        }
213

214
      } else {
215
        listf("  mod_%s.c\n", m->name);
×
216
      }
217
    }
218

219
  } else {
220
    module *m;
221

222
    listf("Loaded modules:\n");
2✔
223
    for (m = loaded_modules; m; m = m->next) {
4✔
224

225
      if (flags & PR_MODULES_LIST_FL_SHOW_VERSION) {
2✔
226
        const char *version;
227

228
        version = m->module_version;
2✔
229
        if (version != NULL) {
2✔
230
          listf("  %s\n", version);
1✔
231

232
        } else {
233
          listf("  mod_%s.c\n", m->name);
1✔
234
        }
235

236
      } else {
237
        listf("  mod_%s.c\n", m->name);
×
238
      }
239
    }
240
  }
241
}
4✔
242

243
void modules_list(int flags) {
1✔
244
  modules_list2(NULL, flags);
1✔
245
}
1✔
246

247
int pr_module_load_authtab(module *m) {
7✔
248
  if (m == NULL ||
13✔
249
      m->name == NULL) {
6✔
250
    errno = EINVAL;
2✔
251
    return -1;
2✔
252
  }
253

254
  if (m->authtable) {
5✔
255
    authtable *authtab;
256

257
    for (authtab = m->authtable; authtab->name; authtab++) {
1✔
258
      authtab->m = m;
1✔
259

260
      if (pr_stash_add_symbol(PR_SYM_AUTH, authtab) < 0) {
1✔
261
        return -1;
262
      }
263
    }
264
  }
265

266
  return 0;
267
}
268

269
int pr_module_load_cmdtab(module *m) {
7✔
270
  if (m == NULL ||
13✔
271
      m->name == NULL) {
6✔
272
    errno = EINVAL;
2✔
273
    return -1;
2✔
274
  }
275

276
  if (m->cmdtable) {
5✔
277
    cmdtable *cmdtab;
278

279
    for (cmdtab = m->cmdtable; cmdtab->command; cmdtab++) {
2✔
280
      cmdtab->m = m;
2✔
281

282
      if (cmdtab->cmd_type == HOOK) {
2✔
283
        if (pr_stash_add_symbol(PR_SYM_HOOK, cmdtab) < 0) {
1✔
284
          return -1;
285
        }
286

287
      } else {
288
        /* All other cmd_types are for CMDs: PRE_CMD, CMD, POST_CMD, etc. */
289
        if (pr_stash_add_symbol(PR_SYM_CMD, cmdtab) < 0) {
1✔
290
          return -1;
291
        }
292
      }
293
    }
294
  }
295

296
  return 0;
297
}
298

299
int pr_module_load_conftab(module *m) {
9✔
300
  if (m == NULL ||
17✔
301
      m->name == NULL) {
8✔
302
    errno = EINVAL;
2✔
303
    return -1;
2✔
304
  }
305

306
  if (m->conftable) {
7✔
307
    conftable *conftab;
308

309
    for (conftab = m->conftable; conftab->directive; conftab++) {
5✔
310
      conftab->m = m;
5✔
311

312
      if (pr_stash_add_symbol(PR_SYM_CONF, conftab) < 0) {
5✔
313
        return -1;
314
      }
315
    }
316
  }
317

318
  return 0;
319
}
320

321
int pr_module_load(module *m) {
8✔
322
  char buf[256];
323

324
  if (m == NULL ||
15✔
325
      m->name == NULL) {
7✔
326
    errno = EINVAL;
2✔
327
    return -1;
2✔
328
  }
329

330
  /* Check the API version the module wants to use. */
331
  if (m->api_version < PR_MODULE_API_VERSION) {
6✔
332
    errno = EACCES;
1✔
333
    return -1;
1✔
334
  }
335

336
  /* Do not allow multiple modules with the same name. */
337
  memset(buf, '\0', sizeof(buf));
5✔
338
  pr_snprintf(buf, sizeof(buf), "mod_%s.c", m->name);
5✔
339
  buf[sizeof(buf)-1] = '\0';
5✔
340

341
  if (pr_module_get(buf) != NULL) {
5✔
342
    errno = EEXIST;
1✔
343
    return -1;
1✔
344
  }
345

346
  /* Invoke the module's initialization routine. */
347
  if (!m->init ||
5✔
348
      m->init() >= 0) {
1✔
349

350
    /* Assign a priority to this module. */
351
    m->priority = curr_module_pri++;
3✔
352

353
    /* Add the module's config, cmd, and auth tables. */
354
    if (pr_module_load_conftab(m) < 0) {
3✔
355
      return -1;
356
    }
357

358
    if (pr_module_load_cmdtab(m) < 0) {
3✔
359
      return -1;
360
    }
361

362
    if (pr_module_load_authtab(m) < 0) {
3✔
363
      return -1;
364
    }
365

366
    /* Add the module to the loaded_modules list. */
367
    if (loaded_modules) {
3✔
368
      m->next = loaded_modules;
×
369
      loaded_modules->prev = m;
×
370
    }
371

372
    loaded_modules = m;
3✔
373

374
    /* Generate an event. */
375
    pr_event_generate("core.module-load", buf);
3✔
376
    return 0;
3✔
377
  }
378

379
  errno = EPERM;
1✔
380
  return -1;
1✔
381
}
382

383
int pr_module_unload(module *m) {
16✔
384
  char buf[256];
385

386
  if (m == NULL ||
31✔
387
      m->name == NULL) {
15✔
388
    errno = EINVAL;
2✔
389
    return -1;
2✔
390
  }
391

392
  /* Make sure this module has been loaded.  We can't unload a module that
393
   * has not been loaded, now can we?
394
   */
395

396
  memset(buf, '\0', sizeof(buf));
14✔
397
  pr_snprintf(buf, sizeof(buf), "mod_%s.c", m->name);
14✔
398
  buf[sizeof(buf)-1] = '\0';
14✔
399

400
  if (pr_module_get(buf) == NULL) {
14✔
401
    errno = ENOENT;
10✔
402
    return -1;
10✔
403
  }
404

405
  /* Generate an event. */
406
  pr_event_generate("core.module-unload", buf);
4✔
407

408
  /* Remove the module from the loaded_modules list. */
409
  if (m->prev) {
4✔
410
    m->prev->next = m->next;
×
411

412
  } else {
413
    /* This module is the start of the loaded_modules list (prev is NULL),
414
     * so we need to update that pointer, too.
415
     */
416
    loaded_modules = m->next;
4✔
417
  }
418

419
  if (m->next) {
4✔
420
    m->next->prev = m->prev;
×
421
  }
422

423
  m->prev = m->next = NULL;
4✔
424

425
  /* Remove the module's config, cmd, and auth tables. */
426
  if (m->conftable) {
4✔
427
    conftable *conftab;
428

429
    for (conftab = m->conftable; conftab->directive; conftab++) {
1✔
430
      pr_stash_remove_symbol(PR_SYM_CONF, conftab->directive, conftab->m);
1✔
431
    }
432
  }
433

434
  if (m->cmdtable) {
4✔
435
    cmdtable *cmdtab;
436

437
    for (cmdtab = m->cmdtable; cmdtab->command; cmdtab++) {
2✔
438
      if (cmdtab->cmd_type == HOOK) {
2✔
439
        pr_stash_remove_symbol(PR_SYM_HOOK, cmdtab->command, cmdtab->m);
1✔
440

441
      } else {
442
        /* All other cmd_types are for CMDs: PRE_CMD, CMD, POST_CMD, etc. */
443
        pr_stash_remove_symbol(PR_SYM_CMD, cmdtab->command, cmdtab->m);
1✔
444
      }
445
    }
446
  }
447

448
  if (m->authtable) {
4✔
449
    authtable *authtab;
450

451
    for (authtab = m->authtable; authtab->name; authtab++) {
1✔
452
      pr_stash_remove_symbol(PR_SYM_AUTH, authtab->name, authtab->m);
1✔
453
    }
454
  }
455

456
  /* Remove any callbacks that the module may have registered, i.e.:
457
   *
458
   * ctrls
459
   * events
460
   * timers
461
   *
462
   * Ideally we would also automatically unregister other callbacks that
463
   * the module may have registered, such as FSIO, NetIO, variables, and
464
   * response handlers.  However, these APIs do not yet allow for
465
   * removal of all callbacks for a given module.
466
   */
467

468
#ifdef PR_USE_CTRLS
469
  pr_ctrls_unregister(m, NULL);
4✔
470
#endif /* PR_USE_CTRLS */
471
  pr_event_unregister(m, NULL, NULL);
4✔
472
  pr_timer_remove(-1, m);
4✔
473

474
  return 0;
4✔
475
}
476

477
int modules_init(void) {
25✔
478
  register unsigned int i = 0;
25✔
479

480
  for (i = 0; static_modules[i]; i++) {
25✔
481
    module *m = static_modules[i];
×
482

483
    if (pr_module_load(m) < 0) {
×
484
      pr_log_pri(PR_LOG_WARNING, "fatal: unable to load module 'mod_%s.c': %s",
×
485
        m->name, strerror(errno));
×
486
      exit(1);
×
487
    }
488
  }
489

490
  return 0;
25✔
491
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc