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