1 /*
2
3 sftp_util.c
4
5 Author: Pekka Riikonen <priikone@silcnet.org>
6
7 Copyright (C) 2001 - 2007 Pekka Riikonen
8
9 The contents of this file are subject to one of the Licenses specified
10 in the COPYING file; You may not use this file except in compliance
11 with the License.
12
13 The software distributed under the License is distributed on an "AS IS"
14 basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15 KIND, either expressed or implied. See the COPYING file for more
16 information.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcsftp.h"
23 #include "sftp_util.h"
24
25 /* Encodes a SFTP packet of type `packet' of length `len'. The variable
26 argument list is encoded as data payload to the buffer. Returns the
27 encoded packet or NULL on error. The caller must free the returned
28 buffer. If `packet_buf' is non-NULL then the new packet data is put
29 to that buffer instead of allocating new one. If the new data cannot
30 fit to `packet_buf' will be reallocated. */
31
silc_sftp_packet_encode(SilcSFTPPacket packet,SilcBuffer packet_buf,SilcUInt32 len,...)32 SilcBuffer silc_sftp_packet_encode(SilcSFTPPacket packet,
33 SilcBuffer packet_buf, SilcUInt32 len, ...)
34 {
35 SilcBuffer buffer;
36 va_list vp;
37
38 va_start(vp, len);
39 buffer = silc_sftp_packet_encode_vp(packet, packet_buf, len, vp);
40 va_end(vp);
41
42 return buffer;
43 }
44
45 /* Same as silc_sftp_packet_encode but takes the variable argument list
46 pointer as argument. */
47
silc_sftp_packet_encode_vp(SilcSFTPPacket packet,SilcBuffer packet_buf,SilcUInt32 len,va_list vp)48 SilcBuffer silc_sftp_packet_encode_vp(SilcSFTPPacket packet,
49 SilcBuffer packet_buf, SilcUInt32 len,
50 va_list vp)
51 {
52 SilcBuffer buffer;
53 bool dyn;
54 int ret;
55
56 if (packet_buf) {
57 if (silc_buffer_truelen(packet_buf) < 4 + 1 + len) {
58 packet_buf = silc_buffer_realloc(packet_buf, 4 + 1 + len);
59 if (!packet_buf)
60 return NULL;
61 }
62
63 buffer = packet_buf;
64 dyn = FALSE;
65 } else {
66 buffer = silc_buffer_alloc(4 + 1 + len);
67 if (!buffer)
68 return NULL;
69 dyn = TRUE;
70 }
71
72 silc_buffer_pull_tail(buffer, 4 + 1 + len);
73 silc_buffer_format(buffer,
74 SILC_STR_UI_INT(len),
75 SILC_STR_UI_CHAR(packet),
76 SILC_STR_END);
77 silc_buffer_pull(buffer, 5);
78
79 ret = silc_buffer_format_vp(buffer, vp);
80 if (ret < 0) {
81 if (dyn)
82 silc_buffer_free(buffer);
83 return NULL;
84 }
85
86 silc_buffer_push(buffer, 5);
87
88 return buffer;
89 }
90
91 /* Decodes the SFTP packet data `packet' and return the SFTP packet type.
92 The payload of the packet is returned to the `payload' pointer. Returns
93 0 if error occurred during decoding and -1 if partial packet was
94 received. */
95
silc_sftp_packet_decode(SilcBuffer packet,unsigned char ** payload,SilcUInt32 * payload_len)96 SilcSFTPPacket silc_sftp_packet_decode(SilcBuffer packet,
97 unsigned char **payload,
98 SilcUInt32 *payload_len)
99 {
100 SilcUInt32 len;
101 SilcUInt8 type;
102 int ret;
103
104 ret = silc_buffer_unformat(packet,
105 SILC_STR_UI_INT(&len),
106 SILC_STR_UI_CHAR(&type),
107 SILC_STR_END);
108 if (ret < 0)
109 return 0;
110
111 if (type < SILC_SFTP_INIT || type > SILC_SFTP_EXTENDED_REPLY)
112 return 0;
113
114 if (len > (silc_buffer_len(packet) - 5))
115 return -1;
116
117 silc_buffer_pull(packet, 5);
118 ret = silc_buffer_unformat(packet,
119 SILC_STR_UI_XNSTRING(payload, len),
120 SILC_STR_END);
121 if (ret < 0)
122 return 0;
123
124 silc_buffer_push(packet, 5);
125
126 *payload_len = len;
127
128 return (SilcSFTPPacket)type;
129 }
130
131 /* Encodes the SFTP attributes to a buffer and returns the allocated buffer.
132 The caller must free the buffer. */
133
silc_sftp_attr_encode(SilcSFTPAttributes attr)134 SilcBuffer silc_sftp_attr_encode(SilcSFTPAttributes attr)
135 {
136 SilcBuffer buffer;
137 int i, ret;
138 SilcUInt32 len = 4;
139
140 if (attr->flags & SILC_SFTP_ATTR_SIZE)
141 len += 8;
142 if (attr->flags & SILC_SFTP_ATTR_UIDGID)
143 len += 8;
144 if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS)
145 len += 4;
146 if (attr->flags & SILC_SFTP_ATTR_ACMODTIME)
147 len += 8;
148 if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
149 len += 4;
150 for (i = 0; i < attr->extended_count; i++) {
151 len += 8;
152 len += silc_buffer_len(attr->extended_type[i]);
153 len += silc_buffer_len(attr->extended_data[i]);
154 }
155 }
156
157 buffer = silc_buffer_alloc_size(len);
158 if (!buffer)
159 return NULL;
160
161 silc_buffer_format(buffer,
162 SILC_STR_UI_INT(attr->flags),
163 SILC_STR_END);
164 silc_buffer_pull(buffer, 4);
165
166 if (attr->flags & SILC_SFTP_ATTR_SIZE) {
167 silc_buffer_format(buffer,
168 SILC_STR_UI_INT64(attr->size),
169 SILC_STR_END);
170 silc_buffer_pull(buffer, 8);
171 }
172
173 if (attr->flags & SILC_SFTP_ATTR_UIDGID) {
174 silc_buffer_format(buffer,
175 SILC_STR_UI_INT(attr->uid),
176 SILC_STR_UI_INT(attr->gid),
177 SILC_STR_END);
178 silc_buffer_pull(buffer, 8);
179 }
180
181 if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS) {
182 silc_buffer_format(buffer,
183 SILC_STR_UI_INT(attr->permissions),
184 SILC_STR_END);
185 silc_buffer_pull(buffer, 4);
186 }
187
188 if (attr->flags & SILC_SFTP_ATTR_ACMODTIME) {
189 silc_buffer_format(buffer,
190 SILC_STR_UI_INT(attr->atime),
191 SILC_STR_UI_INT(attr->mtime),
192 SILC_STR_END);
193 silc_buffer_pull(buffer, 8);
194 }
195
196 if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
197 silc_buffer_format(buffer,
198 SILC_STR_UI_INT(attr->extended_count),
199 SILC_STR_END);
200 silc_buffer_pull(buffer, 4);
201
202 for (i = 0; i < attr->extended_count; i++) {
203 ret =
204 silc_buffer_format(
205 buffer,
206 SILC_STR_UI_INT(silc_buffer_len(attr->extended_type[i])),
207 SILC_STR_DATA(silc_buffer_data(attr->extended_type[i]),
208 silc_buffer_len(attr->extended_type[i])),
209 SILC_STR_UI_INT(silc_buffer_len(attr->extended_data[i])),
210 SILC_STR_DATA(silc_buffer_data(attr->extended_data[i]),
211 silc_buffer_len(attr->extended_data[i])),
212 SILC_STR_END);
213 silc_buffer_pull(buffer, ret);
214 }
215 }
216
217 silc_buffer_push(buffer, buffer->data - buffer->head);
218
219 return buffer;
220 }
221
222 /* Decodes SilcSFTPAttributes from the buffer `buffer'. Returns the allocated
223 attributes that the caller must free or NULL on error. */
224
silc_sftp_attr_decode(SilcBuffer buffer)225 SilcSFTPAttributes silc_sftp_attr_decode(SilcBuffer buffer)
226 {
227 SilcSFTPAttributes attr;
228
229 attr = silc_calloc(1, sizeof(*attr));
230 if (!attr)
231 return NULL;
232
233 if (silc_buffer_unformat(buffer,
234 SILC_STR_UI_INT(&attr->flags),
235 SILC_STR_END) < 0)
236 goto out;
237
238 silc_buffer_pull(buffer, 4);
239
240 if (attr->flags & SILC_SFTP_ATTR_SIZE) {
241 if (silc_buffer_unformat(buffer,
242 SILC_STR_UI_INT64(&attr->size),
243 SILC_STR_END) < 0)
244 goto out;
245
246 silc_buffer_pull(buffer, 8);
247 }
248
249 if (attr->flags & SILC_SFTP_ATTR_UIDGID) {
250 if (silc_buffer_unformat(buffer,
251 SILC_STR_UI_INT(&attr->uid),
252 SILC_STR_UI_INT(&attr->gid),
253 SILC_STR_END) < 0)
254 goto out;
255
256 silc_buffer_pull(buffer, 8);
257 }
258
259 if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS) {
260 if (silc_buffer_unformat(buffer,
261 SILC_STR_UI_INT(&attr->permissions),
262 SILC_STR_END) < 0)
263 goto out;
264
265 silc_buffer_pull(buffer, 4);
266 }
267
268 if (attr->flags & SILC_SFTP_ATTR_ACMODTIME) {
269 if (silc_buffer_unformat(buffer,
270 SILC_STR_UI_INT(&attr->atime),
271 SILC_STR_UI_INT(&attr->mtime),
272 SILC_STR_END) < 0)
273 goto out;
274
275 silc_buffer_pull(buffer, 8);
276 }
277
278 if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
279 int i;
280
281 if (silc_buffer_unformat(buffer,
282 SILC_STR_UI_INT(&attr->extended_count),
283 SILC_STR_END) < 0)
284 goto out;
285
286 silc_buffer_pull(buffer, 4);
287
288 attr->extended_type = silc_calloc(attr->extended_count,
289 sizeof(*attr->extended_type));
290 attr->extended_data = silc_calloc(attr->extended_count,
291 sizeof(*attr->extended_data));
292 if (!attr->extended_type || !attr->extended_data)
293 return NULL;
294
295 for (i = 0; i < attr->extended_count; i++) {
296 unsigned char *tmp, *tmp2;
297 SilcUInt32 tmp_len, tmp2_len;
298
299 if (silc_buffer_unformat(buffer,
300 SILC_STR_UI32_NSTRING(&tmp, &tmp_len),
301 SILC_STR_UI32_NSTRING(&tmp2, &tmp2_len),
302 SILC_STR_END) < 0)
303 goto out;
304
305 attr->extended_type[i] = silc_buffer_alloc(tmp_len);
306 attr->extended_data[i] = silc_buffer_alloc(tmp2_len);
307 if (!attr->extended_type[i] || !attr->extended_data[i])
308 return NULL;
309 silc_buffer_put(attr->extended_type[i], tmp, tmp_len);
310 silc_buffer_put(attr->extended_data[i], tmp2, tmp2_len);
311
312 silc_buffer_pull(buffer, tmp_len + 4 + tmp2_len + 4);
313 }
314 }
315
316 return attr;
317
318 out:
319 silc_sftp_attr_free(attr);
320 return NULL;
321 }
322
323 /* Frees the attributes context and its internals. */
324
silc_sftp_attr_free(SilcSFTPAttributes attr)325 void silc_sftp_attr_free(SilcSFTPAttributes attr)
326 {
327 int i;
328
329 for (i = 0; i < attr->extended_count; i++) {
330 silc_buffer_free(attr->extended_type[i]);
331 silc_buffer_free(attr->extended_data[i]);
332 }
333 silc_free(attr->extended_type);
334 silc_free(attr->extended_data);
335 silc_free(attr);
336 }
337
338 /* Adds an entry to the `name' context. */
339
silc_sftp_name_add(SilcSFTPName name,const char * short_name,const char * long_name,SilcSFTPAttributes attrs)340 void silc_sftp_name_add(SilcSFTPName name, const char *short_name,
341 const char *long_name, SilcSFTPAttributes attrs)
342 {
343 name->filename = silc_realloc(name->filename, sizeof(*name->filename) *
344 (name->count + 1));
345 name->long_filename = silc_realloc(name->long_filename,
346 sizeof(*name->long_filename) *
347 (name->count + 1));
348 name->attrs = silc_realloc(name->attrs, sizeof(*name->attrs) *
349 (name->count + 1));
350 if (!name->filename || !name->long_filename || !name->attrs)
351 return;
352
353 name->filename[name->count] = strdup(short_name);
354 name->long_filename[name->count] = strdup(long_name);
355 name->attrs[name->count] = attrs;
356 name->count++;
357 }
358
359 /* Encodes the SilcSFTPName to a buffer and returns the allocated buffer.
360 The caller must free the buffer. */
361
silc_sftp_name_encode(SilcSFTPName name)362 SilcBuffer silc_sftp_name_encode(SilcSFTPName name)
363 {
364 SilcBuffer buffer;
365 int i, len = 4;
366 SilcBuffer *attr_buf;
367
368 attr_buf = silc_calloc(name->count, sizeof(*attr_buf));
369 if (!attr_buf)
370 return NULL;
371
372 for (i = 0; i < name->count; i++) {
373 len += (8 + strlen(name->filename[i]) + strlen(name->long_filename[i]));
374 attr_buf[i] = silc_sftp_attr_encode(name->attrs[i]);
375 if (!attr_buf[i])
376 return NULL;
377 len += silc_buffer_len(attr_buf[i]);
378 }
379
380 buffer = silc_buffer_alloc(len);
381 if (!buffer)
382 return NULL;
383 silc_buffer_end(buffer);
384
385 silc_buffer_format(buffer,
386 SILC_STR_UI_INT(name->count),
387 SILC_STR_END);
388 silc_buffer_pull(buffer, 4);
389
390 for (i = 0; i < name->count; i++) {
391 len =
392 silc_buffer_format(buffer,
393 SILC_STR_UI_INT(strlen(name->filename[i])),
394 SILC_STR_UI32_STRING(name->filename[i]),
395 SILC_STR_UI_INT(strlen(name->long_filename[i])),
396 SILC_STR_UI32_STRING(name->long_filename[i]),
397 SILC_STR_DATA(silc_buffer_data(attr_buf[i]),
398 silc_buffer_len(attr_buf[i])),
399 SILC_STR_END);
400
401 silc_buffer_pull(buffer, len);
402 silc_free(attr_buf[i]);
403 }
404 silc_free(attr_buf);
405
406 silc_buffer_push(buffer, buffer->data - buffer->head);
407
408 return buffer;
409 }
410
411 /* Decodes a SilcSFTPName structure from the `buffer' that must include
412 `count' many name, longname and attribute values. Returns the allocated
413 structure or NULL on error. */
414
silc_sftp_name_decode(SilcUInt32 count,SilcBuffer buffer)415 SilcSFTPName silc_sftp_name_decode(SilcUInt32 count, SilcBuffer buffer)
416 {
417 SilcSFTPName name;
418 int i;
419 int ret;
420
421 name = silc_calloc(1, sizeof(*name));
422 if (!name)
423 return NULL;
424 name->filename = silc_calloc(count, sizeof(*name->filename));
425 name->long_filename = silc_calloc(count, sizeof(*name->filename));
426 name->attrs = silc_calloc(count, sizeof(*name->attrs));
427 if (!name->filename || !name->long_filename || !name->attrs) {
428 silc_sftp_name_free(name);
429 return NULL;
430 }
431 name->count = count;
432
433 for (i = 0; i < count; i++) {
434 ret =
435 silc_buffer_unformat(buffer,
436 SILC_STR_UI32_STRING_ALLOC(&name->filename[i]),
437 SILC_STR_UI32_STRING_ALLOC(&name->long_filename[i]),
438 SILC_STR_END);
439 if (ret < 0) {
440 silc_sftp_name_free(name);
441 return NULL;
442 }
443
444 silc_buffer_pull(buffer, ret);
445
446 /* Decode attributes, this will pull the `buffer' to correct place
447 for next round automatically. */
448 name->attrs[i] = silc_sftp_attr_decode(buffer);
449 if (!name->attrs[i]) {
450 silc_sftp_name_free(name);
451 return NULL;
452 }
453 }
454
455 return name;
456 }
457
458 /* Frees the name context and its internals. */
459
silc_sftp_name_free(SilcSFTPName name)460 void silc_sftp_name_free(SilcSFTPName name)
461 {
462 int i;
463
464 for (i = 0; i < name->count; i++) {
465 silc_free(name->filename[i]);
466 silc_free(name->long_filename[i]);
467 silc_sftp_attr_free(name->attrs[i]);
468 }
469
470 silc_free(name->filename);
471 silc_free(name->long_filename);
472 silc_free(name->attrs);
473 silc_free(name);
474 }
475
476 /* Maps errno to SFTP status message. */
477
silc_sftp_map_errno(int err)478 SilcSFTPStatus silc_sftp_map_errno(int err)
479 {
480 SilcSFTPStatus ret;
481
482 switch (err) {
483 case 0:
484 ret = SILC_SFTP_STATUS_OK;
485 break;
486 case ENOENT:
487 case ENOTDIR:
488 case EBADF:
489 ret = SILC_SFTP_STATUS_NO_SUCH_FILE;
490 break;
491 case EPERM:
492 case EACCES:
493 case EFAULT:
494 ret = SILC_SFTP_STATUS_PERMISSION_DENIED;
495 break;
496 case ENAMETOOLONG:
497 case EINVAL:
498 ret = SILC_SFTP_STATUS_BAD_MESSAGE;
499 break;
500 default:
501 ret = SILC_SFTP_STATUS_FAILURE;
502 break;
503 }
504
505 return ret;
506 }
507