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