1 /*
2 Copyright 2021 Northern.tech AS
3
4 This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 3.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 To the extent this program is licensed as part of the Enterprise
20 versions of CFEngine, the applicable Commercial Open Source License
21 (COSL) may apply to this file if you as a licensee so wish it. See
22 included file COSL.txt.
23 */
24
25 #include <protocol.h>
26
27 #include <client_code.h>
28 #include <client_protocol.h>
29 #include <definitions.h>
30 #include <net.h>
31 #include <stat_cache.h>
32 #include <string_lib.h>
33 #include <tls_generic.h>
34
ProtocolOpenDir(AgentConnection * conn,const char * path)35 Seq *ProtocolOpenDir(AgentConnection *conn, const char *path)
36 {
37 assert(conn != NULL);
38 assert(path != NULL);
39
40 char buf[CF_MSGSIZE] = {0};
41 int tosend = snprintf(buf, CF_MSGSIZE, "OPENDIR %s", path);
42 if (tosend < 0 || tosend >= CF_MSGSIZE)
43 {
44 return NULL;
45 }
46
47 int ret = SendTransaction(conn->conn_info, buf, tosend, CF_DONE);
48 if (ret == -1)
49 {
50 return NULL;
51 }
52
53 Seq *seq = SeqNew(0, free);
54
55 int more = 1;
56 while (more != 0)
57 {
58 int len = ReceiveTransaction(conn->conn_info, buf, &more);
59 if (len == -1)
60 {
61 break;
62 }
63
64 if (BadProtoReply(buf))
65 {
66 Log(LOG_LEVEL_ERR, "Protocol error: %s", buf);
67 SeqDestroy(seq);
68 return NULL;
69 }
70
71 /*
72 * Iterates over each string in the received transaction and appends
73 * it to the Seq list, until it either finds the CFD_TERMINATOR
74 * string, or reaches the end of the message.
75 */
76 for (int i = 0; i < len && buf[i] != '\0'; i += strlen(buf + i) + 1)
77 {
78 if (StringEqualN(buf + i, CFD_TERMINATOR,
79 sizeof(CFD_TERMINATOR) - 1))
80 {
81 more = 0;
82 break;
83 }
84
85 char *str = xstrdup(buf + i);
86 SeqAppend(seq, str);
87 }
88 }
89
90 return seq;
91 }
92
ProtocolGet(AgentConnection * conn,const char * remote_path,const char * local_path,const uint32_t file_size,int perms)93 bool ProtocolGet(AgentConnection *conn, const char *remote_path,
94 const char *local_path, const uint32_t file_size, int perms)
95 {
96 assert(conn != NULL);
97 assert(remote_path != NULL);
98 assert(local_path != NULL);
99 assert(file_size != 0);
100
101 perms = (perms == 0) ? CF_PERMS_DEFAULT : perms;
102
103 unlink(local_path);
104 FILE *file_ptr = safe_fopen_create_perms(local_path, "wx", perms);
105 if (file_ptr == NULL)
106 {
107 Log(LOG_LEVEL_WARNING, "Failed to open file %s (fopen: %s)",
108 local_path, GetErrorStr());
109 return false;
110 }
111
112 char buf[CF_MSGSIZE] = {0};
113 int to_send = snprintf(buf, CF_MSGSIZE, "GET %d %s",
114 CF_MSGSIZE, remote_path);
115
116
117 int ret = SendTransaction(conn->conn_info, buf, to_send, CF_DONE);
118 if (ret == -1)
119 {
120 Log(LOG_LEVEL_WARNING, "Failed to send request for remote file %s:%s",
121 conn->this_server, remote_path);
122 unlink(local_path);
123 fclose(file_ptr);
124 return false;
125 }
126
127 char cfchangedstr[sizeof(CF_CHANGEDSTR1 CF_CHANGEDSTR2)];
128 snprintf(cfchangedstr, sizeof(cfchangedstr), "%s%s",
129 CF_CHANGEDSTR1, CF_CHANGEDSTR2);
130
131 bool success = true;
132 uint32_t received_bytes = 0;
133 while (received_bytes < file_size)
134 {
135 int len = TLSRecv(conn->conn_info->ssl, buf, CF_MSGSIZE);
136 if (len == -1)
137 {
138 Log(LOG_LEVEL_WARNING, "Failed to GET file %s:%s",
139 conn->this_server, remote_path);
140 success = false;
141 break;
142 }
143 else if (len > CF_MSGSIZE)
144 {
145 Log(LOG_LEVEL_WARNING,
146 "Incorrect length of incoming packet "
147 "while retrieving %s:%s, %d > %d",
148 conn->this_server, remote_path, len, CF_MSGSIZE);
149 success = false;
150 break;
151 }
152
153 if (BadProtoReply(buf))
154 {
155 Log(LOG_LEVEL_ERR,
156 "Error from server while retrieving file %s:%s: %s",
157 conn->this_server, remote_path, buf);
158 success = false;
159 break;
160 }
161
162 if (StringEqualN(buf, cfchangedstr, sizeof(cfchangedstr) - 1))
163 {
164 Log(LOG_LEVEL_ERR,
165 "Remote file %s:%s changed during file transfer",
166 conn->this_server, remote_path);
167 success = false;
168 break;
169 }
170
171 ret = fwrite(buf, sizeof(char), len, file_ptr);
172 if (ret < 0)
173 {
174 Log(LOG_LEVEL_ERR,
175 "Failed to write during retrieval of file %s:%s (fwrite: %s)",
176 conn->this_server, remote_path, GetErrorStr());
177 success = false;
178 break;
179 }
180
181 received_bytes += len;
182 }
183
184 if (!success)
185 {
186 unlink(local_path);
187 }
188
189 fclose(file_ptr);
190 return success;
191 }
192
ProtocolStatGet(AgentConnection * conn,const char * remote_path,const char * local_path,int perms)193 bool ProtocolStatGet(AgentConnection *conn, const char *remote_path,
194 const char *local_path, int perms)
195 {
196 assert(conn != NULL);
197 assert(remote_path != NULL);
198
199 struct stat sb;
200 bool ret = ProtocolStat(conn, remote_path, &sb);
201 if (!ret)
202 {
203 Log(LOG_LEVEL_ERR,
204 "Failed to stat remote file %s:%s",
205 conn->this_server, remote_path);
206 return false;
207 }
208
209 return ProtocolGet(conn, remote_path, local_path, sb.st_size, perms);
210 }
211
ProtocolStat(AgentConnection * const conn,const char * const remote_path,struct stat * const stat_buf)212 bool ProtocolStat(AgentConnection *const conn, const char *const remote_path,
213 struct stat *const stat_buf)
214 {
215 assert(conn != NULL);
216 assert(remote_path != NULL);
217 assert(stat_buf != NULL);
218
219 time_t tloc = time(NULL);
220 if (tloc == (time_t) -1)
221 {
222 Log(LOG_LEVEL_WARNING,
223 "Couldn't read system clock, defaulting to 0 in case server "
224 "does not care about clock differences (time: %s)",
225 GetErrorStr());
226 tloc = 0;
227 }
228
229 char buf[CF_BUFSIZE] = {0};
230 int to_send = snprintf(buf, CF_BUFSIZE, "SYNCH %jd STAT %s",
231 (intmax_t) tloc, remote_path);
232
233 int ret = SendTransaction(conn->conn_info, buf, to_send, CF_DONE);
234 if (ret == -1)
235 {
236 Log(LOG_LEVEL_WARNING,
237 "Could not send stat request for remote file %s:%s.",
238 conn->this_server, remote_path);
239 return false;
240 }
241
242 int recvd_len = ReceiveTransaction(conn->conn_info, buf, NULL) == -1;
243 if (recvd_len == -1)
244 {
245 Log(LOG_LEVEL_WARNING,
246 "Receiving file statistics from %s failed!",
247 conn->this_server);
248 return false;
249 }
250
251 if (BadProtoReply(buf))
252 {
253 Log(LOG_LEVEL_WARNING,
254 "Could not stat remote file %s:%s, response: %s",
255 conn->this_server, remote_path, buf);
256 return false;
257 }
258
259 if (!OKProtoReply(buf))
260 {
261 Log(LOG_LEVEL_WARNING,
262 "Illegal response from server while statting %s:%s",
263 conn->this_server, remote_path);
264 return false;
265 }
266
267 Stat cf_stat;
268 ret = StatParseResponse(buf, &cf_stat);
269 if (!ret)
270 {
271 Log(LOG_LEVEL_WARNING,
272 "Failed to parse the response from the server "
273 "while statting %s:%s",
274 conn->this_server, remote_path);
275 return false;
276 }
277
278 mode_t file_type = FileTypeToMode(cf_stat.cf_type);
279 if (file_type == 0)
280 {
281 Log(LOG_LEVEL_VERBOSE,
282 "Invalid file type identifier for file %s:%s, %u",
283 conn->this_server, remote_path, cf_stat.cf_type);
284 return false;
285 }
286
287 stat_buf->st_mode = file_type | cf_stat.cf_mode;
288 stat_buf->st_uid = cf_stat.cf_uid;
289 stat_buf->st_gid = cf_stat.cf_gid;
290 stat_buf->st_size = cf_stat.cf_size;
291 stat_buf->st_mtime = cf_stat.cf_mtime;
292 stat_buf->st_ctime = cf_stat.cf_ctime;
293 stat_buf->st_atime = cf_stat.cf_atime;
294 stat_buf->st_ino = cf_stat.cf_ino;
295 stat_buf->st_dev = cf_stat.cf_dev;
296 stat_buf->st_nlink = cf_stat.cf_nlink;
297
298 // Receive link destination, but do nothing
299 ReceiveTransaction(conn->conn_info, buf, NULL);
300
301 return true;
302 }
303