17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * Copyright (c) 2001 Markus Friedl. All rights reserved.
37c478bd9Sstevel@tonic-gate * Copyright (c) 2001 Damien Miller. All rights reserved.
47c478bd9Sstevel@tonic-gate *
57c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without
67c478bd9Sstevel@tonic-gate * modification, are permitted provided that the following conditions
77c478bd9Sstevel@tonic-gate * are met:
87c478bd9Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright
97c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer.
107c478bd9Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright
117c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the
127c478bd9Sstevel@tonic-gate * documentation and/or other materials provided with the distribution.
137c478bd9Sstevel@tonic-gate *
147c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
157c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
167c478bd9Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
177c478bd9Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
187c478bd9Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
197c478bd9Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
207c478bd9Sstevel@tonic-gate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
217c478bd9Sstevel@tonic-gate * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
227c478bd9Sstevel@tonic-gate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
237c478bd9Sstevel@tonic-gate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
247c478bd9Sstevel@tonic-gate */
257c478bd9Sstevel@tonic-gate
26*90685d2cSjp161948 /* $OpenBSD: sftp-common.c,v 1.20 2006/08/03 03:34:42 deraadt Exp $ */
277c478bd9Sstevel@tonic-gate
287c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
297c478bd9Sstevel@tonic-gate
30*90685d2cSjp161948 #include "includes.h"
31*90685d2cSjp161948
32*90685d2cSjp161948 #include <sys/types.h>
33*90685d2cSjp161948 #include <sys/stat.h>
34*90685d2cSjp161948 #include <sys/param.h>
35*90685d2cSjp161948
36*90685d2cSjp161948 #include <grp.h>
37*90685d2cSjp161948 #include <pwd.h>
38*90685d2cSjp161948 #include <stdio.h>
39*90685d2cSjp161948 #include <string.h>
40*90685d2cSjp161948 #include <time.h>
41*90685d2cSjp161948 #include <stdarg.h>
42*90685d2cSjp161948
43*90685d2cSjp161948 #include "xmalloc.h"
447c478bd9Sstevel@tonic-gate #include "buffer.h"
457c478bd9Sstevel@tonic-gate #include "bufaux.h"
467c478bd9Sstevel@tonic-gate #include "log.h"
477c478bd9Sstevel@tonic-gate
487c478bd9Sstevel@tonic-gate #include "sftp.h"
497c478bd9Sstevel@tonic-gate #include "sftp-common.h"
507c478bd9Sstevel@tonic-gate
517c478bd9Sstevel@tonic-gate /* Clear contents of attributes structure */
527c478bd9Sstevel@tonic-gate void
attrib_clear(Attrib * a)537c478bd9Sstevel@tonic-gate attrib_clear(Attrib *a)
547c478bd9Sstevel@tonic-gate {
557c478bd9Sstevel@tonic-gate a->flags = 0;
567c478bd9Sstevel@tonic-gate a->size = 0;
577c478bd9Sstevel@tonic-gate a->uid = 0;
587c478bd9Sstevel@tonic-gate a->gid = 0;
597c478bd9Sstevel@tonic-gate a->perm = 0;
607c478bd9Sstevel@tonic-gate a->atime = 0;
617c478bd9Sstevel@tonic-gate a->mtime = 0;
627c478bd9Sstevel@tonic-gate }
637c478bd9Sstevel@tonic-gate
647c478bd9Sstevel@tonic-gate /* Convert from struct stat to filexfer attribs */
657c478bd9Sstevel@tonic-gate void
stat_to_attrib(const struct stat * st,Attrib * a)66*90685d2cSjp161948 stat_to_attrib(const struct stat *st, Attrib *a)
677c478bd9Sstevel@tonic-gate {
687c478bd9Sstevel@tonic-gate attrib_clear(a);
697c478bd9Sstevel@tonic-gate a->flags = 0;
707c478bd9Sstevel@tonic-gate a->flags |= SSH2_FILEXFER_ATTR_SIZE;
717c478bd9Sstevel@tonic-gate a->size = st->st_size;
727c478bd9Sstevel@tonic-gate a->flags |= SSH2_FILEXFER_ATTR_UIDGID;
737c478bd9Sstevel@tonic-gate a->uid = st->st_uid;
747c478bd9Sstevel@tonic-gate a->gid = st->st_gid;
757c478bd9Sstevel@tonic-gate a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
767c478bd9Sstevel@tonic-gate a->perm = st->st_mode;
777c478bd9Sstevel@tonic-gate a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME;
787c478bd9Sstevel@tonic-gate a->atime = st->st_atime;
797c478bd9Sstevel@tonic-gate a->mtime = st->st_mtime;
807c478bd9Sstevel@tonic-gate }
817c478bd9Sstevel@tonic-gate
827c478bd9Sstevel@tonic-gate /* Convert from filexfer attribs to struct stat */
837c478bd9Sstevel@tonic-gate void
attrib_to_stat(const Attrib * a,struct stat * st)84*90685d2cSjp161948 attrib_to_stat(const Attrib *a, struct stat *st)
857c478bd9Sstevel@tonic-gate {
867c478bd9Sstevel@tonic-gate memset(st, 0, sizeof(*st));
877c478bd9Sstevel@tonic-gate
887c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
897c478bd9Sstevel@tonic-gate st->st_size = a->size;
907c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
917c478bd9Sstevel@tonic-gate st->st_uid = a->uid;
927c478bd9Sstevel@tonic-gate st->st_gid = a->gid;
937c478bd9Sstevel@tonic-gate }
947c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
957c478bd9Sstevel@tonic-gate st->st_mode = a->perm;
967c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
977c478bd9Sstevel@tonic-gate st->st_atime = a->atime;
987c478bd9Sstevel@tonic-gate st->st_mtime = a->mtime;
997c478bd9Sstevel@tonic-gate }
1007c478bd9Sstevel@tonic-gate }
1017c478bd9Sstevel@tonic-gate
1027c478bd9Sstevel@tonic-gate /* Decode attributes in buffer */
1037c478bd9Sstevel@tonic-gate Attrib *
decode_attrib(Buffer * b)1047c478bd9Sstevel@tonic-gate decode_attrib(Buffer *b)
1057c478bd9Sstevel@tonic-gate {
1067c478bd9Sstevel@tonic-gate static Attrib a;
1077c478bd9Sstevel@tonic-gate
1087c478bd9Sstevel@tonic-gate attrib_clear(&a);
1097c478bd9Sstevel@tonic-gate a.flags = buffer_get_int(b);
1107c478bd9Sstevel@tonic-gate if (a.flags & SSH2_FILEXFER_ATTR_SIZE)
1117c478bd9Sstevel@tonic-gate a.size = buffer_get_int64(b);
1127c478bd9Sstevel@tonic-gate if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
1137c478bd9Sstevel@tonic-gate a.uid = buffer_get_int(b);
1147c478bd9Sstevel@tonic-gate a.gid = buffer_get_int(b);
1157c478bd9Sstevel@tonic-gate }
1167c478bd9Sstevel@tonic-gate if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1177c478bd9Sstevel@tonic-gate a.perm = buffer_get_int(b);
1187c478bd9Sstevel@tonic-gate if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1197c478bd9Sstevel@tonic-gate a.atime = buffer_get_int(b);
1207c478bd9Sstevel@tonic-gate a.mtime = buffer_get_int(b);
1217c478bd9Sstevel@tonic-gate }
1227c478bd9Sstevel@tonic-gate /* vendor-specific extensions */
1237c478bd9Sstevel@tonic-gate if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) {
1247c478bd9Sstevel@tonic-gate char *type, *data;
1257c478bd9Sstevel@tonic-gate int i, count;
1267c478bd9Sstevel@tonic-gate
1277c478bd9Sstevel@tonic-gate count = buffer_get_int(b);
1287c478bd9Sstevel@tonic-gate for (i = 0; i < count; i++) {
1297c478bd9Sstevel@tonic-gate type = buffer_get_string(b, NULL);
1307c478bd9Sstevel@tonic-gate data = buffer_get_string(b, NULL);
1317c478bd9Sstevel@tonic-gate debug3("Got file attribute \"%s\"", type);
1327c478bd9Sstevel@tonic-gate xfree(type);
1337c478bd9Sstevel@tonic-gate xfree(data);
1347c478bd9Sstevel@tonic-gate }
1357c478bd9Sstevel@tonic-gate }
1367c478bd9Sstevel@tonic-gate return &a;
1377c478bd9Sstevel@tonic-gate }
1387c478bd9Sstevel@tonic-gate
1397c478bd9Sstevel@tonic-gate /* Encode attributes to buffer */
1407c478bd9Sstevel@tonic-gate void
encode_attrib(Buffer * b,const Attrib * a)141*90685d2cSjp161948 encode_attrib(Buffer *b, const Attrib *a)
1427c478bd9Sstevel@tonic-gate {
1437c478bd9Sstevel@tonic-gate buffer_put_int(b, a->flags);
1447c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
1457c478bd9Sstevel@tonic-gate buffer_put_int64(b, a->size);
1467c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
1477c478bd9Sstevel@tonic-gate buffer_put_int(b, a->uid);
1487c478bd9Sstevel@tonic-gate buffer_put_int(b, a->gid);
1497c478bd9Sstevel@tonic-gate }
1507c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1517c478bd9Sstevel@tonic-gate buffer_put_int(b, a->perm);
1527c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1537c478bd9Sstevel@tonic-gate buffer_put_int(b, a->atime);
1547c478bd9Sstevel@tonic-gate buffer_put_int(b, a->mtime);
1557c478bd9Sstevel@tonic-gate }
1567c478bd9Sstevel@tonic-gate }
1577c478bd9Sstevel@tonic-gate
1587c478bd9Sstevel@tonic-gate /* Convert from SSH2_FX_ status to text error message */
1597c478bd9Sstevel@tonic-gate const char *
fx2txt(int status)1607c478bd9Sstevel@tonic-gate fx2txt(int status)
1617c478bd9Sstevel@tonic-gate {
1627c478bd9Sstevel@tonic-gate switch (status) {
1637c478bd9Sstevel@tonic-gate case SSH2_FX_OK:
164*90685d2cSjp161948 return(gettext("No error"));
1657c478bd9Sstevel@tonic-gate case SSH2_FX_EOF:
166*90685d2cSjp161948 return(gettext("End of file"));
1677c478bd9Sstevel@tonic-gate case SSH2_FX_NO_SUCH_FILE:
168*90685d2cSjp161948 return(gettext("No such file or directory"));
1697c478bd9Sstevel@tonic-gate case SSH2_FX_PERMISSION_DENIED:
170*90685d2cSjp161948 return(gettext("Permission denied"));
1717c478bd9Sstevel@tonic-gate case SSH2_FX_FAILURE:
172*90685d2cSjp161948 return(gettext("Failure"));
1737c478bd9Sstevel@tonic-gate case SSH2_FX_BAD_MESSAGE:
174*90685d2cSjp161948 return(gettext("Bad message"));
1757c478bd9Sstevel@tonic-gate case SSH2_FX_NO_CONNECTION:
176*90685d2cSjp161948 return(gettext("No connection"));
1777c478bd9Sstevel@tonic-gate case SSH2_FX_CONNECTION_LOST:
178*90685d2cSjp161948 return(gettext("Connection lost"));
1797c478bd9Sstevel@tonic-gate case SSH2_FX_OP_UNSUPPORTED:
180*90685d2cSjp161948 return(gettext("Operation unsupported"));
1817c478bd9Sstevel@tonic-gate default:
182*90685d2cSjp161948 return(gettext("Unknown status"));
1837c478bd9Sstevel@tonic-gate }
1847c478bd9Sstevel@tonic-gate /* NOTREACHED */
1857c478bd9Sstevel@tonic-gate }
1867c478bd9Sstevel@tonic-gate
1877c478bd9Sstevel@tonic-gate /*
1887c478bd9Sstevel@tonic-gate * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh
1897c478bd9Sstevel@tonic-gate */
1907c478bd9Sstevel@tonic-gate char *
ls_file(const char * name,const struct stat * st,int remote)191*90685d2cSjp161948 ls_file(const char *name, const struct stat *st, int remote)
1927c478bd9Sstevel@tonic-gate {
1937c478bd9Sstevel@tonic-gate int ulen, glen, sz = 0;
1947c478bd9Sstevel@tonic-gate struct passwd *pw;
1957c478bd9Sstevel@tonic-gate struct group *gr;
1967c478bd9Sstevel@tonic-gate struct tm *ltime = localtime(&st->st_mtime);
1977c478bd9Sstevel@tonic-gate char *user, *group;
1987c478bd9Sstevel@tonic-gate char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1];
1997c478bd9Sstevel@tonic-gate
2007c478bd9Sstevel@tonic-gate strmode(st->st_mode, mode);
2017c478bd9Sstevel@tonic-gate if (!remote && (pw = getpwuid(st->st_uid)) != NULL) {
2027c478bd9Sstevel@tonic-gate user = pw->pw_name;
2037c478bd9Sstevel@tonic-gate } else {
2047c478bd9Sstevel@tonic-gate snprintf(ubuf, sizeof ubuf, "%u", (u_int)st->st_uid);
2057c478bd9Sstevel@tonic-gate user = ubuf;
2067c478bd9Sstevel@tonic-gate }
2077c478bd9Sstevel@tonic-gate if (!remote && (gr = getgrgid(st->st_gid)) != NULL) {
2087c478bd9Sstevel@tonic-gate group = gr->gr_name;
2097c478bd9Sstevel@tonic-gate } else {
2107c478bd9Sstevel@tonic-gate snprintf(gbuf, sizeof gbuf, "%u", (u_int)st->st_gid);
2117c478bd9Sstevel@tonic-gate group = gbuf;
2127c478bd9Sstevel@tonic-gate }
2137c478bd9Sstevel@tonic-gate if (ltime != NULL) {
2147c478bd9Sstevel@tonic-gate if (time(NULL) - st->st_mtime < (365*24*60*60)/2)
2157c478bd9Sstevel@tonic-gate sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime);
2167c478bd9Sstevel@tonic-gate else
2177c478bd9Sstevel@tonic-gate sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime);
2187c478bd9Sstevel@tonic-gate }
2197c478bd9Sstevel@tonic-gate if (sz == 0)
2207c478bd9Sstevel@tonic-gate tbuf[0] = '\0';
2217c478bd9Sstevel@tonic-gate ulen = MAX(strlen(user), 8);
2227c478bd9Sstevel@tonic-gate glen = MAX(strlen(group), 8);
223*90685d2cSjp161948 snprintf(buf, sizeof buf, "%s %3u %-*s %-*s %8llu %s %s", mode,
224*90685d2cSjp161948 (u_int)st->st_nlink, ulen, user, glen, group,
225*90685d2cSjp161948 (unsigned long long)st->st_size, tbuf, name);
2267c478bd9Sstevel@tonic-gate return xstrdup(buf);
2277c478bd9Sstevel@tonic-gate }
228