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