1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 2001-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, 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 /* ProFTPD Controls command-line client */
26
27 #include "conf.h"
28 #include "privs.h"
29
30 #ifdef HAVE_GETOPT_H
31 # include <getopt.h>
32 #endif
33
34 static const char *program = "ftpdctl";
35
36 /* NOTE: these empty stubs are needed for proper linking. What a mess. */
37
38 unsigned char is_master;
39 session_t session;
40 server_rec *main_server = NULL;
41
get_param_ptr(xaset_t * set,const char * name,int recurse)42 void *get_param_ptr(xaset_t *set, const char *name, int recurse) {
43 return NULL;
44 }
45
pr_alarms_block(void)46 void pr_alarms_block(void) {
47 }
48
pr_alarms_unblock(void)49 void pr_alarms_unblock(void) {
50 }
51
pr_auth_name2gid(pool * p,const char * name)52 gid_t pr_auth_name2gid(pool *p, const char *name) {
53 return (gid_t) -1;
54 }
55
pr_auth_name2uid(pool * p,const char * name)56 uid_t pr_auth_name2uid(pool *p, const char *name) {
57 return (uid_t) -1;
58 }
59
pr_event_generate(const char * event,const void * event_data)60 void pr_event_generate(const char *event, const void *event_data) {
61 (void) event;
62 (void) event_data;
63 }
64
pr_event_listening(const char * event)65 int pr_event_listening(const char *event) {
66 return -1;
67 }
68
pr_fs_fadvise(int fd,off_t off,off_t len,int advice)69 void pr_fs_fadvise(int fd, off_t off, off_t len, int advice) {
70 }
71
pr_fs_get_usable_fd(int fd)72 int pr_fs_get_usable_fd(int fd) {
73 return -1;
74 }
75
pr_localtime(pool * p,const time_t * t)76 struct tm *pr_localtime(pool *p, const time_t *t) {
77 struct tm *tm;
78
79 #if defined(HAVE_LOCALTIME_R)
80 if (p == NULL) {
81 errno = EINVAL;
82 return NULL;
83 }
84
85 tm = localtime_r(t, pcalloc(p, sizeof(struct tm)));
86 #else
87 tm = localtime(t);
88 #endif /* HAVE_LOCALTIME_R */
89
90 return tm;
91 }
92
pr_privs_root(const char * file,int lineno)93 int pr_privs_root(const char *file, int lineno) {
94 return 0;
95 }
96
pr_privs_relinquish(const char * file,int lineno)97 int pr_privs_relinquish(const char *file, int lineno) {
98 return 0;
99 }
100
pr_random_next(long min,long max)101 long pr_random_next(long min, long max) {
102 return 1;
103 }
104
pr_signals_block(void)105 void pr_signals_block(void) {
106 }
107
pr_signals_unblock(void)108 void pr_signals_unblock(void) {
109 }
110
pr_signals_handle(void)111 void pr_signals_handle(void) {
112 }
113
pr_snprintf(char * buf,size_t bufsz,const char * fmt,...)114 int pr_snprintf(char *buf, size_t bufsz, const char *fmt, ...) {
115 va_list msg;
116 int res;
117
118 va_start(msg, fmt);
119 res = pr_vsnprintf(buf, bufsz, fmt, msg);
120 va_end(msg);
121
122 return res;
123 }
124
pr_table_alloc(pool * p,int flags)125 pr_table_t *pr_table_alloc(pool *p, int flags) {
126 errno = EPERM;
127 return NULL;
128 }
129
pr_table_add(pr_table_t * tab,const char * k,const void * v,size_t sz)130 int pr_table_add(pr_table_t *tab, const char *k, const void *v, size_t sz) {
131 errno = EPERM;
132 return -1;
133 }
134
pr_table_count(pr_table_t * tab)135 int pr_table_count(pr_table_t *tab) {
136 errno = EPERM;
137 return -1;
138 }
139
pr_table_empty(pr_table_t * tab)140 int pr_table_empty(pr_table_t *tab) {
141 errno = EPERM;
142 return -1;
143 }
144
pr_table_exists(pr_table_t * tab,const char * k)145 int pr_table_exists(pr_table_t *tab, const char *k) {
146 errno = EPERM;
147 return -1;
148 }
149
pr_table_free(pr_table_t * tab)150 int pr_table_free(pr_table_t *tab) {
151 errno = EPERM;
152 return -1;
153 }
154
pr_table_get(pr_table_t * tab,const char * k,size_t * sz)155 const void *pr_table_get(pr_table_t *tab, const char *k, size_t *sz) {
156 errno = EPERM;
157 return NULL;
158 }
159
pr_table_remove(pr_table_t * tab,const char * k,size_t * sz)160 const void *pr_table_remove(pr_table_t *tab, const char *k, size_t *sz) {
161 errno = EPERM;
162 return NULL;
163 }
164
pr_table_set(pr_table_t * tab,const char * k,const void * v,size_t sz)165 int pr_table_set(pr_table_t *tab, const char *k, const void *v, size_t sz) {
166 errno = EPERM;
167 return -1;
168 }
169
pr_trace_msg(const char * channel,int level,const char * fmt,...)170 int pr_trace_msg(const char *channel, int level, const char *fmt, ...) {
171 errno = ENOSYS;
172 return -1;
173 }
174
pr_vsnprintf(char * buf,size_t bufsz,const char * fmt,va_list msg)175 int pr_vsnprintf(char *buf, size_t bufsz, const char *fmt, va_list msg) {
176 return vsnprintf(buf, bufsz, fmt, msg);
177 }
178
sstrncpy(char * dst,const char * src,size_t n)179 int sstrncpy(char *dst, const char *src, size_t n) {
180 register char *d = dst;
181 int res = 0;
182
183 if (dst == NULL) {
184 errno = EINVAL;
185 return -1;
186 }
187
188 if (n == 0) {
189 return 0;
190 }
191
192 if (src && *src) {
193 for (; *src && n > 1; n--) {
194 *d++ = *src++;
195 res++;
196 }
197 }
198
199 *d = '\0';
200 return res;
201 }
202
203 #ifdef PR_USE_CTRLS
204
205 /* need a SIGPIPE handler */
sig_pipe(int sig)206 static RETSIGTYPE sig_pipe(int sig) {
207 signal(SIGPIPE, sig_pipe);
208 }
209
usage(void)210 static void usage(void) {
211 fprintf(stdout, "usage: %s [options] action [...]\n", program);
212 fprintf(stdout, " -h\tdisplays this message\n");
213 fprintf(stdout, " -s\tspecify an alternate local socket\n");
214 fprintf(stdout, " -v\tdisplays more verbose information\n");
215 fprintf(stdout, "\n");
216 return;
217 }
218
main(int argc,char * argv[])219 int main(int argc, char *argv[]) {
220 unsigned char verbose = FALSE;
221 char **respargv = NULL;
222 const char *cmdopts = "hs:v";
223
224 register int i = 0;
225 char *socket_file = PR_RUN_DIR "/proftpd.sock";
226 int sockfd = -1, optc = 0, status = 0, respargc = 0;
227 unsigned int reqargc = 0;
228 pool *ctl_pool = NULL;
229 array_header *reqargv = NULL;
230
231 /* Set the POSIXLY_CORRECT environment variable, so that control handlers
232 * can themselves have optional flags.
233 */
234 if (putenv("POSIXLY_CORRECT=1") < 0) {
235 fprintf(stderr, "%s: unable to set POSIXLY_CORRECT: %s\n", program,
236 strerror(errno));
237 exit(1);
238 }
239
240 opterr = 0;
241 while ((optc = getopt(argc, argv, cmdopts)) != -1) {
242 switch (optc) {
243 case 'h':
244 usage();
245 return 0;
246
247 case 's':
248 if (*optarg != '/') {
249 fprintf(stderr, "%s: alternate socket path must be an absolute "
250 "path\n", program);
251 return 1;
252 }
253
254 socket_file = optarg;
255 break;
256
257 case 'v':
258 verbose = TRUE;
259 break;
260
261 case '?':
262 fprintf(stdout, "%s: unknown option: %c\n", program, (char) optopt);
263 break;
264 }
265 }
266
267 /* Make sure we were called with at least one argument. */
268 if (argv[optind] == NULL) {
269 fprintf(stdout, "%s: missing required action\n", program);
270 exit(1);
271 }
272
273 signal(SIGPIPE, sig_pipe);
274
275 /* Allocate some memory for proftpd objects. */
276 ctl_pool = make_sub_pool(NULL);
277
278 reqargv = make_array(ctl_pool, 0, sizeof(char *));
279
280 /* Process the command-line args into an array_header. */
281 for (i = optind; i < argc; i++) {
282 if (verbose)
283 fprintf(stdout, "%s: adding \"%s\" to reqargv\n", program, argv[i]);
284 *((char **) push_array(reqargv)) = pstrdup(ctl_pool, argv[i]);
285 reqargc++;
286 }
287
288 /* Don't forget to NULL-terminate the array. */
289 *((char **) push_array(reqargv)) = NULL;
290
291 /* Open a connection to the socket maintained by mod_ctrls. */
292 if (verbose)
293 fprintf(stdout, "%s: contacting server using '%s'\n", program,
294 socket_file);
295
296 sockfd = pr_ctrls_connect(socket_file);
297 if (sockfd < 0) {
298 fprintf(stderr, "%s: error contacting server using '%s': %s\n", program,
299 socket_file, strerror(errno));
300 exit(1);
301 }
302
303 if (verbose)
304 fprintf(stdout, "%s: sending control request\n", program);
305
306 if (pr_ctrls_send_msg(sockfd, 0, reqargc, (char **) reqargv->elts) < 0) {
307 fprintf(stderr, "%s: error sending request: %s\n", program,
308 strerror(errno));
309 exit(1);
310 }
311
312 /* Read and display the responses. */
313
314 if (verbose)
315 fprintf(stdout, "%s: receiving control response\n", program);
316
317 /* Manually set errno to this value, so that if an error (like a segfault)
318 * occurs, the error string displayed to the user is more indicative of
319 * the cause of the lack of responses.
320 */
321 errno = EPERM;
322
323 if ((respargc = pr_ctrls_recv_response(ctl_pool, sockfd, &status,
324 &respargv)) < 0) {
325 fprintf(stdout, "%s: error receiving response: %s\n", program,
326 strerror(errno));
327 exit(1);
328 }
329
330 if (respargv != NULL) {
331 for (i = 0; i < respargc; i++)
332 fprintf(stdout, "%s: %s\n", program, respargv[i]);
333
334 } else
335 fprintf(stdout, "%s: no response from server\n", program);
336
337 destroy_pool(ctl_pool);
338 ctl_pool = NULL;
339
340 return 0;
341 }
342
343 #else
344
main(int argc,char * argv[])345 int main(int argc, char *argv[]) {
346 printf("%s:\n", program);
347 printf(" Controls support disabled.\n");
348 printf(" Please recompile proftpd using --enable-ctrls\n");
349 return 1;
350 }
351
352 #endif /* PR_USE_CTRLS */
353