Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * ProFTPD - FTP server daemon |
3 | | * Copyright (c) 2009-2022 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 | | #include "conf.h" |
26 | | |
27 | | /* This struct and the list of such structs are used to try to reduce |
28 | | * the use of the following idiom to identify which command a given |
29 | | * cmd_rec is: |
30 | | * |
31 | | * if (strcmp(cmd->argv[0], C_USER) == 0) |
32 | | * |
33 | | * Rather than using strcmp(3) so freely, try to reduce the command to |
34 | | * a fixed ID (an index into the struct list); this ID can then be compared |
35 | | * rather than using strcmp(3). For commands not in the list, strcmp(3) |
36 | | * can always be used as a fallback. |
37 | | * |
38 | | * A future improvement would be to sort the entries in the table so that |
39 | | * the most common commands appear earlier in the table, and make the |
40 | | * linear scan even shorter. But I'd need to collect better metrics in |
41 | | * order to do that. |
42 | | */ |
43 | | |
44 | | struct cmd_entry { |
45 | | const char *cmd_name; |
46 | | size_t cmd_namelen; |
47 | | }; |
48 | | |
49 | | static struct cmd_entry cmd_ids[] = { |
50 | | { " ", 1 }, /* Index 0 is intentionally filled with a sentinel */ |
51 | | { C_USER, 4 }, /* PR_CMD_USER_ID (1) */ |
52 | | { C_PASS, 4 }, /* PR_CMD_PASS_ID (2) */ |
53 | | { C_ACCT, 4 }, /* PR_CMD_ACCT_ID (3) */ |
54 | | { C_CWD, 3 }, /* PR_CMD_CWD_ID (4) */ |
55 | | { C_XCWD, 4 }, /* PR_CMD_XCWD_ID (5) */ |
56 | | { C_CDUP, 4 }, /* PR_CMD_CDUP_ID (6) */ |
57 | | { C_XCUP, 4 }, /* PR_CMD_XCUP_ID (7) */ |
58 | | { C_SMNT, 4 }, /* PR_CMD_SMNT_ID (8) */ |
59 | | { C_REIN, 4 }, /* PR_CMD_REIN_ID (9) */ |
60 | | { C_QUIT, 4 }, /* PR_CMD_QUIT_ID (10) */ |
61 | | { C_PORT, 4 }, /* PR_CMD_PORT_ID (11) */ |
62 | | { C_EPRT, 4 }, /* PR_CMD_EPRT_ID (12) */ |
63 | | { C_PASV, 4 }, /* PR_CMD_PASV_ID (13) */ |
64 | | { C_EPSV, 4 }, /* PR_CMD_EPSV_ID (14) */ |
65 | | { C_TYPE, 4 }, /* PR_CMD_TYPE_ID (15) */ |
66 | | { C_STRU, 4 }, /* PR_CMD_STRU_ID (16) */ |
67 | | { C_MODE, 4 }, /* PR_CMD_MODE_ID (17) */ |
68 | | { C_RETR, 4 }, /* PR_CMD_RETR_ID (18) */ |
69 | | { C_STOR, 4 }, /* PR_CMD_STOR_ID (19) */ |
70 | | { C_STOU, 4 }, /* PR_CMD_STOU_ID (20) */ |
71 | | { C_APPE, 4 }, /* PR_CMD_APPE_ID (21) */ |
72 | | { C_ALLO, 4 }, /* PR_CMD_ALLO_ID (22) */ |
73 | | { C_REST, 4 }, /* PR_CMD_REST_ID (23) */ |
74 | | { C_RNFR, 4 }, /* PR_CMD_RNFR_ID (24) */ |
75 | | { C_RNTO, 4 }, /* PR_CMD_RNTO_ID (25) */ |
76 | | { C_ABOR, 4 }, /* PR_CMD_ABOR_ID (26) */ |
77 | | { C_DELE, 4 }, /* PR_CMD_DELE_ID (27) */ |
78 | | { C_MDTM, 4 }, /* PR_CMD_MDTM_ID (28) */ |
79 | | { C_RMD, 3 }, /* PR_CMD_RMD_ID (29) */ |
80 | | { C_XRMD, 4 }, /* PR_CMD_XRMD_ID (30) */ |
81 | | { C_MKD, 3 }, /* PR_CMD_MKD_ID (31) */ |
82 | | { C_MLSD, 4 }, /* PR_CMD_MLSD_ID (32) */ |
83 | | { C_MLST, 4 }, /* PR_CMD_MLST_ID (33) */ |
84 | | { C_XMKD, 4 }, /* PR_CMD_XMKD_ID (34) */ |
85 | | { C_PWD, 3 }, /* PR_CMD_PWD_ID (35) */ |
86 | | { C_XPWD, 4 }, /* PR_CMD_XPWD_ID (36) */ |
87 | | { C_SIZE, 4 }, /* PR_CMD_SIZE_ID (37) */ |
88 | | { C_LIST, 4 }, /* PR_CMD_LIST_ID (38) */ |
89 | | { C_NLST, 4 }, /* PR_CMD_NLST_ID (39) */ |
90 | | { C_SITE, 4 }, /* PR_CMD_SITE_ID (40) */ |
91 | | { C_SYST, 4 }, /* PR_CMD_SYST_ID (41) */ |
92 | | { C_STAT, 4 }, /* PR_CMD_STAT_ID (42) */ |
93 | | { C_HELP, 4 }, /* PR_CMD_HELP_ID (43) */ |
94 | | { C_NOOP, 4 }, /* PR_CMD_NOOP_ID (44) */ |
95 | | { C_FEAT, 4 }, /* PR_CMD_FEAT_ID (45) */ |
96 | | { C_OPTS, 4 }, /* PR_CMD_OPTS_ID (46) */ |
97 | | { C_LANG, 4 }, /* PR_CMD_LANG_ID (47) */ |
98 | | { C_ADAT, 4 }, /* PR_CMD_ADAT_ID (48) */ |
99 | | { C_AUTH, 4 }, /* PR_CMD_AUTH_ID (49) */ |
100 | | { C_CCC, 3 }, /* PR_CMD_CCC_ID (50) */ |
101 | | { C_CONF, 4 }, /* PR_CMD_CONF_ID (51) */ |
102 | | { C_ENC, 3 }, /* PR_CMD_ENC_ID (52) */ |
103 | | { C_MIC, 3 }, /* PR_CMD_MIC_ID (53) */ |
104 | | { C_PBSZ, 4 }, /* PR_CMD_PBSZ_ID (54) */ |
105 | | { C_PROT, 4 }, /* PR_CMD_PROT_ID (55) */ |
106 | | { C_MFF, 3 }, /* PR_CMD_MFF_ID (56) */ |
107 | | { C_MFMT, 4 }, /* PR_CMD_MFMT_ID (57) */ |
108 | | { C_HOST, 4 }, /* PR_CMD_HOST_ID (58) */ |
109 | | { C_CLNT, 4 }, /* PR_CMD_CLNT_ID (59) */ |
110 | | { C_RANG, 4 }, /* PR_CMD_RANG_ID (60) */ |
111 | | { C_CSID, 4 }, /* PR_CMD_CSID_ID (61) */ |
112 | | |
113 | | { NULL, 0 } |
114 | | }; |
115 | | |
116 | | /* Due to potential XSS issues (see Bug#4143), we want to explicitly |
117 | | * check for commands from other text-based protocols (e.g. HTTP and SMTP); |
118 | | * if we see these, we want to close the connection with extreme prejudice. |
119 | | */ |
120 | | |
121 | | static struct cmd_entry http_ids[] = { |
122 | | { " ", 1 }, /* Index 0 is intentionally filled with a sentinel */ |
123 | | { "CONNECT", 7 }, |
124 | | { "DELETE", 6 }, |
125 | | { "GET", 3 }, |
126 | | { "HEAD", 4 }, |
127 | | { "OPTIONS", 7 }, |
128 | | { "PATCH", 5 }, |
129 | | { "POST", 4 }, |
130 | | { "PUT", 3 }, |
131 | | |
132 | | { NULL, 0 } |
133 | | }; |
134 | | |
135 | | static struct cmd_entry smtp_ids[] = { |
136 | | { " ", 1 }, /* Index 0 is intentionally filled with a sentinel */ |
137 | | { "DATA", 4 }, |
138 | | { "EHLO", 4 }, |
139 | | { "HELO", 4 }, |
140 | | { "MAIL", 4 }, |
141 | | { "RCPT", 4 }, |
142 | | { "RSET", 4 }, |
143 | | { "VRFY", 4 }, |
144 | | |
145 | | { NULL, 0 } |
146 | | }; |
147 | | |
148 | | static const char *trace_channel = "command"; |
149 | | |
150 | 0 | cmd_rec *pr_cmd_alloc(pool *p, unsigned int argc, ...) { |
151 | 0 | pool *newpool = NULL; |
152 | 0 | cmd_rec *cmd = NULL; |
153 | 0 | int *xerrno = NULL; |
154 | 0 | va_list args; |
155 | |
|
156 | 0 | if (p == NULL) { |
157 | 0 | errno = EINVAL; |
158 | 0 | return NULL; |
159 | 0 | } |
160 | | |
161 | 0 | newpool = make_sub_pool(p); |
162 | 0 | pr_pool_tag(newpool, "cmd_rec pool"); |
163 | |
|
164 | 0 | cmd = pcalloc(newpool, sizeof(cmd_rec)); |
165 | 0 | cmd->argc = argc; |
166 | 0 | cmd->stash_index = -1; |
167 | 0 | cmd->stash_hash = 0; |
168 | 0 | cmd->pool = newpool; |
169 | 0 | cmd->tmp_pool = make_sub_pool(cmd->pool); |
170 | 0 | pr_pool_tag(cmd->tmp_pool, "cmd_rec tmp pool"); |
171 | |
|
172 | 0 | if (argc > 0) { |
173 | 0 | register unsigned int i = 0; |
174 | |
|
175 | 0 | cmd->argv = pcalloc(cmd->pool, sizeof(void *) * (argc + 1)); |
176 | 0 | va_start(args, argc); |
177 | |
|
178 | 0 | for (i = 0; i < argc; i++) { |
179 | 0 | cmd->argv[i] = va_arg(args, void *); |
180 | 0 | } |
181 | |
|
182 | 0 | va_end(args); |
183 | 0 | cmd->argv[argc] = NULL; |
184 | |
|
185 | 0 | pr_pool_tag(cmd->pool, cmd->argv[0]); |
186 | 0 | } |
187 | | |
188 | | /* This table will not contain that many entries, so a low number |
189 | | * of chains should suffice. |
190 | | */ |
191 | 0 | cmd->notes = pr_table_nalloc(cmd->pool, 0, 8); |
192 | | |
193 | | /* Initialize the "errno" note to be zero, so that it is always present. */ |
194 | 0 | xerrno = palloc(cmd->pool, sizeof(int)); |
195 | 0 | *xerrno = 0; |
196 | 0 | (void) pr_table_add(cmd->notes, "errno", xerrno, sizeof(int)); |
197 | |
|
198 | 0 | return cmd; |
199 | 0 | } |
200 | | |
201 | 0 | int pr_cmd_clear_cache(cmd_rec *cmd) { |
202 | 0 | if (cmd == NULL) { |
203 | 0 | errno = EINVAL; |
204 | 0 | return -1; |
205 | 0 | } |
206 | | |
207 | | /* Clear the strings that have been cached for this command in the |
208 | | * notes table. |
209 | | */ |
210 | | |
211 | 0 | (void) pr_table_remove(cmd->notes, "displayable-str", NULL); |
212 | 0 | (void) pr_cmd_set_errno(cmd, 0); |
213 | |
|
214 | 0 | return 0; |
215 | 0 | } |
216 | | |
217 | 0 | int pr_cmd_cmp(cmd_rec *cmd, int cmd_id) { |
218 | 0 | if (cmd == NULL || |
219 | 0 | cmd_id <= 0) { |
220 | 0 | errno = EINVAL; |
221 | 0 | return -1; |
222 | 0 | } |
223 | | |
224 | 0 | if (cmd->argc == 0 || |
225 | 0 | cmd->argv == NULL) { |
226 | 0 | return 1; |
227 | 0 | } |
228 | | |
229 | | /* The cmd ID is unknown; look it up. */ |
230 | 0 | if (cmd->cmd_id == 0) { |
231 | 0 | cmd->cmd_id = pr_cmd_get_id(cmd->argv[0]); |
232 | 0 | } |
233 | | |
234 | | /* The cmd ID is known to be unknown. */ |
235 | 0 | if (cmd->cmd_id < 0) { |
236 | 0 | return 1; |
237 | 0 | } |
238 | | |
239 | 0 | if (cmd->cmd_id == cmd_id) { |
240 | 0 | return 0; |
241 | 0 | } |
242 | | |
243 | 0 | return cmd->cmd_id < cmd_id ? -1 : 1; |
244 | 0 | } |
245 | | |
246 | 0 | int pr_cmd_get_errno(cmd_rec *cmd) { |
247 | 0 | void *v; |
248 | 0 | int *xerrno; |
249 | |
|
250 | 0 | if (cmd == NULL) { |
251 | 0 | errno = EINVAL; |
252 | 0 | return -1; |
253 | 0 | } |
254 | | |
255 | 0 | v = (void *) pr_table_get(cmd->notes, "errno", NULL); |
256 | 0 | if (v == NULL) { |
257 | 0 | errno = ENOENT; |
258 | 0 | return -1; |
259 | 0 | } |
260 | | |
261 | 0 | xerrno = v; |
262 | 0 | return *xerrno; |
263 | 0 | } |
264 | | |
265 | 0 | int pr_cmd_set_errno(cmd_rec *cmd, int xerrno) { |
266 | 0 | void *v; |
267 | |
|
268 | 0 | if (cmd == NULL || |
269 | 0 | cmd->notes == NULL) { |
270 | 0 | errno = EINVAL; |
271 | 0 | return -1; |
272 | 0 | } |
273 | | |
274 | 0 | v = (void *) pr_table_get(cmd->notes, "errno", NULL); |
275 | 0 | if (v == NULL) { |
276 | 0 | errno = ENOENT; |
277 | 0 | return -1; |
278 | 0 | } |
279 | | |
280 | 0 | *((int *) v) = xerrno; |
281 | 0 | return 0; |
282 | 0 | } |
283 | | |
284 | 0 | int pr_cmd_set_name(cmd_rec *cmd, const char *cmd_name) { |
285 | 0 | if (cmd == NULL || |
286 | 0 | cmd_name == NULL) { |
287 | 0 | errno = EINVAL; |
288 | 0 | return -1; |
289 | 0 | } |
290 | | |
291 | 0 | cmd->argv[0] = (char *) cmd_name; |
292 | 0 | cmd->cmd_id = pr_cmd_get_id(cmd->argv[0]); |
293 | |
|
294 | 0 | return 0; |
295 | 0 | } |
296 | | |
297 | 0 | int pr_cmd_strcmp(cmd_rec *cmd, const char *cmd_name) { |
298 | 0 | int cmd_id; |
299 | 0 | size_t cmd_namelen; |
300 | |
|
301 | 0 | if (cmd == NULL || |
302 | 0 | cmd_name == NULL) { |
303 | 0 | errno = EINVAL; |
304 | 0 | return -1; |
305 | 0 | } |
306 | | |
307 | 0 | if (cmd->argc == 0 || |
308 | 0 | cmd->argv == NULL) { |
309 | 0 | return 1; |
310 | 0 | } |
311 | | |
312 | | /* The cmd ID is unknown; look it up. */ |
313 | 0 | if (cmd->cmd_id == 0) { |
314 | 0 | cmd->cmd_id = pr_cmd_get_id(cmd->argv[0]); |
315 | 0 | } |
316 | |
|
317 | 0 | if (cmd->cmd_id > 0) { |
318 | 0 | int res; |
319 | |
|
320 | 0 | cmd_id = pr_cmd_get_id(cmd_name); |
321 | |
|
322 | 0 | res = pr_cmd_cmp(cmd, cmd_id); |
323 | 0 | if (res == 0) { |
324 | 0 | return 0; |
325 | 0 | } |
326 | | |
327 | 0 | return strncasecmp(cmd_name, cmd->argv[0], |
328 | 0 | cmd_ids[cmd->cmd_id].cmd_namelen + 1); |
329 | 0 | } |
330 | | |
331 | 0 | cmd_namelen = strlen(cmd_name); |
332 | 0 | return strncmp(cmd->argv[0], cmd_name, cmd_namelen + 1); |
333 | 0 | } |
334 | | |
335 | 0 | const char *pr_cmd_get_displayable_str(cmd_rec *cmd, size_t *str_len) { |
336 | 0 | const char *res; |
337 | 0 | unsigned int argc; |
338 | 0 | void **argv; |
339 | 0 | pool *p; |
340 | |
|
341 | 0 | if (cmd == NULL) { |
342 | 0 | errno = EINVAL; |
343 | 0 | return NULL; |
344 | 0 | } |
345 | | |
346 | 0 | res = pr_table_get(cmd->notes, "displayable-str", NULL); |
347 | 0 | if (res != NULL) { |
348 | 0 | if (str_len != NULL) { |
349 | 0 | *str_len = strlen(res); |
350 | 0 | } |
351 | |
|
352 | 0 | return res; |
353 | 0 | } |
354 | | |
355 | 0 | argc = cmd->argc; |
356 | 0 | argv = cmd->argv; |
357 | 0 | p = cmd->pool; |
358 | |
|
359 | 0 | res = ""; |
360 | | |
361 | | /* Check for "sensitive" commands. */ |
362 | 0 | if (pr_cmd_cmp(cmd, PR_CMD_PASS_ID) == 0 || |
363 | 0 | pr_cmd_cmp(cmd, PR_CMD_ADAT_ID) == 0) { |
364 | 0 | argc = 2; |
365 | 0 | argv[1] = "(hidden)"; |
366 | 0 | } |
367 | |
|
368 | 0 | if (argc > 0) { |
369 | 0 | register unsigned int i; |
370 | |
|
371 | 0 | res = pstrcat(p, res, pr_fs_decode_path(p, argv[0]), NULL); |
372 | |
|
373 | 0 | for (i = 1; i < argc; i++) { |
374 | 0 | res = pstrcat(p, res, " ", pr_fs_decode_path(p, argv[i]), NULL); |
375 | 0 | } |
376 | 0 | } |
377 | |
|
378 | 0 | if (pr_table_add(cmd->notes, pstrdup(cmd->pool, "displayable-str"), |
379 | 0 | pstrdup(cmd->pool, res), 0) < 0) { |
380 | 0 | if (errno != EEXIST) { |
381 | 0 | pr_trace_msg(trace_channel, 4, |
382 | 0 | "error setting 'displayable-str' command note: %s", strerror(errno)); |
383 | 0 | } |
384 | 0 | } |
385 | |
|
386 | 0 | if (str_len != NULL) { |
387 | 0 | *str_len = strlen(res); |
388 | 0 | } |
389 | |
|
390 | 0 | return res; |
391 | 0 | } |
392 | | |
393 | 0 | int pr_cmd_get_id(const char *cmd_name) { |
394 | 0 | register unsigned int i; |
395 | 0 | size_t cmd_namelen; |
396 | 0 | char first_letter; |
397 | |
|
398 | 0 | if (cmd_name == NULL) { |
399 | 0 | errno = EINVAL; |
400 | 0 | return -1; |
401 | 0 | } |
402 | | |
403 | 0 | cmd_namelen = strlen(cmd_name); |
404 | | |
405 | | /* Take advantage of the fact that we know, a priori, that the shortest |
406 | | * command name in the list is 3 characters, and that the longest is 4 |
407 | | * characters. No need to scan the list if we know that the given name |
408 | | * is not within that length range. |
409 | | */ |
410 | 0 | if (cmd_namelen < PR_CMD_MIN_NAMELEN || |
411 | 0 | cmd_namelen > PR_CMD_MAX_NAMELEN) { |
412 | 0 | errno = ENOENT; |
413 | 0 | return -1; |
414 | 0 | } |
415 | | |
416 | 0 | first_letter = toupper((int) cmd_name[0]); |
417 | |
|
418 | 0 | for (i = 1; cmd_ids[i].cmd_name != NULL; i++) { |
419 | 0 | if (cmd_ids[i].cmd_namelen != cmd_namelen) { |
420 | 0 | continue; |
421 | 0 | } |
422 | | |
423 | 0 | if (cmd_ids[i].cmd_name[0] != first_letter) { |
424 | 0 | continue; |
425 | 0 | } |
426 | | |
427 | 0 | if (strcasecmp(cmd_ids[i].cmd_name, cmd_name) == 0) { |
428 | 0 | return i; |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | 0 | errno = ENOENT; |
433 | 0 | return -1; |
434 | 0 | } |
435 | | |
436 | | static int is_known_cmd(struct cmd_entry *known_cmds, const char *cmd_name, |
437 | 0 | size_t cmd_namelen) { |
438 | 0 | register unsigned int i; |
439 | 0 | int known = FALSE; |
440 | |
|
441 | 0 | for (i = 0; known_cmds[i].cmd_name != NULL; i++) { |
442 | 0 | if (cmd_namelen == known_cmds[i].cmd_namelen) { |
443 | 0 | if (strncmp(cmd_name, known_cmds[i].cmd_name, cmd_namelen + 1) == 0) { |
444 | 0 | known = TRUE; |
445 | 0 | break; |
446 | 0 | } |
447 | 0 | } |
448 | 0 | } |
449 | |
|
450 | 0 | return known; |
451 | 0 | } |
452 | | |
453 | 0 | int pr_cmd_is_http(cmd_rec *cmd) { |
454 | 0 | const char *cmd_name; |
455 | 0 | size_t cmd_namelen; |
456 | |
|
457 | 0 | if (cmd == NULL) { |
458 | 0 | errno = EINVAL; |
459 | 0 | return -1; |
460 | 0 | } |
461 | | |
462 | 0 | cmd_name = cmd->argv[0]; |
463 | 0 | if (cmd_name == NULL) { |
464 | 0 | errno = EINVAL; |
465 | 0 | return -1; |
466 | 0 | } |
467 | | |
468 | 0 | if (cmd->cmd_id == 0) { |
469 | 0 | cmd->cmd_id = pr_cmd_get_id(cmd_name); |
470 | 0 | } |
471 | |
|
472 | 0 | if (cmd->cmd_id >= 0) { |
473 | 0 | return FALSE; |
474 | 0 | } |
475 | | |
476 | 0 | cmd_namelen = strlen(cmd_name); |
477 | 0 | return is_known_cmd(http_ids, cmd_name, cmd_namelen); |
478 | 0 | } |
479 | | |
480 | 0 | int pr_cmd_is_smtp(cmd_rec *cmd) { |
481 | 0 | const char *cmd_name; |
482 | 0 | size_t cmd_namelen; |
483 | |
|
484 | 0 | if (cmd == NULL) { |
485 | 0 | errno = EINVAL; |
486 | 0 | return -1; |
487 | 0 | } |
488 | | |
489 | 0 | cmd_name = cmd->argv[0]; |
490 | 0 | if (cmd_name == NULL) { |
491 | 0 | errno = EINVAL; |
492 | 0 | return -1; |
493 | 0 | } |
494 | | |
495 | 0 | if (cmd->cmd_id == 0) { |
496 | 0 | cmd->cmd_id = pr_cmd_get_id(cmd_name); |
497 | 0 | } |
498 | |
|
499 | 0 | if (cmd->cmd_id >= 0) { |
500 | 0 | return FALSE; |
501 | 0 | } |
502 | | |
503 | 0 | cmd_namelen = strlen(cmd_name); |
504 | 0 | return is_known_cmd(smtp_ids, cmd_name, cmd_namelen); |
505 | 0 | } |
506 | | |
507 | 0 | int pr_cmd_is_ssh2(cmd_rec *cmd) { |
508 | 0 | const char *cmd_name; |
509 | |
|
510 | 0 | if (cmd == NULL) { |
511 | 0 | errno = EINVAL; |
512 | 0 | return -1; |
513 | 0 | } |
514 | | |
515 | 0 | cmd_name = cmd->argv[0]; |
516 | 0 | if (cmd_name == NULL) { |
517 | 0 | errno = EINVAL; |
518 | 0 | return -1; |
519 | 0 | } |
520 | | |
521 | 0 | if (cmd->cmd_id == 0) { |
522 | 0 | cmd->cmd_id = pr_cmd_get_id(cmd_name); |
523 | 0 | } |
524 | |
|
525 | 0 | if (cmd->cmd_id >= 0) { |
526 | 0 | return FALSE; |
527 | 0 | } |
528 | | |
529 | 0 | if (strncmp(cmd_name, "SSH-2.0-", 8) == 0 || |
530 | 0 | strncmp(cmd_name, "SSH-1.99-", 9) == 0) { |
531 | 0 | return TRUE; |
532 | 0 | } |
533 | | |
534 | 0 | return FALSE; |
535 | 0 | } |