1 /*
2  * ProFTPD - mod_sftp date(1) simulation
3  * Copyright (c) 2012-2020 TJ Saunders
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, TJ Saunders and other respective copyright holders
20  * give permission to link this program with OpenSSL, and distribute the
21  * resulting executable, without including the source code for OpenSSL in the
22  * source distribution.
23  */
24 
25 #include "mod_sftp.h"
26 #include "ssh2.h"
27 #include "packet.h"
28 #include "msg.h"
29 #include "channel.h"
30 #include "date.h"
31 #include "misc.h"
32 #include "disconnect.h"
33 
34 static pool *date_pool = NULL;
35 static int date_use_gmt = FALSE;
36 
37 /* Use a struct to maintain the per-channel date-specific values. */
38 struct date_session {
39   struct date_session *next, *prev;
40 
41   pool *pool;
42   uint32_t channel_id;
43   int use_gmt;
44 };
45 
46 static struct date_session *date_sessions = NULL;
47 
48 static const char *trace_channel = "ssh2";
49 
date_get_session(uint32_t channel_id)50 static struct date_session *date_get_session(uint32_t channel_id) {
51   struct date_session *sess;
52 
53   sess = date_sessions;
54   while (sess) {
55     pr_signals_handle();
56 
57     if (sess->channel_id == channel_id) {
58       return sess;
59     }
60 
61     sess = sess->next;
62   }
63 
64   errno = ENOENT;
65   return NULL;
66 }
67 
68 /* Main entry point */
sftp_date_handle_packet(pool * p,void * ssh2,uint32_t channel_id,unsigned char * data,uint32_t datalen)69 int sftp_date_handle_packet(pool *p, void *ssh2, uint32_t channel_id,
70     unsigned char *data, uint32_t datalen) {
71 
72   (void) ssh2;
73   (void) channel_id;
74   (void) data;
75   (void) datalen;
76 
77   /* This callback should never be called; the client will request an
78    * 'exec date' command, and mod_sftp should send the channel data back
79    * immediately, without needing anything more from the client.
80    *
81    * Thus this is essentially a no-op.
82    */
83 
84   return 0;
85 }
86 
sftp_date_set_params(pool * p,uint32_t channel_id,array_header * req)87 int sftp_date_set_params(pool *p, uint32_t channel_id, array_header *req) {
88   char **reqargv = NULL;
89   int optc;
90   const char *opts = "u";
91 
92   if (!(sftp_services & SFTP_SERVICE_FL_DATE)) {
93     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "%s",
94       "'date' exec request denied by Protocols config");
95     errno = EPERM;
96     return -1;
97   }
98 
99   reqargv = (char **) req->elts;
100 
101   if (pr_trace_get_level(trace_channel) >= 5) {
102     register unsigned int i;
103 
104     for (i = 0; i < req->nelts; i++) {
105       if (reqargv[i]) {
106         pr_trace_msg(trace_channel, 5, "reqargv[%u] = '%s'", i, reqargv[i]);
107       }
108     }
109   }
110 
111   /* Possible options are:
112    *
113    *  -u (GMT time)
114    */
115 
116   pr_getopt_reset();
117 
118   while ((optc = getopt(req->nelts-1, reqargv, opts)) != -1) {
119     switch (optc) {
120       case 'u':
121         date_use_gmt = TRUE;
122         break;
123 
124       case '?':
125         /* Ignore unsupported options */
126         (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
127           "ignoring supported date(1) option '%c'", (char) optopt);
128         break;
129     }
130   }
131 
132   date_pool = make_sub_pool(sftp_pool);
133   pr_pool_tag(date_pool, "SSH2 Date Pool");
134 
135   return 0;
136 }
137 
sftp_date_postopen_session(uint32_t channel_id)138 int sftp_date_postopen_session(uint32_t channel_id) {
139   struct date_session *sess;
140   unsigned char *buf, *ptr;
141   const char *date_str;
142   uint32_t buflen, bufsz;
143   time_t now;
144 
145   sess = date_get_session(channel_id);
146   if (sess == NULL) {
147     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
148       "no existing date(1) session for channel ID %lu, rejecting request",
149       (unsigned long) channel_id);
150     return -1;
151   }
152 
153   time(&now);
154   date_str = pr_strtime3(sess->pool, now, sess->use_gmt);
155 
156   /* XXX Is this big enough?  Too big? */
157   buflen = bufsz = 128;
158   buf = ptr = palloc(sess->pool, bufsz);
159 
160   /* pr_strtime3() trims off the trailing newline, so add it back in. */
161   sftp_msg_write_string(&buf, &buflen,
162     pstrcat(sess->pool, date_str, "\n", NULL));
163 
164   if (sftp_channel_write_data(sess->pool, channel_id, ptr,
165       (bufsz - buflen)) < 0) {
166     return -1;
167   }
168 
169   /* Return 1 to indicate that we're already done with this channel. */
170   return 1;
171 }
172 
sftp_date_open_session(uint32_t channel_id)173 int sftp_date_open_session(uint32_t channel_id) {
174   pool *sub_pool;
175   struct date_session *sess, *last;
176 
177   /* Check to see if we already have an date session opened for the given
178    * channel ID.
179    */
180   sess = last = date_sessions;
181   while (sess != NULL) {
182     pr_signals_handle();
183 
184     if (sess->channel_id == channel_id) {
185       errno = EEXIST;
186       return -1;
187     }
188 
189     if (sess->next == NULL) {
190       /* This is the last item in the list. */
191       last = sess;
192     }
193 
194     sess = sess->next;
195   }
196 
197   /* Looks like we get to allocate a new one. */
198   sub_pool = make_sub_pool(date_pool);
199   pr_pool_tag(sub_pool, "date session pool");
200 
201   sess = pcalloc(sub_pool, sizeof(struct date_session));
202   sess->pool = sub_pool;
203   sess->channel_id = channel_id;
204   sess->use_gmt = date_use_gmt;
205 
206   if (last) {
207     last->next = sess;
208     sess->prev = last;
209 
210   } else {
211     date_sessions = sess;
212   }
213 
214   return 0;
215 }
216 
sftp_date_close_session(uint32_t channel_id)217 int sftp_date_close_session(uint32_t channel_id) {
218   /* no-op */
219   return 0;
220 }
221