/* ****************************************************************************** * SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipssh_sftp.c,v $ $Revision: 1.89.10.13 $ * $Source: /home/interpeak/CVSRoot/ipssh/src/ipssh_sftp.c,v $ * $Author: yli10 $ $Date: 2019-09-06 06:45:53 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Roger Boden ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #ifdef IPSSH_USE_SFTP /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #include "ipssh_h.h" #include "ipssh.h" #include "ipssh_transport.h" #include "ipssh_connection.h" #include "ipssh_sftp.h" #include "ipssh_buffer.h" #include #include #include #include #include #include #include /* **************************************************************************** * 4 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 5 DEFINES **************************************************************************** */ #define IPSSH_SFTP_READDIR_BUF_SIZE 4*1024 #define IPSSH_FILE_HANDLE 1 #define IPSSH_DIR_HANDLE 2 #define IPSSH_S_IFDIR 0x4000 #define IPSSH_S_IFREG 0x8000 /* **************************************************************************** * 6 TYPES **************************************************************************** */ /* External handle, given to client */ struct Ipssh_sftp_ext_handle_st { Ip_u32 id; Ip_u8 cookie[8]; Ip_u8 mac[8]; }; typedef struct Ipssh_sftp_ext_handle_st Ipssh_sftp_ext_handle; struct Ipssh_sftp_int_handle_st { Ipcom_list list; Ip_u32 type; Ip_u32 id; char* path; Ip_s32 eof; IP_DIR* dir; Ip_fd fd; Ip_u64 read_pos; Ip_u64 write_pos; Ipssh_sftp_func_cb* funcs; }; typedef struct Ipssh_sftp_int_handle_st Ipssh_sftp_int_handle; typedef Ipcom_list Ipssh_sftp_handle_map; /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* sftp message handlers */ IP_GLOBAL Ip_err ipssh_msg_fxp_init(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_opendir(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_readdir(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_open(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_lstat(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_setstat(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_read(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_write(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_realpath(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_close(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_eof(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_remove(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_mkdir(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_rmdir(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); IP_GLOBAL Ip_err ipssh_msg_fxp_rename(Ipssh_channel* channel, Ipcom_buffer* pkt_buf); /* Send message functions */ IP_GLOBAL Ip_err ipssh_send_fxp_version(Ipssh_channel* channel); IP_GLOBAL Ip_err ipssh_send_fxp_name(Ipssh_channel* channel, Ip_u32 req_id, char* path); IP_GLOBAL Ip_err ipssh_send_fxp_handle(Ipssh_channel* channel, Ip_u32 req_id, Ipssh_sftp_ext_handle* handle, Ip_u32 handle_len); IP_STATIC Ip_err ipssh_sftp_msg_handler(Ipssh_channel* channel, Ipcom_buffer* msg); /* **************************************************************************** * 8 DATA **************************************************************************** */ IP_STATIC Ip_u8 ipssh_handle_cookie[8] = { 4,7,1,7,4,7,1,7 }; /* **************************************************************************** * 9 LOCAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipssh_get_file_priv *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC void ipssh_get_file_priv(Ip_mode_t mode, char *p) { /* print type */ if(IP_S_ISDIR(mode)) /* directory */ *p++ = 'd'; #ifdef IP_S_ISCHR else if(IP_S_ISCHR(mode)) /* character special */ *p++ = 'c'; #endif #ifdef IP_S_ISBLK else if(IP_S_ISBLK(mode)) /* block special */ *p++ = 'b'; #endif else if(IP_S_ISREG(mode)) /* regular */ *p++ = '-'; #ifdef IP_S_ISLNK else if(IP_S_ISLNK(mode)) /* symbolic link */ *p++ = 'l'; #endif else /* unknown */ *p++ = '?'; /* usr */ if(mode & IP_S_IRUSR) *p++ = 'r'; else *p++ = '-'; if(mode & IP_S_IWUSR) *p++ = 'w'; else *p++ = '-'; if(mode & IP_S_IXUSR) *p++ = 'x'; else *p++ = '-'; /* group */ if (mode & IP_S_IRGRP) *p++ = 'r'; else *p++ = '-'; if (mode & IP_S_IWGRP) *p++ = 'w'; else *p++ = '-'; if(mode & IP_S_IXGRP) *p++ = 'x'; else *p++ = '-'; /* other */ if (mode & IP_S_IROTH) *p++ = 'r'; else *p++ = '-'; if (mode & IP_S_IWOTH) *p++ = 'w'; else *p++ = '-'; if(mode & IP_S_IXOTH) *p++ = 'x'; else *p++ = '-'; *p = '\0'; } /* *=========================================================================== * ipssh_make_long_name *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC void ipssh_make_long_name(const char* path, const char* filename, struct Ip_stat* file_stats, char* long_list) { char *tmp_path; char file_priv[11]; struct Ip_tm *tm; tmp_path = ipcom_malloc(ipcom_strlen(path) + 1 + ipcom_strlen(filename)+1); if(!tmp_path) { ipcom_strcpy(long_list,filename); return; } ipcom_strcpy(tmp_path, path); ipcom_strcat(tmp_path, "/"); ipcom_strcat(tmp_path, filename); ipssh_get_file_priv(file_stats->st_mode, file_priv); tm = (struct Ip_tm *)ipcom_localtime((void *)&(file_stats->st_mtime)); /*lint !e64*/ if (tm != IP_NULL) ipcom_sprintf(long_list, "%10s %3d %-4ld %-4ld %-8ld %04d-%02d-%02d %02d:%02d %s", file_priv, 1, file_stats->st_uid, file_stats->st_gid, file_stats->st_size, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, filename); ipcom_free(tmp_path); } /* *=========================================================================== * ipssh_pack_stat_data *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_pack_stat_data(Ipcom_buffer* pkt, struct Ip_stat* stat) { return ipssh_pack_attr(pkt, stat, IPSSH_FILEXFER_ATTR_SIZE| IPSSH_FILEXFER_ATTR_ACMODTIME| IPSSH_FILEXFER_ATTR_PERMISSIONS| IPSSH_FILEXFER_ATTR_UIDGID); } /* *=========================================================================== * ipssh_conv_flags *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_u32 ipssh_conv_flags(Ip_u32 flags) { Ip_u32 ret = 0; if((flags & IPSSH_FXF_READ) && (flags & IPSSH_FXF_WRITE)) ret |= IP_O_RDWR; if(flags & IPSSH_FXF_READ) ret |= IP_O_RDONLY; if(flags & IPSSH_FXF_WRITE) ret |= IP_O_WRONLY; if(flags & IPSSH_FXF_APPEND) ret |= IP_O_APPEND; if(flags & IPSSH_FXF_CREAT) ret |= IP_O_CREAT; #ifdef IP_O_EXCL if(flags & IPSSH_FXF_EXCL) ret |= IP_O_EXCL; #endif #ifdef IP_O_TRUNC if(flags & IPSSH_FXF_TRUNC) ret |= IP_O_TRUNC; #endif return ret; } /* *=========================================================================== * ipssh_make_ext_int_paths *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_make_ext_int_paths(char* org_path, char* start_dir, Ipcom_shell_ctx* sh_ctx, char** ext_path, char** int_path) { char* session_root_dir = IP_NULL; Ip_err ret; char start_dir_buf[IPSSH_SFTP_MAX_PATH]; if (ipcom_strcmp(org_path, ".") != 0 && org_path[0] != '/') return IPCOM_ERR_INVALID_ARG; ret = ipcom_shell_ctx_get(sh_ctx, IPSSH_SFTP_ROOT_DIR, (Ip_u8**)&session_root_dir, IP_NULL); if (ret != IPCOM_SUCCESS && ret != IPCOM_ERR_NOT_FOUND) return ret; if (session_root_dir) IPCOM_LOG1(DEBUG, "ipssh_make_ext_int_path :: session root dir: %s", session_root_dir); IPCOM_LOG1(DEBUG, "ipssh_make_ext_int_path :: start dir: %s", start_dir); if (!ipcom_normpath(org_path, org_path)) return IPCOM_ERR_FAILED; if (ipcom_strcmp(start_dir, ".") == 0 || ipcom_strcmp(start_dir, "./") == 0) { if (!ipcom_getcwd(start_dir_buf, sizeof(start_dir_buf))) { IPCOM_LOG1(DEBUG, "ipssh_make_ext_int_path :: ipcom_getcwd() failed (errno=%d)", ipcom_errno); return IPCOM_ERR_FAILED; } if (!ipcom_normpath(start_dir_buf, start_dir_buf)) return IPCOM_ERR_FAILED; start_dir = start_dir_buf; } ret = ipcom_shell_ctx_get(sh_ctx, IPSSH_SFTP_ROOT_DIR, (Ip_u8**)&session_root_dir, IP_NULL); if (ret != IPCOM_SUCCESS && ret != IPCOM_ERR_NOT_FOUND) return ret; if (session_root_dir) if (!ipcom_normpath(session_root_dir, session_root_dir)) { ipcom_free(session_root_dir); return IPCOM_ERR_FAILED; } if (ipcom_strcmp(org_path, ".") == 0) { if (session_root_dir) { /* If session_root_dir and start_dir are the same, return "/" as external path */ if (ipcom_strlen(session_root_dir) == ipcom_strlen(start_dir) && ipcom_strcmp(session_root_dir, start_dir) == 0) { *int_path = ipcom_strdup(start_dir); if (ext_path) *ext_path = ipcom_strdup("/"); } /* If start dir is not a sub-directory of session root dir, make session root dir our start dir */ else if (ipcom_strlen(session_root_dir) > ipcom_strlen(start_dir) || ipcom_strncmp(session_root_dir, start_dir, ipcom_strlen(session_root_dir))) { *int_path = ipcom_strdup(session_root_dir); if (ext_path) *ext_path = ipcom_strdup("/"); } else { *int_path = ipcom_strdup(start_dir); if (ext_path) { *ext_path = ipcom_malloc(IP_MAX(ipcom_strlen(start_dir),ipcom_strlen(session_root_dir))+1); if (!*ext_path) goto cleanup; /* 'Peel off' common path of session_root_dir from start_dir */ ipcom_strcpy(*ext_path, start_dir+ipcom_strlen(session_root_dir)); } } } else { if (ext_path) *ext_path = ipcom_strdup(start_dir); *int_path = ipcom_strdup(start_dir); } } else { if (session_root_dir) { if (ext_path) *ext_path = ipcom_strdup(org_path); *int_path = ipcom_malloc(ipcom_strlen(session_root_dir)+ipcom_strlen(org_path)+2); if ((ext_path && !*ext_path) || !*int_path) goto cleanup; ipcom_strcpy(*int_path, session_root_dir); if ((*int_path)[ipcom_strlen(session_root_dir)] != '/' && org_path[0] != '/') ipcom_strcat(*int_path, "/"); ipcom_strcat(*int_path, org_path); } else { if (ext_path) *ext_path = ipcom_strdup(org_path); *int_path = ipcom_strdup(org_path); if ((ext_path && !*ext_path) || !*int_path) goto cleanup; } } if (session_root_dir) { ipcom_free(session_root_dir); session_root_dir = IP_NULL; } if ((!ext_path || *ext_path) && *int_path) return IPCOM_SUCCESS; cleanup: if (ext_path && *ext_path) ipcom_free(*ext_path); if (*int_path) ipcom_free(*int_path); if (session_root_dir) ipcom_free(session_root_dir); return IPCOM_ERR_FAILED; } /* *=========================================================================== * ipssh_sftp_srv_pkt_dispatch *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_sftp_pkt_dispatch(Ipssh_channel* channel, Ipcom_buffer* pkt) { switch(channel->type) { #ifdef IPSSH_USE_SFTP_CLT case IPSSH_CH_TYPE_SFTP_CLT: return ipssh_sftp_clt_pkt_dispatch(channel, pkt); #endif case IPSSH_CH_TYPE_SFTP_SRV: return ipssh_sftp_srv_pkt_dispatch(channel, pkt); default: IP_PANIC(); return IPCOM_ERR_FAILED; } } /* *=========================================================================== * ipssh_sftp_msg_handler *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_sftp_msg_handler(Ipssh_channel* channel, Ipcom_buffer* msg) { Ip_u32 sftp_msg_len; Ip_s32 rc; if(!channel->data.sftp.msg) /* Is this the beginning of a fresh sftp message? */ { if (ipcom_buffer_len(msg) < 4) { /* Since we don't have received enough bytes to read the packet length, we * allocate a buffer that will fit a max-size packet. */ sftp_msg_len = IPSSH_CH_MAX_PKT + 128; channel->data.sftp.msg_len = 0; } else { if(ipssh_buffer_get_uint(msg, &sftp_msg_len)) return IPCOM_ERR_FAILED; if(sftp_msg_len >= IPSSH_CH_MAX_PKT + 128) { IPCOM_LOG1(ERR, "ipssh_sftp_msg_handler :: the packet's message length (%d) is more than max-size", sftp_msg_len); return IPCOM_ERR_FAILED; } channel->data.sftp.msg_len = sftp_msg_len; } if(sftp_msg_len > ipcom_buffer_len(msg)) { /* We have only received a fragment of an sftp message */ channel->data.sftp.msg = ipcom_buffer_new(sftp_msg_len); if(!channel->data.sftp.msg) { IPCOM_LOG0(ERR, "ipssh_sftp_msg_handler :: out of memory"); return IPCOM_ERR_FAILED; } (void)ipcom_buffer_put(channel->data.sftp.msg, ipcom_buffer_start_ptr(msg), ipcom_buffer_len(msg)); IPCOM_LOG2(DEBUG2, "ipssh_sftp_msg_handler :: incomplete packet: sftp_msg_len=%d, ch_data_len=%d", sftp_msg_len, ipcom_buffer_len(msg)); (void)ipcom_buffer_consume(msg, ipcom_buffer_len(msg)); return IPCOM_SUCCESS; } rc = ipssh_sftp_pkt_dispatch(channel, msg); } else /* We have a packet fragment already */ { Ip_size_t len; Ip_size_t bytes_missing; IPCOM_LOG3(DEBUG2, "ipssh_sftp_msg_handler :: incomplete packet: sftp_msg_len=%d, pos=%d, ch_data_len=%d", channel->data.sftp.msg->alloc, ipcom_buffer_len(channel->data.sftp.msg), ipcom_buffer_len(msg)); if (channel->data.sftp.msg_len == 0) { Ip_size_t oldlen; oldlen = ipcom_buffer_len(channel->data.sftp.msg); if (oldlen >= 4) return IPCOM_ERR_FAILED; /*It mostly cannot occur*/ if (4 > (oldlen + ipcom_buffer_len(msg))) { (void)ipcom_buffer_put(channel->data.sftp.msg, ipcom_buffer_start_ptr(msg), ipcom_buffer_len(msg)); (void)ipcom_buffer_consume(msg, ipcom_buffer_len(msg)); return IPCOM_SUCCESS; } /* We only get the complete sftp packet length */ (void)ipcom_buffer_put(channel->data.sftp.msg, ipcom_buffer_start_ptr(msg), 4-oldlen); (void)ipcom_buffer_consume(msg, 4-oldlen); if (ipssh_buffer_get_uint(channel->data.sftp.msg, &channel->data.sftp.msg_len)) return IPCOM_ERR_FAILED; } bytes_missing = channel->data.sftp.msg_len-ipcom_buffer_len(channel->data.sftp.msg); len = IP_MIN(bytes_missing, ipcom_buffer_len(msg)); /*lint !e666 */ (void)ipcom_buffer_put(channel->data.sftp.msg, ipcom_buffer_start_ptr(msg), len); (void)ipcom_buffer_consume(msg, len); if(channel->data.sftp.msg_len == 0 || ipcom_buffer_len(channel->data.sftp.msg) < (Ip_u32)channel->data.sftp.msg_len) return IPCOM_SUCCESS; /* We do not have a complete sftp message at this point */ /* Right, now we have a complete sftp message */ rc = ipssh_sftp_pkt_dispatch(channel, channel->data.sftp.msg); ipcom_buffer_free(channel->data.sftp.msg); channel->data.sftp.msg = IP_NULL; } /* Did we receive more than one sftp message? */ if(rc == IPCOM_SUCCESS && ipcom_buffer_len(msg) > 0) { /* We have an additional packet */ rc = ipssh_sftp_msg_handler(channel, msg); } return rc; } /* *=========================================================================== * ipssh_map_insert *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_map_insert(Ipssh_sftp_handle_map* sftp_map, Ipssh_sftp_int_handle* new_entry) { Ipcom_list* item; Ipcom_list* map = (Ipcom_list*) sftp_map; IPCOM_LOG2(DEBUG2, "ipssh_map_insert :: path = %s, id=%d", new_entry->path ? new_entry->path : "null", new_entry->id); /* Check that the ID is not already inserted */ for(item = ipcom_list_first(map); item; item = ipcom_list_next(item)) { Ipssh_sftp_int_handle* entry = (Ipssh_sftp_int_handle*) item; if(entry->id == new_entry->id) { IPCOM_LOG1(ERR, "ipssh_map_insert :: id already in map: %d", new_entry->id); return IPCOM_ERR_FAILED; } } ipcom_list_insert_first(map, (Ipcom_list*) new_entry); return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_map_lookup *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ipssh_sftp_int_handle* ipssh_map_lookup(Ipssh_sftp_handle_map* sftp_map, Ip_u32 id) { Ipcom_list* item; Ipcom_list* map = (Ipcom_list*) sftp_map; IPCOM_LOG1(DEBUG2, "ipssh_map_lookup :: id=%d", id); for(item = ipcom_list_first(map); item; item = ipcom_list_next(item)) { Ipssh_sftp_int_handle* entry = (Ipssh_sftp_int_handle*) item; if(entry->id == id) return entry; } IPCOM_LOG1(ERR, "ipssh_map_lookup :: failed to find id=%d", id); return IP_NULL; } /* *=========================================================================== * ipssh_map_remove *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_map_remove(Ipssh_sftp_handle_map* sftp_map, Ip_u32 id) { Ipcom_list* item; Ipcom_list* map = (Ipcom_list*) sftp_map; IPCOM_LOG1(DEBUG2, "ipssh_map_remove :: id=%d", id); for(item = ipcom_list_first(map); item; item = ipcom_list_next(item)) { Ipssh_sftp_int_handle* entry = (Ipssh_sftp_int_handle*) item; if(entry->id == id) { IPCOM_LOG2(DEBUG2, "ipssh_map_remove :: id=%d, path=%s", id, entry->path?entry->path:"null"); ipcom_list_remove(item); if(entry->type == IPSSH_DIR_HANDLE ) { if(entry->funcs->closedir_cb) entry->funcs->closedir_cb(entry->dir); /*lint !e522 */ else IP_PANIC(); } else { if(entry->funcs->close_cb) entry->funcs->close_cb(entry->fd); /*lint !e522 */ else IP_PANIC(); } if(entry->path) ipcom_free(entry->path); ipcom_free(entry); return IPCOM_SUCCESS; } } return IPCOM_ERR_FAILED; } /* *=========================================================================== * ipssh_create_handle *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_create_handle(Ipssh_sftp_ext_handle** handle, Ip_u32* len) { static Ip_u32 id = 1; Ipssh_sftp_ext_handle* tmp_handle; if(!(tmp_handle = ipcom_malloc(sizeof(Ipssh_sftp_ext_handle))) ) { IPCOM_LOG0(ERR, "ipssh_create_handle :: out of memory"); return IP_NULL; } tmp_handle->id = id++; ipcom_memcpy(tmp_handle->cookie, ipssh_handle_cookie, sizeof(tmp_handle->cookie)); *handle = tmp_handle; *len = sizeof(Ipssh_sftp_ext_handle); return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_validate_handle *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_validate_handle(Ipssh_sftp_ext_handle* handle, Ip_u32 len) { if(len != sizeof(Ipssh_sftp_ext_handle) || ipcom_memcmp(handle->cookie, ipssh_handle_cookie, sizeof(handle->cookie)) ) { IPCOM_LOG0(ERR, "ipssh_validate_handle :: invalid handle"); return IPCOM_ERR_FAILED; } return IPCOM_SUCCESS; } /* **************************************************************************** * 10 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipssh_send_fxp_msg *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_send_fxp_msg(Ipssh_channel* channel, Ipcom_buffer* msg) { Ip_u32 sftp_msg_len; sftp_msg_len = (Ip_u32)ipcom_buffer_len(msg)-18; IPSSH_SET_32_PTR(ipcom_buffer_ptr2pos(msg, 14), sftp_msg_len); /*lint !e666*/ return ipssh_send_channel_data_msg(channel, msg); } /* *=========================================================================== * ipssh_sftp_srv_pkt_dispatch *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_sftp_srv_pkt_dispatch(Ipssh_channel* channel, Ipcom_buffer* pkt) { Ip_u32 req_id; Ip_u8 pkt_type; Ip_err rc; if(ipcom_buffer_get_char(pkt, &pkt_type) != IPCOM_SUCCESS) return IPCOM_ERR_FAILED; IPCOM_LOG2(DEBUG2, "ipssh_sftp_srv_pkt_dispatch :: pkt type: %d, data_len:%d", pkt_type, ipcom_buffer_len(pkt)); /* check the SFTP user access */ if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_ACCESS, IP_NULL, IP_NULL) != IPCOM_SUCCESS) /*lint !e644*/ { /* Unpack message */ (void)ipssh_buffer_get_uint(pkt, &req_id); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED); IPCOM_LOG3(ERR, "ipssh_sftp_srv_pkt_dispatch :: unknown pkt type: %d, ch_id: %d, req_id: %d", pkt_type, channel->my_ch_id, req_id); return rc; } switch(pkt_type) { case IPSSH_FXP_INIT: rc = ipssh_msg_fxp_init(channel, pkt); break; case IPSSH_FXP_OPENDIR: rc = ipssh_msg_fxp_opendir(channel, pkt); break; case IPSSH_FXP_READDIR: rc = ipssh_msg_fxp_readdir(channel, pkt); break; case IPSSH_FXP_CLOSE: rc = ipssh_msg_fxp_close(channel, pkt); break; case IPSSH_FXP_OPEN: rc = ipssh_msg_fxp_open(channel, pkt); break; case IPSSH_FXP_LSTAT: case IPSSH_FXP_STAT: rc = ipssh_msg_fxp_lstat(channel, pkt); break; case IPSSH_FXP_SETSTAT: rc = ipssh_msg_fxp_setstat(channel, pkt); break; case IPSSH_FXP_REALPATH: rc = ipssh_msg_fxp_realpath(channel, pkt); break; case IPSSH_FXP_READ: rc = ipssh_msg_fxp_read(channel, pkt); break; case IPSSH_FXP_WRITE: rc = ipssh_msg_fxp_write(channel, pkt); break; case IPSSH_FXP_REMOVE: rc = ipssh_msg_fxp_remove(channel, pkt); break; case IPSSH_FXP_RMDIR: rc = ipssh_msg_fxp_rmdir(channel, pkt); break; case IPSSH_FXP_MKDIR: rc = ipssh_msg_fxp_mkdir(channel, pkt); break; case IPSSH_FXP_RENAME: rc = ipssh_msg_fxp_rename(channel, pkt); break; /* Unsupported messages */ case IPSSH_FXP_FSTAT: case IPSSH_FXP_FSETSTAT: case IPSSH_FXP_READLINK: case IPSSH_FXP_SYMLINK: case IPSSH_FXP_EXTENDED: case IPSSH_FXP_EXTENDED_REPLY: if(ipssh_buffer_get_uint(pkt, &req_id) != IPCOM_SUCCESS) { ipcom_buffer_reset(pkt); return IPCOM_ERR_FAILED; } else { IPCOM_LOG3(ERR, "ipssh_sftp_srv_pkt_dispatch :: pkt type not supported: %d, ch_id: %d, req_id: %d", pkt_type, channel->my_ch_id, req_id); ipcom_buffer_reset(pkt); return ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OP_UNSUPPORTED); } /* Messages a server shouldn't receive */ case IPSSH_FXP_ATTRS: case IPSSH_FXP_VERSION: case IPSSH_FXP_STATUS: case IPSSH_FXP_NAME: default: if(ipssh_buffer_get_uint(pkt, &req_id) == IPCOM_SUCCESS) { (void)ipssh_send_fxp_status(channel, req_id, IPSSH_FX_BAD_MESSAGE); IPCOM_LOG3(ERR, "ipssh_sftp_srv_pkt_dispatch :: unknown pkt type: %d, ch_id: %d, req_id: %d", pkt_type, channel->my_ch_id, req_id); } IP_PANIC(); return IPCOM_ERR_FAILED; } return rc; } /* *=========================================================================== * ipssh_mode_ipcom2ssh *=========================================================================== * Description: Converts file mode bits from IPCOM values to SSH values. */ IP_GLOBAL Ip_mode_t ipssh_mode_ipcom2ssh(Ip_mode_t mode) { Ip_mode_t ret; /* Set access mode bits */ ret = mode & 0x1FF; if(IP_S_ISDIR(mode)) ret |= IPSSH_S_IFDIR; if(IP_S_ISREG(mode)) ret |= IPSSH_S_IFREG; return ret; } /* *=========================================================================== * ipssh_mode_ssh2ipcom *=========================================================================== * Description: Converts file mode bits from SSH values to IPCOM values. */ IP_GLOBAL Ip_mode_t ipssh_mode_ssh2ipcom(Ip_mode_t mode) { Ip_mode_t ret; /* Set access mode bits */ ret = mode & 0x1FF; if(mode & IPSSH_S_IFDIR) ret |= IP_S_IFDIR; if(mode & IPSSH_S_IFREG) ret |= IP_S_IFREG; return ret; } /* *=========================================================================== * ipssh_unpack_attr *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_unpack_attr(Ipcom_buffer* buf, Ip_u32 attr_flags, struct Ip_stat* file_attr) { if(attr_flags & IPSSH_FILEXFER_ATTR_SIZE) { Ip_u32 len; if(ipssh_buffer_get_uint(buf, &len) || /* hi */ ipssh_buffer_get_uint(buf, &len) ) /* lo */ { IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack size attribute"); return IPCOM_ERR_FAILED; } file_attr->st_size = len; } if(attr_flags & IPSSH_FILEXFER_ATTR_UIDGID) { Ip_u32 uid; Ip_u32 gid; if(ipssh_buffer_get_uint(buf, &uid) || ipssh_buffer_get_uint(buf, &gid) ) { IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack uid/gid attributes"); return IPCOM_ERR_FAILED; } } if(attr_flags & IPSSH_FILEXFER_ATTR_PERMISSIONS ) { Ip_u32 mode; if(ipssh_buffer_get_uint(buf, &mode) ) { IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack perm attribute"); return IPCOM_ERR_FAILED; } file_attr->st_mode = ipssh_mode_ssh2ipcom((Ip_mode_t)mode); } if(attr_flags & IPSSH_FILEXFER_ATTR_ACMODTIME) { Ip_s32 atime, mtime; if(ipssh_buffer_get_int(buf, &atime) || ipssh_buffer_get_int(buf, &mtime)) { IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack time attributes"); return IPCOM_ERR_FAILED; } file_attr->st_atime = ip_ntohl(atime); file_attr->st_mtime = ip_ntohl(mtime); } if(attr_flags & IPSSH_FILEXFER_ATTR_EXTENDED) { Ip_u32 count; char* type; char* data; if(ipssh_buffer_get_uint(buf, &count)) { IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack ext count"); return IPCOM_ERR_FAILED; } while(count) { type = ipssh_buffer_get_sshstr(buf); if (type) ipcom_free(type); else { IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack ext data"); return IPCOM_ERR_FAILED; } data = ipssh_buffer_get_sshstr(buf); if (data) ipcom_free(data); else { IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack ext data"); return IPCOM_ERR_FAILED; } } } return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_pack_attr *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_pack_attr(Ipcom_buffer* pkt, struct Ip_stat* buf, Ip_u32 flags) { if(ipcom_buffer_capacity(pkt) < 32) return IPCOM_ERR_FAILED; (void)ipssh_buffer_put_int(pkt, flags); if(flags & IPSSH_FILEXFER_ATTR_SIZE) { /* 64 bits */ (void)ipcom_buffer_put_int(pkt, 0); (void)ipcom_buffer_put_int(pkt, ip_htonl(buf->st_size)); } if(flags & IPSSH_FILEXFER_ATTR_UIDGID) { (void)ipcom_buffer_put_int(pkt, ip_htonl(buf->st_uid)); (void)ipcom_buffer_put_int(pkt, ip_htonl(buf->st_gid)); } if(flags & IPSSH_FILEXFER_ATTR_PERMISSIONS) { Ip_mode_t ssh_attr; ssh_attr = ipssh_mode_ipcom2ssh(buf->st_mode); (void)ipcom_buffer_put_int(pkt, ip_htonl(ssh_attr)); } if(flags & IPSSH_FILEXFER_ATTR_ACMODTIME) { (void)ipcom_buffer_put_int(pkt, ip_htonl(buf->st_atime)); (void)ipcom_buffer_put_int(pkt, ip_htonl(buf->st_mtime)); } return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_channel_data_sftp_handler *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_channel_data_sftp_handler(Ipssh_channel* channel, Ipcom_buffer* msg) { Ip_u32 ch_data_len; Ip_err rc; /* Peel off channel data length */ if(ipssh_buffer_get_uint(msg, &ch_data_len)) return IPCOM_ERR_FAILED; rc = ipssh_sftp_msg_handler(channel, msg); return rc; } /* *=========================================================================== * ipssh_send_fxp_status *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_send_fxp_status(Ipssh_channel* channel, Ip_u32 req_id, Ip_u32 status) { Ipcom_buffer* pkt; Ip_s32 rc; IPCOM_LOG4(DEBUG, "ipssh_send_fxp_status :: my ch: %d, peer ch: %d, req_id: %d, status: %d", channel->my_ch_id, channel->peer_ch_id, req_id, status); pkt = ipssh_buffer_new_sftp_pkt(40, IPSSH_FXP_STATUS); if(!pkt) { IPCOM_LOG0(ERR, "ipssh_send_fxp_status :: out of memory"); return IPCOM_ERR_FAILED; } (void)ipcom_buffer_put_int(pkt, ip_htonl(req_id)); (void)ipcom_buffer_put_int(pkt, ip_htonl(status)); (void)ipssh_buffer_put_sshstr(pkt, ""); /* Empty error message */ (void)ipssh_buffer_put_sshstr(pkt, ""); /* Empty lang tag */ rc = ipssh_send_fxp_msg(channel, pkt); ipcom_buffer_free(pkt); return rc; } /* *=========================================================================== * ipssh_send_fxp_version *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_send_fxp_version(Ipssh_channel* channel) { Ip_err rc; Ipcom_buffer* pkt; IPCOM_LOG0(DEBUG, "ipssh_send_fxp_version"); pkt = ipssh_buffer_new_sftp_pkt(4, IPSSH_FXP_VERSION); if( !pkt) { IPCOM_LOG0(ERR, "ipssh_send_fxp_version, failed to allocate packet"); return IPCOM_ERR_NO_MEMORY; } (void)ipssh_buffer_put_int(pkt, 3); rc = ipssh_send_fxp_msg(channel, pkt); ipcom_buffer_free(pkt); return rc; } /* *=========================================================================== * ipssh_send_fxp_name *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_send_fxp_name(Ipssh_channel* channel, Ip_u32 req_id, char* path) { Ipcom_buffer* pkt_buf; Ip_err rc; IPCOM_LOG2(DEBUG, "ipssh_send_fxp_name :: my_ch_id: %d, req_id: %d", channel->my_ch_id, req_id); pkt_buf = ipssh_buffer_new_sftp_pkt(64 + 2*ipcom_strlen(path), IPSSH_FXP_NAME); /* 21 + strlen path only required */ if(!pkt_buf) { IPCOM_LOG0(ERR, "ipssh_send_fxp_name :: out of memory"); rc = IPCOM_ERR_FAILED; goto cleanup; } if(ipcom_buffer_put_int(pkt_buf, ip_htonl(req_id)) || ipcom_buffer_put_int(pkt_buf, ip_htonl(1)) || /* count */ ipssh_buffer_put_sshstr(pkt_buf, path) || ipssh_buffer_put_sshstr(pkt_buf, path) || /* Long name without long listing info */ ipcom_buffer_put_int(pkt_buf, ip_htonl(0)) ) /* attr, no attributes present */ { IPCOM_LOG0(ERR, "ipssh_send_fxp_name :: failed to pack message"); rc = IPCOM_ERR_FAILED; goto cleanup; } rc = ipssh_send_fxp_msg(channel, pkt_buf); cleanup: if(pkt_buf) ipcom_buffer_free(pkt_buf); return rc; } /* *=========================================================================== * ipssh_send_fxp_handle *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_send_fxp_handle(Ipssh_channel* channel, Ip_u32 req_id, Ipssh_sftp_ext_handle* handle, Ip_u32 handle_len) { Ipcom_buffer* pkt_buf; Ip_s32 rc; IPCOM_LOG3(DEBUG, "ipssh_send_fxp_handle :: my_ch_id: %d, peer_ch_id: %d, req_id: %d", channel->my_ch_id, channel->peer_ch_id, req_id); pkt_buf = ipssh_buffer_new_sftp_pkt(64 + handle_len, IPSSH_FXP_HANDLE); if(!pkt_buf) { IPCOM_LOG0(ERR, "ipssh_send_fxp_handle :: out of memory"); return IPCOM_ERR_FAILED; } if(ipcom_buffer_put_int(pkt_buf, ip_htonl(req_id)) || ipssh_buffer_put_sshbuf(pkt_buf, (Ip_u8*)handle, handle_len) ) { IPCOM_LOG0(ERR, "ipssh_send_fxp_handle :: failed to pack message"); rc = IPCOM_ERR_FAILED; goto cleanup; } rc = ipssh_send_fxp_msg(channel, pkt_buf); cleanup: if(pkt_buf) ipcom_buffer_free(pkt_buf); return rc; } /* *=========================================================================== * ipssh_msg_fxp_write *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_write(Ipssh_channel* channel, Ipcom_buffer* in_pkt) { Ipssh_sftp_ext_handle* handle = IP_NULL; Ip_s32 rc; Ip_u32 handle_len; Ipssh_sftp_int_handle* int_handle; Ip_u32 req_id; Ip_u64 offset; Ip_u32 len; Ip_s32 bytes_written; /* Unpack message */ if(ipssh_buffer_get_uint(in_pkt, &req_id) || !(handle = (Ipssh_sftp_ext_handle*)ipssh_buffer_get_sshbuf(in_pkt, &handle_len)) || ipssh_buffer_get_u64(in_pkt, &offset) || ipssh_buffer_get_uint(in_pkt, &len) || ipcom_buffer_len(in_pkt) < len) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_write :: failed to unpack message"); rc = IPCOM_ERR_FAILED; goto cleanup; } IPCOM_LOG5(DEBUG, "ipssh_msg_fxp_write :: my_ch_id: %d, peer_ch_id: %d, req_id: %d, offset: %d, len: %d", channel->my_ch_id, channel->peer_ch_id, req_id, IP_U64_GETLO(offset), len); if(ipssh_validate_handle(handle, handle_len) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_write :: ipssh_validate_handle() failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } if(!(int_handle = ipssh_map_lookup(&(channel->data.sftp.handle_map), handle->id)) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_write :: ipssh_map_lookup() failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } if(IP_U64_NEQUAL(offset,int_handle->write_pos)) { ip_assert(int_handle->funcs->lseek_cb); if(int_handle->funcs->lseek_cb(int_handle->fd, offset, IP_SEEK_SET) == -1) { IPCOM_LOG1(ERR, "ipssh_msg_fxp_write :: lseek_cb() failed, %s", ipcom_strerror(ipcom_errno)); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); goto cleanup; } int_handle->write_pos = offset; } ip_assert(int_handle->funcs->write_cb); bytes_written = (Ip_s32)int_handle->funcs->write_cb(int_handle->fd, (const char*)ipcom_buffer_start_ptr(in_pkt), (Ip_size_t)len); /*lint !e64*/ if(bytes_written != (Ip_s32)len) { IPCOM_LOG1(ERR, "ipssh_msg_fxp_write :: write_cb() failed, %s", ipcom_strerror(ipcom_errno)); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); goto cleanup; } IP_U64_ADD32(int_handle->write_pos,bytes_written); (void)ipcom_buffer_consume(in_pkt, len); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK); cleanup: if(handle) ipcom_free(handle); return rc; } /* *=========================================================================== * ipssh_msg_fxp_read *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_read(Ipssh_channel* channel, Ipcom_buffer* in_pkt) { Ipssh_sftp_ext_handle* handle = IP_NULL; Ip_s32 rc = IPCOM_SUCCESS; Ip_u32 handle_len; Ipcom_buffer* out_pkt = IP_NULL; Ipssh_sftp_int_handle* int_handle; Ip_u32 req_id; Ip_u64 offset; Ip_u32 len; int msg_size; Ip_s32 bytes_read; Ip_s32 bytes_requested; Ip_u32 data_len_pos; /* Unpack message */ if(ipssh_buffer_get_uint(in_pkt, &req_id) || !(handle = (Ipssh_sftp_ext_handle*)ipssh_buffer_get_sshbuf(in_pkt, &handle_len)) || ipssh_buffer_get_u64(in_pkt, &offset) || ipssh_buffer_get_uint(in_pkt, &len) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_read :: failed to unpack message"); rc = IPCOM_ERR_FAILED; goto cleanup; } IPCOM_LOG4(DEBUG, "ipssh_msg_fxp_read :: my_ch_id: %d, peer_ch_id: %d, offset: %d, len: %d", channel->my_ch_id, channel->peer_ch_id, IP_U64_GETLO(offset), len); if(ipssh_validate_handle(handle, handle_len) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_read :: ipssh_validate_handle() failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } if(!(int_handle = ipssh_map_lookup(&(channel->data.sftp.handle_map), handle->id)) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_read :: ipssh_map_lookup() failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } /* Send IPSSH_FXP_DATA message */ if (channel->ssh_conn->flags & IPSSH_FLAG_BUG_UNRESTRICTED_SFTP_MSG_SIZE) msg_size = len+8; else msg_size = IP_MIN(IPSSH_CH_MAX_PKT-5, len+8); ip_assert(msg_size > 8); out_pkt = ipssh_buffer_new_sftp_pkt(msg_size, IPSSH_FXP_DATA); if(!out_pkt) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_read :: out of memory"); rc = IPCOM_ERR_FAILED; goto cleanup; } (void)ipssh_buffer_put_int(out_pkt, req_id); data_len_pos = (Ip_u32)ipcom_buffer_len(out_pkt); (void)ipcom_buffer_decr_cap(out_pkt, 4); if(IP_U64_NEQUAL(offset, int_handle->read_pos)) { ip_assert(int_handle->funcs->lseek_cb); if(int_handle->funcs->lseek_cb(int_handle->fd, offset, IP_SEEK_SET) == -1) { if(!int_handle->eof) { IPCOM_LOG1(ERR, "ipssh_msg_fxp_read :: ipcom_lseek() failed, %d", ipcom_errno); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); goto cleanup; } } int_handle->read_pos = offset; } ip_assert(int_handle->funcs->read_cb); bytes_requested = (Ip_s32)ipcom_buffer_capacity(out_pkt)-IPSSH_MAX_PAD; /* Reserve space for padding */ bytes_read = (Ip_s32)int_handle->funcs->read_cb(int_handle->fd, (char*)ipcom_buffer_end_ptr(out_pkt), bytes_requested); if(bytes_read < 0 ) { IPCOM_LOG1(INFO, "ipssh_msg_fxp_read :: read_cb() failed, errno=%d", ipcom_errno); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); goto cleanup; } if(bytes_read == 0) { if(ipssh_send_fxp_status(channel, req_id, IPSSH_FX_EOF) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_read :: ipssh_send_fxp_status() failed"); rc = IPCOM_ERR_FAILED; } goto cleanup; } IP_U64_ADD32(int_handle->read_pos,bytes_read); if(bytes_read < bytes_requested) int_handle->eof = 1; (void)ipcom_buffer_decr_cap(out_pkt, bytes_read); IPSSH_SET_32_PTR(ipcom_buffer_ptr2pos(out_pkt, data_len_pos), bytes_read); /*lint !e666*/ rc = ipssh_send_fxp_msg(channel, out_pkt); cleanup: if(handle) ipcom_free(handle); if(out_pkt) ipcom_buffer_free(out_pkt); return rc; } /* *=========================================================================== * ipssh_msg_fxp_readdir *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_readdir(Ipssh_channel* channel, Ipcom_buffer* pkt_buf) { Ip_u32 req_id; Ipssh_sftp_ext_handle* handle = IP_NULL; Ip_s32 rc = IPCOM_SUCCESS; Ip_u32 handle_len, count; int count_pos; Ipcom_buffer* out_pkt = IP_NULL; Ipssh_sftp_int_handle* map_entry; char* tmp_buf = IP_NULL; /* Unpack message */ if(ipssh_buffer_get_uint(pkt_buf, &(req_id)) || !(handle = (Ipssh_sftp_ext_handle*)ipssh_buffer_get_sshbuf(pkt_buf, &handle_len))) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: failed to unpack message"); rc = IPCOM_ERR_FAILED; goto cleanup; } IPCOM_LOG2(DEBUG, "ipssh_msg_fxp_readdir :: my_ch_id: %d, peer_ch_id: %d", channel->my_ch_id, channel->peer_ch_id); if(ipssh_validate_handle(handle, handle_len) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: ipssh_validate_handle() failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } out_pkt = ipssh_buffer_new_sftp_pkt(IPSSH_SFTP_READDIR_BUF_SIZE, IPSSH_FXP_NAME); if(!out_pkt ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: out of memory"); rc = IPCOM_ERR_FAILED; goto cleanup; } if(!(map_entry = ipssh_map_lookup(&(channel->data.sftp.handle_map), handle->id)) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: ipssh_map_lookup() failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } if(map_entry->eof) { if(ipssh_send_fxp_status(channel, req_id, IPSSH_FX_EOF) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: ipssh_send_fxp_status() failed"); rc = IPCOM_ERR_FAILED; } goto cleanup; } /* Construct FXP_NAME packet */ (void)ipssh_buffer_put_int(out_pkt, req_id); count_pos = (int)ipcom_buffer_len(out_pkt); (void)ipcom_buffer_decr_cap(out_pkt, 4); count = 0; tmp_buf = ipcom_malloc(256); if(tmp_buf == IP_NULL) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: out of memory"); rc = IPCOM_ERR_FAILED; goto cleanup; } ip_assert(map_entry->funcs->readdir_cb); while( ipcom_buffer_capacity(out_pkt) > 256 ) { struct Ip_stat file_stat; struct Ip_dirent* dir_entry; dir_entry = map_entry->funcs->readdir_cb(map_entry->dir); if(!dir_entry) { map_entry->eof = 1; break; } count++; ipcom_strcpy(tmp_buf, map_entry->path); if(tmp_buf[ipcom_strlen(tmp_buf)-1] != '/') ipcom_strcat(tmp_buf, "/"); ipcom_strcat(tmp_buf, dir_entry->d_name); if(!map_entry->funcs->stat_cb || map_entry->funcs->stat_cb(tmp_buf, &file_stat) != IPCOM_SUCCESS) { ipcom_memset(&file_stat, 0, sizeof(file_stat)); } ipssh_make_long_name(map_entry->path, dir_entry->d_name, &file_stat, tmp_buf); if(ipssh_buffer_put_sshstr(out_pkt, dir_entry->d_name) || ipssh_buffer_put_sshstr(out_pkt, tmp_buf) || ipssh_pack_stat_data(out_pkt, &file_stat)) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: failed to pack FXP_NAME packet"); rc = IPCOM_ERR_FAILED; goto cleanup; } } IPSSH_SET_32_PTR(ipcom_buffer_ptr2pos(out_pkt, count_pos), count); /*lint !e666*/ rc = ipssh_send_fxp_msg(channel, out_pkt); cleanup: if(handle) ipcom_free(handle); if(out_pkt) ipcom_buffer_free(out_pkt); if(tmp_buf) ipcom_free(tmp_buf); return rc; } /* *=========================================================================== * ipssh_msg_fxp_close *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_close(Ipssh_channel* channel, Ipcom_buffer* pkt_buf) { Ipssh_sftp_ext_handle* handle = IP_NULL; Ip_u32 req_id, handle_len; Ip_s32 rc; /* Unpack message */ if(ipssh_buffer_get_uint(pkt_buf, &(req_id)) || !(handle = (Ipssh_sftp_ext_handle*)ipssh_buffer_get_sshbuf(pkt_buf, &handle_len))) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_close :: failed to unpack message"); (void)ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); rc = IPCOM_ERR_FAILED; goto cleanup; } IPCOM_LOG1(DEBUG, "ipssh_msg_fxp_close, req_id: %d", req_id); if(ipssh_validate_handle(handle, handle_len) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_close :: ipssh_validate_handle() failed"); (void)ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); rc = IPCOM_ERR_FAILED; goto cleanup; } if(ipssh_map_remove(&(channel->data.sftp.handle_map), handle->id) == IPCOM_SUCCESS) { rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK); } else { (void)ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); rc = IPCOM_ERR_FAILED; } cleanup: if(handle) ipcom_free(handle); return rc; } /* *=========================================================================== * ipssh_msg_fxp_lstat *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_lstat(Ipssh_channel* channel, Ipcom_buffer* pkt_buf) { char* path = IP_NULL; Ip_u32 req_id; Ip_s32 rc = IPCOM_ERR_FAILED; Ipcom_buffer* out_pkt = IP_NULL; struct Ip_stat buf; Ipssh_sftp_func_cb* funcs; char* int_path = IP_NULL; /* Unpack message */ if(ipssh_buffer_get_uint(pkt_buf, &req_id) || !(path=ipssh_buffer_get_sshstr(pkt_buf)) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_lstat :: failed to unpack message"); goto cleanup; } if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &int_path) != IPCOM_SUCCESS) { IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_lstat :: ipssh_make_ext_int_paths failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } IPCOM_LOG4(DEBUG, "ipssh_msg_fxp_lstat :: my_ch_id: %d, srv ch id: %d, req_id: %d, path=%s", channel->my_ch_id, channel->peer_ch_id, req_id, path); funcs = ipssh_get_sftp_func_cb(int_path); if(!funcs || !funcs->stat_cb) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_lstat :: no stat callback"); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); goto cleanup; } if(funcs->stat_cb(int_path, &buf)) { IPCOM_LOG1(ERR, "ipssh_msg_fxp_lstat :: stat_cb() failed, %s", ipcom_strerror(ipcom_errno)); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_NO_SUCH_FILE); goto cleanup; } out_pkt = ipssh_buffer_new_sftp_pkt(128, IPSSH_FXP_ATTRS); if(!out_pkt) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_lstat :: out of memory"); goto cleanup; } (void)ipcom_buffer_put_int(out_pkt, ip_htonl(req_id)); (void)ipssh_pack_stat_data(out_pkt, &buf); rc = ipssh_send_fxp_msg(channel, out_pkt); cleanup: if(path) ipcom_free(path); if(int_path) ipcom_free(int_path); if(out_pkt) ipcom_buffer_free(out_pkt); return rc; } /* *=========================================================================== * ipssh_msg_fxp_setstat *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_setstat(Ipssh_channel* channel, Ipcom_buffer* pkt_buf) { char* path = IP_NULL; Ip_u32 req_id; Ip_u32 attr_flags; Ip_s32 rc = IPCOM_ERR_FAILED; struct Ip_stat file_attr; /* Unpack message */ if(ipssh_buffer_get_uint(pkt_buf, &req_id) || !(path=ipssh_buffer_get_sshstr(pkt_buf)) || ipssh_buffer_get_uint(pkt_buf, &attr_flags) || ipssh_unpack_attr(pkt_buf, attr_flags, &file_attr)) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_setstat :: failed to unpack message"); goto cleanup; } IPCOM_LOG4(DEBUG, "ipssh_msg_fxp_setstat :: my_ch_id: %d, srv ch id: %d, req_id: %d, path=%s", channel->my_ch_id, channel->peer_ch_id, req_id, path); IPCOM_LOG0(INFO, "Received FXP_SETSTAT, ignoring"); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK); cleanup: if(path) ipcom_free(path); return rc; } /* *=========================================================================== * ipssh_msg_fxp_rename *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_rename(Ipssh_channel* channel, Ipcom_buffer* pkt_buf) { Ip_u32 req_id; Ip_s32 rc = IPCOM_ERR_FAILED; char* oldpath = IP_NULL; char* newpath = IP_NULL; char* int_oldpath = IP_NULL; char* int_newpath = IP_NULL; Ipssh_sftp_func_cb* funcs; /* Unpack message */ if(ipssh_buffer_get_uint(pkt_buf, &req_id) || !(oldpath=ipssh_buffer_get_sshstr(pkt_buf)) || !(newpath=ipssh_buffer_get_sshstr(pkt_buf)) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_rename :: failed to unpack message"); goto cleanup; } IPCOM_LOG5(DEBUG, "ipssh_msg_fxp_rename :: my_ch_id: %d, peer_ch_id: %d, req_id:%d, oldpath=%s, newpath=%s", channel->my_ch_id, channel->peer_ch_id, req_id, oldpath, newpath); if (ipssh_make_ext_int_paths(oldpath, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &int_oldpath) != IPCOM_SUCCESS || ipssh_make_ext_int_paths(newpath, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &int_newpath) != IPCOM_SUCCESS) { IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_rename :: ipssh_make_ext_int_paths failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_REMOVE, int_oldpath, 0) != IPCOM_SUCCESS) { rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED); goto cleanup; } if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_OPEN, int_newpath, IPSSH_FXP_WRITE) != IPCOM_SUCCESS) { rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED); goto cleanup; } funcs = ipssh_get_sftp_func_cb(int_oldpath); if(!funcs || !funcs->rename_cb) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_rename :: no rename callback"); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); goto cleanup; } if(funcs->rename_cb(int_oldpath, int_newpath)) { IPCOM_LOG3(ERR, "ipssh_msg_fxp_rename :: rename callback failed, oldpath: %s, newpath: %s, errno: %d", int_oldpath, int_newpath, ipcom_errno); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); goto cleanup; } else rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK); cleanup: if(newpath) ipcom_free(newpath); if(oldpath) ipcom_free(oldpath); if(int_newpath) ipcom_free(int_newpath); if(int_oldpath) ipcom_free(int_oldpath); return rc; } /* *=========================================================================== * ipssh_msg_fxp_rmdir *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_rmdir(Ipssh_channel* channel, Ipcom_buffer* pkt_buf) { Ip_u32 req_id; Ip_s32 rc = IPCOM_ERR_FAILED; char* path = IP_NULL; Ipssh_sftp_func_cb* funcs; char* int_path = IP_NULL; /* Unpack message */ if(ipssh_buffer_get_uint(pkt_buf, &req_id) || !(path=ipssh_buffer_get_sshstr(pkt_buf)) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_rmdir :: failed to unpack message"); goto cleanup; } IPCOM_LOG4(DEBUG, "ipssh_msg_fxp_rmdir :: my_ch_id: %d, peer_ch_id: %d, req_id:%d, path=%s", channel->my_ch_id, channel->peer_ch_id, req_id, path); if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &int_path) != IPCOM_SUCCESS) { IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_rmdir :: ipssh_make_ext_int_paths failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_RMDIR, int_path, 0) != IPCOM_SUCCESS) { rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED); goto cleanup; } funcs = ipssh_get_sftp_func_cb(int_path); if(!funcs || !funcs->rmdir_cb) { IPCOM_LOG1(ERR, "ipssh_msg_fxp_rmdir :: failed to get rmdir callback for file %s", int_path); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); goto cleanup; } if(funcs->rmdir_cb(int_path) != 0 ) { IPCOM_LOG2(ERR, "ipssh_msg_fxp_rmdir :: rmdir callback() failed for %s, errno: %d", path, ipcom_errno); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); } else rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK); cleanup: if(path) ipcom_free(path); if(int_path) ipcom_free(int_path); return rc; } /* *=========================================================================== * ipssh_msg_fxp_mkdir *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_mkdir(Ipssh_channel* channel, Ipcom_buffer* pkt_buf) { Ip_u32 req_id; Ip_s32 rc = IPCOM_ERR_FAILED; char* path = IP_NULL; Ip_u32 attr_flags; struct Ip_stat dir_attr; Ipssh_sftp_func_cb* funcs; char* int_path = IP_NULL; /* Resonable default value in case message does not contain file attributes */ dir_attr.st_mode = 0777; /* Unpack message */ if(ipssh_buffer_get_uint(pkt_buf, &req_id) || !(path=ipssh_buffer_get_sshstr(pkt_buf)) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_mkdir :: failed to unpack message"); goto cleanup; } if(ipcom_buffer_len(pkt_buf) >= 4) { if(ipssh_buffer_get_uint(pkt_buf, &attr_flags) || ipssh_unpack_attr(pkt_buf, attr_flags, &dir_attr)) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_mkdir :: failed to unpack file attributes"); goto cleanup; } } if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &int_path) != IPCOM_SUCCESS) { IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_mkdir :: ipssh_make_ext_int_paths failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } IPCOM_LOG4(DEBUG, "ipssh_msg_fxp_mkdir :: my_ch_id: %d, peer_ch_id: %d, req_id:%d, path=%s", channel->my_ch_id, channel->peer_ch_id, req_id, int_path); if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_MKDIR, int_path, 0) != IPCOM_SUCCESS) { rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED); goto cleanup; } funcs = ipssh_get_sftp_func_cb(int_path); if(!funcs || !funcs->mkdir_cb) { IPCOM_LOG1(ERR, "ipssh_msg_fxp_mkdir :: failed to get mkdir callback for file %s", int_path); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); goto cleanup; } if(funcs->mkdir_cb(int_path, dir_attr.st_mode) != 0 ) { IPCOM_LOG2(ERR, "ipssh_msg_fxp_mkdir :: ipcom_mkdir() failed for %s, errno: %d", int_path, ipcom_errno); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); } else rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK); cleanup: if(path) ipcom_free(path); if(int_path) ipcom_free(int_path); return rc; } /* *=========================================================================== * ipssh_msg_fxp_remove *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_remove(Ipssh_channel* channel, Ipcom_buffer* pkt_buf) { Ip_u32 req_id; Ip_s32 rc = IPCOM_ERR_FAILED; char* path = IP_NULL; char* int_path = IP_NULL; Ipssh_sftp_func_cb* funcs; /* Unpack message */ if(ipssh_buffer_get_uint(pkt_buf, &req_id) || !(path=ipssh_buffer_get_sshstr(pkt_buf)) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_remove :: failed to unpack message"); goto cleanup; } if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &int_path) != IPCOM_SUCCESS) { IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_remove :: ipssh_make_ext_int_paths failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } IPCOM_LOG4(DEBUG, "ipssh_msg_fxp_remove :: my_ch_id: %d, peer_ch_id: %d, req_id:%d, path=%s", channel->my_ch_id, channel->peer_ch_id, req_id, int_path); if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_REMOVE, int_path, 0) != IPCOM_SUCCESS) { rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED); goto cleanup; } funcs = ipssh_get_sftp_func_cb(int_path); if(!funcs || !funcs->unlink_cb) { IPCOM_LOG1(ERR, "ipssh_msg_fxp_remove :: no unlink callback: %s", int_path); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); goto cleanup; } if(funcs->unlink_cb(int_path) != 0 ) { IPCOM_LOG2(ERR, "ipssh_msg_fxp_remove :: ipcom_unlink() failed for %s, errno: %d", int_path, ipcom_errno); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); } else rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK); cleanup: if(path) ipcom_free(path); if(int_path) ipcom_free(int_path); return rc; } /* *=========================================================================== * ipssh_msg_fxp_open *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_open(Ipssh_channel* channel, Ipcom_buffer* pkt_buf) { Ip_u32 req_id; Ip_u32 flags; Ip_u32 attr_flags = 0; Ip_u32 x_handle_len; Ipssh_sftp_int_handle* handle; Ipssh_sftp_ext_handle* x_handle = IP_NULL; struct Ip_stat file_stat; char* path = IP_NULL; Ip_s32 rc = IPCOM_ERR_FAILED; ipcom_memset(&file_stat, 0, sizeof(struct Ip_stat)); file_stat.st_mode = 0644; /* Set a reasonable default value (-rw-r--r--), in case client sends no file privs */ if(!(handle = ipcom_malloc(sizeof(Ipssh_sftp_int_handle))) ) { IPCOM_LOG0(ERR,"ipssh_msg_fxp_open :: out of memory"); return IPCOM_ERR_FAILED; } ipcom_memset(handle, 0, sizeof(Ipssh_sftp_int_handle)); handle->fd = -1; handle->type = IPSSH_FILE_HANDLE; /* Unpack message */ if(ipssh_buffer_get_uint(pkt_buf, &req_id) || !(path=ipssh_buffer_get_sshstr(pkt_buf)) || ipssh_buffer_get_uint(pkt_buf, &flags) || ipssh_buffer_get_uint(pkt_buf, &attr_flags) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_open :: failed to unpack message"); goto cleanup; } if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &handle->path) != IPCOM_SUCCESS) { IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_open :: ipssh_make_ext_int_paths failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } IPCOM_LOG5(DEBUG, "ipssh_msg_fxp_open :: my_ch_id: %d, peer_ch_id: %d, req_id:%d, path=%s, flags: %d", channel->my_ch_id, channel->peer_ch_id, req_id, handle->path, flags); /*lint !e644*/ /* Unpack attr according to attr_flag */ if(attr_flags != 0 ) { if(ipssh_unpack_attr(pkt_buf, attr_flags, &file_stat) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_open :: failed to unpack attrs"); goto cleanup; } } if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_OPEN, handle->path, flags) != IPCOM_SUCCESS) /*lint !e644*/ { rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED); goto cleanup; } if(ipssh_create_handle(&(x_handle), &(x_handle_len)) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_open :: ipssh_create_handle failed"); goto cleanup; } handle->id = x_handle->id; /* Set up function pointers */ handle->funcs = ipssh_get_sftp_func_cb(handle->path); if(!handle->funcs || !handle->funcs->open_cb || !handle->funcs->close_cb || !handle->funcs->stat_cb || !handle->funcs->read_cb || !handle->funcs->write_cb || !handle->funcs->lseek_cb || !handle->funcs->opendir_cb || !handle->funcs->closedir_cb || !handle->funcs->readdir_cb || !handle->funcs->mkdir_cb || !handle->funcs->rmdir_cb || !handle->funcs->unlink_cb || !handle->funcs->rename_cb) { IPCOM_LOG1(ERR, "ipssh_msg_fxp_open :: failed to get callbacks for file: %s", handle->path); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); goto cleanup; } handle->fd = handle->funcs->open_cb(handle->path, ipssh_conv_flags(flags), file_stat.st_mode); if(handle->fd == -1) { IPCOM_LOG2(ERR, "ipssh_msg_fxp_open :: open() failed for file: %s, %s", handle->path, ipcom_strerror(ipcom_errno)); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); goto cleanup; } if(ipssh_map_insert(&(channel->data.sftp.handle_map), handle)) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_open :: ipssh_map_insert failed"); goto cleanup; } handle = IP_NULL; if(ipssh_send_fxp_handle(channel, req_id, x_handle, x_handle_len) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_open :: ipssh_send_fxp_handle() failed"); goto cleanup; } rc = IPCOM_SUCCESS; cleanup: if (path) ipcom_free(path); if(handle) { if(handle->path) ipcom_free(handle->path); if(handle->fd != -1) { ip_assert(handle->funcs->close_cb); handle->funcs->close_cb(handle->fd); /*lint !e522*/ } ipcom_free(handle); } if(x_handle) ipcom_free(x_handle); return rc; } /* *=========================================================================== * ipssh_msg_fxp_opendir *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_opendir(Ipssh_channel* channel, Ipcom_buffer* pkt_buf) { Ipssh_sftp_int_handle* entry; Ipssh_sftp_ext_handle* handle = IP_NULL; Ip_u32 handle_len = 0; Ip_s32 rc = IPCOM_SUCCESS; Ip_u32 req_id; char* path = IP_NULL; IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_opendir"); if(!(entry = ipcom_malloc(sizeof(Ipssh_sftp_int_handle))) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_opendir :: out of memory"); return IPCOM_ERR_FAILED; } ipcom_memset(entry, 0, sizeof(Ipssh_sftp_int_handle)); /* Unpack message */ if(ipssh_buffer_get_uint(pkt_buf, &(req_id)) || !(path = ipssh_buffer_get_sshstr(pkt_buf)) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_opendir :: failed to unpack message"); rc = IPCOM_ERR_FAILED; goto cleanup; } if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &entry->path) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_opendir :: ipssh_make_ext_int_paths failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_OPENDIR, entry->path, 0) != IPCOM_SUCCESS) { rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED); goto cleanup; } entry->funcs = ipssh_get_sftp_func_cb(entry->path); if(!entry->funcs || !entry->funcs->open_cb || !entry->funcs->close_cb || !entry->funcs->stat_cb || !entry->funcs->read_cb || !entry->funcs->write_cb || !entry->funcs->lseek_cb || !entry->funcs->opendir_cb || !entry->funcs->closedir_cb || !entry->funcs->readdir_cb || !entry->funcs->mkdir_cb || !entry->funcs->rmdir_cb || !entry->funcs->unlink_cb || !entry->funcs->rename_cb) { IPCOM_LOG1(ERR, "ipssh_msg_fxp_opendir :: failed to get callbacks for %s", entry->path); rc = IPCOM_ERR_FAILED; goto cleanup; } if(!(entry->dir = entry->funcs->opendir_cb(entry->path))) { rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_NO_SUCH_FILE); goto cleanup; } entry->type = IPSSH_DIR_HANDLE; if(ipssh_create_handle(&(handle), &(handle_len)) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_opendir :: ipssh_create_handle failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } entry->id = handle->id; if(ipssh_map_insert(&(channel->data.sftp.handle_map), entry)) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_opendir :: ipssh_map_insert failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } entry = IP_NULL; if(ipssh_send_fxp_handle(channel, req_id, handle, handle_len) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_opendir :: ipssh_send_fxp_handle() failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } cleanup: if (path) ipcom_free(path); if (entry) { if (entry->path) ipcom_free(entry->path); ipcom_free(entry); } if (handle) ipcom_free(handle); return rc; } /* *=========================================================================== * ipssh_msg_fxp_realpath *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_realpath(Ipssh_channel* channel, Ipcom_buffer* pkt_buf) { Ip_u32 req_id; char * path = IP_NULL; struct Ip_stat buf; Ipssh_sftp_func_cb* funcs; Ip_s32 rc = IPCOM_SUCCESS; char* int_path = IP_NULL; char* ext_path = IP_NULL; /* Unpack message */ if(ipssh_buffer_get_uint(pkt_buf, &req_id) || !(path = ipssh_buffer_get_sshstr(pkt_buf)) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_realpath :: failed to unpack message"); rc = IPCOM_ERR_FAILED; goto cleanup; } IPCOM_LOG3(DEBUG, "ipssh_msg_fxp_realpath :: my_ch_id: %d, req_id:%d, path: %s", channel->my_ch_id, req_id, path); if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, &ext_path, &int_path) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_realpath :: ipssh_make_ext_int_paths failed"); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_NO_SUCH_PATH); goto cleanup; } /* Provide the absolute path in case custom routine requires it */ funcs = ipssh_get_sftp_func_cb(int_path); if(!funcs) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_realpath :: no stat callback"); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE); goto cleanup; } if(funcs->stat_cb(int_path, &buf)) { IPCOM_LOG1(DEBUG, "ipssh_msg_fxp_realpath :: stat_cb() failed, %s", ipcom_strerror(ipcom_errno)); rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_NO_SUCH_FILE); goto cleanup; } if(ipssh_send_fxp_name(channel, req_id, ext_path) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_realpath :: ipssh_send_fxp_name() failed"); rc = IPCOM_ERR_FAILED; goto cleanup; } cleanup: if(path) ipcom_free(path); if(int_path) ipcom_free(int_path); if(ext_path) ipcom_free(ext_path); return rc; } /* *=========================================================================== * ipssh_msg_fxp_init *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_fxp_init(Ipssh_channel* channel, Ipcom_buffer* pkt_buf) { Ip_u32 version; IPCOM_LOG1(DEBUG, "ipssh_msg_fxp_init :: my_ch_id:%d", channel->my_ch_id); if(ipssh_buffer_get_uint(pkt_buf, &version) ) { IPCOM_LOG0(ERR, "ipssh_msg_fxp_init :: ipssh_buffer_get_uint() failed"); return IPCOM_ERR_FAILED; } IPCOM_LOG2(DEBUG, "ipssh_msg_fxp_init :: my_ch_id:%d, version: %d", channel->my_ch_id, version); return ipssh_send_fxp_version(channel); } /* *=========================================================================== * ipssh_sftp_cleanup *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL void ipssh_sftp_cleanup(Ipssh_channel* channel) { Ipssh_sftp_int_handle* handle; if(channel->data.sftp.msg) ipcom_buffer_free(channel->data.sftp.msg); for(handle = (Ipssh_sftp_int_handle*)ipcom_list_first(&channel->data.sftp.handle_map); handle; handle = (Ipssh_sftp_int_handle*)ipcom_list_first(&channel->data.sftp.handle_map)) { ipcom_list_remove(&handle->list); /* check whether a file or directory was opened */ if (handle->type == IPSSH_DIR_HANDLE) { if (handle->funcs->closedir_cb) handle->funcs->closedir_cb(handle->dir); else IP_PANIC(); } else if (handle->type == IPSSH_FILE_HANDLE) { if (handle->funcs->close_cb) handle->funcs->close_cb(handle->fd); else IP_PANIC(); } else IP_PANIC(); if(handle->path) ipcom_free (handle->path); ipcom_free(handle); } } #ifdef IPSSH_USE_SFTP /* *=========================================================================== * ipssh_channel_sftp_init *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL void ipssh_channel_sftp_init(Ipssh_channel* channel, int ch_type) { channel->type = ch_type; channel->handler = ipssh_channel_data_sftp_handler; ipcom_list_init(&channel->data.sftp.handle_map); } #endif IP_GLOBAL Ip_err ipssh_sftp_subsystem_init_cb(Ipssh_channel* channel, Ip_fd* sock) { *sock = IP_INVALID_SOCKET; if(!channel->ssh_conn->config.sftp) { IPCOM_LOG0(INFO, "channel request :: sftp session requested, but sftp service not active"); return IPCOM_ERR_FAILED; } ipssh_channel_sftp_init(channel, IPSSH_CH_TYPE_SFTP_SRV); channel->ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_SSH_SFTP; return IPCOM_SUCCESS; } #else int ipssh_sftp_empty_file; #endif /* IPSSH_USE_SFTP */ /* **************************************************************************** * END OF FILE **************************************************************************** */