/* * * Author : GĂ©rald FENOY * * Copyright 2017-2019 GeoLabs SARL. All rights reserved. * * This work was supported by public funds received in the framework of GEOSUD, * a project (ANR-10-EQPX-20) of the program "Investissements d'Avenir" managed * by the French National Research Agency * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "sshapi.h" #include "service_internal.h" SSHCON *sessions[MAX_PARALLEL_SSH_CON]; /** * Wait until one or more file descriptor has been changed for the socket for a * time defined by timeout * @param socket_fd int defining the sockket file descriptor * @param session an exeisting LIBSSH2_SESSION */ int waitsocket(int socket_fd, LIBSSH2_SESSION *session) { struct timeval timeout; int rc; fd_set fd; fd_set *writefd = NULL; fd_set *readfd = NULL; int dir; timeout.tv_sec = 10; timeout.tv_usec = 0; FD_ZERO(&fd); FD_SET(socket_fd, &fd); /* now make sure we wait in the correct direction */ dir = libssh2_session_block_directions(session); if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) readfd = &fd; if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) writefd = &fd; rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout); return rc; } /** * Connect to a remote host using SSH protocol * @param conf maps The main configuration maps * @return the libssh2 sessions pointer or NULL in case any failure occured. */ SSHCON *ssh_connect(maps* conf){ unsigned long hostaddr; int i, use_pw = 1; struct sockaddr_in sin; const char *fingerprint; SSHCON *result=(SSHCON*)malloc((2*sizeof(int))+sizeof(LIBSSH2_SESSION*)+sizeof(LIBSSH2_SFTP*)); result->sock_id=NULL; result->index=NULL; result->session=NULL; result->sftp_session=NULL; const char *user; const char *password=NULL; const char *public_key; char *private_key; int rc; FILE *local; char mem[1024 * 1000]; char error[1024]; int port=22; map* hpc_config=getMapFromMaps(conf,"lenv","configId"); map* hpc_host=getMapFromMaps(conf,hpc_config->value,"ssh_host"); map* hpc_port=getMapFromMaps(conf,hpc_config->value,"ssh_port"); map* hpc_user=getMapFromMaps(conf,hpc_config->value,"ssh_user"); map* hpc_password=getMapFromMaps(conf,hpc_config->value,"ssh_password"); map* hpc_public_key=getMapFromMaps(conf,hpc_config->value,"ssh_key"); char ip[100]; struct hostent *my_hostent; struct in_addr **addrs; if (hpc_host != NULL) { // Fetch ip address for the hostname if ( (my_hostent = gethostbyname( hpc_host->value ) ) == NULL){ herror("gethostbyname"); setMapInMaps(conf,"lenv","message",_("Issue when invoking gethostbyname!")); return NULL; } addrs = (struct in_addr **) my_hostent->h_addr_list; for(i = 0; addrs[i] != NULL; i++) { strcpy(ip , inet_ntoa(*addrs[i]) ); break; } } #ifdef WIN32 WSADATA wsadata; int err; err = WSAStartup(MAKEWORD(2,0), &wsadata); if (err != 0) { sprintf(error, "WSAStartup failed with error: %d\n", err); setMapInMaps(conf,"lenv","message",error); return NULL; } #endif if (hpc_host != NULL) { hostaddr = inet_addr(ip); } else { setMapInMaps(conf,"lenv","message","No host parameter found in your main.cfg file!\n"); return NULL; } // Default port is 22 if(hpc_port!=NULL){ port=atoi(hpc_port->value); } // In case there is no HPC > user the it must failed if (hpc_user != NULL) { user = hpc_user->value; }else{ setMapInMaps(conf,"lenv","message","No user parameter found in your main.cfg file!"); return NULL; } // TODO: in case password is available but there is also the public key // defined, then we can consider this password as the pass phrase. if (hpc_password != NULL) { password = hpc_password->value; }else{ use_pw=-1; if (hpc_public_key != NULL) { public_key = hpc_public_key->value; private_key = zStrdup(hpc_public_key->value); private_key[strlen(public_key)-4]=0; }else{ setMapInMaps(conf,"lenv","message","No method found to authenticate!"); return NULL; } } rc = libssh2_init (0); if (rc != 0) { sprintf (error, "libssh2 initialization failed (%d)\n", rc); setMapInMaps(conf,"lenv","message",error); return NULL; } result->sock_id = socket(AF_INET, SOCK_STREAM, 0); sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = hostaddr; if (connect(result->sock_id, (struct sockaddr*)(&sin),sizeof(struct sockaddr_in)) != 0) { setMapInMaps(conf,"lenv","message","Failed to connect to remote host!"); return NULL; } result->session = libssh2_session_init(); result->sftp_session = NULL; map* tmp=getMapFromMaps(conf,"lenv","nb_sessions"); if(tmp!=NULL){ char nb_sessions[10]; int nb_sess=atoi(tmp->value); sprintf(nb_sessions,"%d",nb_sess+1); setMapInMaps(conf,"lenv","nb_sessions",nb_sessions); result->index=nb_sess+1; sessions[nb_sess+1]=result; }else{ setMapInMaps(conf,"lenv","nb_sessions","0"); sessions[0]=result; result->index=0; } if (!result->session) return NULL; libssh2_session_set_blocking(result->session, 1); while ((rc = libssh2_session_handshake(result->session, result->sock_id)) == LIBSSH2_ERROR_EAGAIN); if (rc) { sprintf(error, "Failure establishing SSH session: %d\n", rc); setMapInMaps(conf,"lenv","message",error); return NULL; } fingerprint = libssh2_hostkey_hash(result->session, LIBSSH2_HOSTKEY_HASH_SHA1); if (use_pw>0) { while ((rc = libssh2_userauth_password(result->session, user, password)) == LIBSSH2_ERROR_EAGAIN); if (rc) { setMapInMaps(conf,"lenv","message","Authentication by password failed."); ssh_close(conf); return NULL; } } else { while ((rc = libssh2_userauth_publickey_fromfile(result->session, user, public_key, private_key, password)) == LIBSSH2_ERROR_EAGAIN); if (rc) { setMapInMaps(conf,"lenv","message","Authentication by public key failed"); ssh_close(conf); return NULL; } free(private_key); } return result; } /** * Get the number of opened SSH connections * @param conf maps pointer to the main configuration maps * @return the number of opened SSH connections */ int ssh_get_cnt(maps* conf){ int result=0; map* myMap=getMapFromMaps(conf,"lenv","nb_sessions"); if(myMap!=NULL){ result=atoi(myMap->value); } return result; } /** * Verify if a file exists on the remote host * @param conf maps pointer to the main configuration maps * @param targetPath const char* defining the path for storing the file on the * remote host * @return true in case of success, false if failure occured */ size_t ssh_file_exists(maps* conf,const char* targetPath,int cnt){ size_t result=-1; if(cnt>0) cnt-=1; int rc; LIBSSH2_SFTP_ATTRIBUTES attrs; LIBSSH2_SFTP_HANDLE *sftp_handle; do{ sftp_handle = libssh2_sftp_open(sessions[cnt]->sftp_session, targetPath, LIBSSH2_FXF_READ, LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR| LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH); if (!sftp_handle) { fprintf(stderr, "Unable to open file with SFTP: %d %ld\n",__LINE__, libssh2_sftp_last_error(sessions[cnt]->sftp_session)); return 0; } }while(!sftp_handle); #ifdef SSH_DEBUG fprintf(stderr, "libssh2_sftp_open() is done, get file information\n"); #endif do { rc = libssh2_sftp_stat_ex(sessions[cnt]->sftp_session, targetPath, strlen(targetPath), LIBSSH2_SFTP_LSTAT, &attrs ); if (rc<0 && (libssh2_session_last_errno(sessions[cnt]->session) != LIBSSH2_ERROR_EAGAIN)) { fprintf(stderr, "error trying to fstat_ex, returned %d\n", rc); break; } else { #ifdef SSH_DEBUG fprintf(stderr, "Stat Data: RetCode=%d\n", rc); fprintf(stderr, "Stat Data: Size=%llu\n", attrs.filesize); fprintf(stderr, "Stat Data: Perm=%lx\n", attrs.permissions); fprintf(stderr, "Stat Data: mtime=%lu\n", attrs.mtime); #endif if(rc==0) break; result=attrs.filesize; } } while (true); libssh2_sftp_close(sftp_handle); //libssh2_sftp_shutdown(sessions[cnt]->sftp_session); return result; } /** * Upload a file over an opened SSH connection * @param conf maps pointer to the main configuration maps * @param localPath const char* defining the local path for accessing the file * @param targetPath const char* defining the path for storing the file on the * remote host * @return true in case of success, false if failure occured */ bool ssh_copy(maps* conf,const char* localPath,const char* targetPath,int cnt){ char mem[1024 * 16]; size_t nread; size_t memuse=0; time_t start; long total = 0; int counter=0; int duration; int rc; LIBSSH2_SFTP_HANDLE *sftp_handle; //map* myMap=getMapFromMaps(conf,"lenv","cnt_session"); if(getMapFromMaps(conf,"lenv","cnt_session")!=NULL){ char tmp[10]; sprintf(tmp,"%d",cnt+1); setMapInMaps(conf,"lenv","cnt_session",tmp); }else setMapInMaps(conf,"lenv","cnt_session","0"); FILE *local = fopen(localPath, "rb"); if (!local) { fprintf(stderr, "Can't open local file %s\n", localPath); return false; } do { sessions[cnt]->sftp_session = libssh2_sftp_init(sessions[cnt]->session); if (!sessions[cnt]->sftp_session && (libssh2_session_last_errno(sessions[cnt]->session) != LIBSSH2_ERROR_EAGAIN)) { fprintf(stderr, "Unable to init SFTP session\n"); return false; } if(!sessions[cnt]->sftp_session) zSleep(10); } while (!sessions[cnt]->sftp_session); do { sftp_handle = libssh2_sftp_open(sessions[cnt]->sftp_session, targetPath, LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR| LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH); if (!sftp_handle && (libssh2_session_last_errno(sessions[cnt]->session) != LIBSSH2_ERROR_EAGAIN)) { fprintf(stderr, "Unable to open file with SFTP\n"); return false; } if(!sftp_handle) zSleep(10); } while (!sftp_handle); start = time(NULL); do { nread = fread(&mem[memuse], 1, sizeof(mem)-memuse, local); if (nread <= 0) { if (memuse > 0) nread = 0; else break; } memuse += nread; total += nread; while ((rc = libssh2_sftp_write(sftp_handle, mem, memuse)) == LIBSSH2_ERROR_EAGAIN) { waitsocket(sessions[cnt]->sock_id, sessions[cnt]->session); } if(rc < 0) break; memuse = 0; } while (rc > 0); duration = (int)(time(NULL)-start); fclose(local); libssh2_sftp_close_handle(sftp_handle); libssh2_sftp_shutdown(sessions[cnt]->sftp_session); return true; } /** * Download a file over an opened SSH connection * @param conf maps pointer to the main configuration maps * @param localPath const char* defining the local path for storing the file * @param targetPath const char* defining the path for accessing the file on the * remote host * @return 0 in case of success, -1 if failure occured */ int ssh_fetch(maps* conf,const char* localPath,const char* targetPath,int cnt){ size_t nread; size_t memuse=0; time_t start; long total = 0; int duration; int rc; LIBSSH2_SFTP_HANDLE *sftp_handle; FILE *local = fopen(localPath, "wb"); if (!local) { fprintf(stderr, "Can't open local file %s\n", localPath); return -1; } do { sessions[cnt]->sftp_session = libssh2_sftp_init(sessions[cnt]->session); if (!sessions[cnt]->sftp_session && (libssh2_session_last_errno(sessions[cnt]->session) != LIBSSH2_ERROR_EAGAIN)) { fprintf(stderr, "Unable to init SFTP session\n"); return -1; } if(!sessions[cnt]->sftp_session) zSleep(10); } while (!sessions[cnt]->sftp_session); do { sftp_handle = libssh2_sftp_open(sessions[cnt]->sftp_session, targetPath, LIBSSH2_FXF_READ, 0); if (!sftp_handle) { if (libssh2_session_last_errno(sessions[cnt]->session) != LIBSSH2_ERROR_EAGAIN) { fprintf(stderr, " ** Unable to open file with SFTP\n"); return -1; } else { waitsocket(sessions[cnt]->sock_id, sessions[cnt]->session); } } if(!sftp_handle) zSleep(10); } while (!sftp_handle); int result=0; int counter=0; do { do { char* mem=(char*)malloc(16*1024); rc = libssh2_sftp_read(sftp_handle, mem,16*1024); if(rc > 0) { fwrite(mem, rc, 1, local); } free(mem); if(counter%25==0) zSleep(10); } while (rc > 0); if(rc != LIBSSH2_ERROR_EAGAIN) { result=-1; break; } struct timeval timeout; fd_set fd; timeout.tv_sec = 10; timeout.tv_usec = 0; FD_ZERO(&fd); FD_SET(sessions[cnt]->sock_id, &fd); rc = select(sessions[cnt]->sock_id+1, &fd, &fd, NULL, &timeout); if(rc <= 0) { if(rc==0) fprintf(stderr, "SFTP download timed out: %d\n", rc); else fprintf(stderr, "SFTP download error: %d\n", rc); return -1; } if(counter%50==0) zSleep(10); counter++; } while (1); duration = (int)(time(NULL)-start); fclose(local); libssh2_sftp_close_handle(sftp_handle); libssh2_sftp_shutdown(sessions[cnt]->sftp_session); return 0; } /** * Execute a command over an opened SSH connection * @param conf maps pointer to the main configuration maps * @param command const char pointer to the command to be executed * @return bytecount resulting from the execution of the command */ int ssh_exec(maps* conf,const char* command,int cnt){ LIBSSH2_CHANNEL *channel; int rc; int bytecount = 0; int exitcode; char *exitsignal=(char *)"none"; while( (channel = libssh2_channel_open_session(sessions[cnt]->session)) == NULL && libssh2_session_last_error(sessions[cnt]->session,NULL,NULL,0) == LIBSSH2_ERROR_EAGAIN ) { waitsocket(sessions[cnt]->sock_id, sessions[cnt]->session); } if( channel == NULL ){ fprintf(stderr,"Error\n"); return -1; } while( (rc = libssh2_channel_exec(channel, command)) == LIBSSH2_ERROR_EAGAIN ) { waitsocket(sessions[cnt]->sock_id, sessions[cnt]->session); } if( rc != 0 ) { fprintf(stderr,"Error\n"); return -1; } map* tmpPath=getMapFromMaps(conf,"main","tmpPath"); map* uuid=getMapFromMaps(conf,"lenv","usid"); char *logPath=(char*)malloc((strlen(tmpPath->value)+strlen(uuid->value)+11)*sizeof(char)); sprintf(logPath,"%s/exec_out_%s",tmpPath->value,uuid->value); FILE* logFile=fopen(logPath,"wb"); free(logPath); while(true){ int rc; do { char buffer[0x4000]; rc = libssh2_channel_read( channel, buffer, sizeof(buffer) ); if( rc > 0 ){ int i; bytecount += rc; buffer[rc]=0; fprintf(logFile,"%s",buffer); fflush(logFile); } } while( rc > 0 ); if( rc == LIBSSH2_ERROR_EAGAIN ) { waitsocket(sessions[cnt]->sock_id, sessions[cnt]->session); } else break; } fclose(logFile); exitcode = 127; while( (rc = libssh2_channel_close(channel)) == LIBSSH2_ERROR_EAGAIN ) waitsocket(sessions[cnt]->sock_id, sessions[cnt]->session); if( rc == 0 ) { exitcode = libssh2_channel_get_exit_status( channel ); libssh2_channel_get_exit_signal(channel, &exitsignal, NULL, NULL, NULL, NULL, NULL); } if (exitsignal) fprintf(stderr, "\nGot signal: %s\n", exitsignal); else fprintf(stderr, "\nEXIT: %d bytecount: %d\n", exitcode, bytecount); libssh2_channel_free(channel); return bytecount; } /** * Close an opened SSH connection * @param conf maps pointer to the main configuration maps * @param con SSHCON pointer to the SSH connection * @return true in case of success, false if failure occured */ bool ssh_close_session(maps* conf,SSHCON* con){ if(con==NULL) return true; while (libssh2_session_disconnect(con->session, "Normal Shutdown, Thank you for using the ZOO-Project sshapi") == LIBSSH2_ERROR_EAGAIN); #ifdef WIN32 closesocket(con->sock_id); #else close(con->sock_id); #endif libssh2_session_free(con->session); con=NULL; return true; } /** * Close all the opened SSH connections * @param conf maps pointer to the main configuration maps * @return true in case of success, false if failure occured */ bool ssh_close(maps* conf){ int i,nb_sessions; map* tmp=getMapFromMaps(conf,"lenv","nb_sessions"); if(tmp!=NULL){ nb_sessions=atoi(tmp->value); for(i=0;icontent=createMap("length","0"); addMapsToMaps(conf,queueMaps); freeMaps(&queueMaps); free(queueMaps); queueMap=getMapFromMaps(*conf,"uploadQueue","length"); } maps* queueMaps=getMaps(*conf,"uploadQueue"); int queueIndex=atoi(queueMap->value); if(input!=NULL){ if(getMap(input->content,"cache_file")!=NULL){ map* length=getMap(input->content,"length"); if(length==NULL){ addToMap(input->content,"length","1"); length=getMap(input->content,"length"); } int len=atoi(length->value); int i=0; for(i=0;icontent,"localPath",i), getMapArray(input->content,"targetPath",i)}; setMapArray(queueMaps->content,"input",queueIndex+i,input->name); setMapArray(queueMaps->content,"localPath",queueIndex+i,tmp[0]->value); setMapArray(queueMaps->content,"targetPath",queueIndex+i,tmp[1]->value); } } } return true; } int fileMd5Check(maps** conf,const char* localPath,const char* targetPath){ if(strstr(localPath,".zca")!=NULL){ char *logPath=NULL; char *command=(char*)malloc(((2*strlen(targetPath))+42)*sizeof(char)); sprintf(command,"(md5sum %s | awk {'print $1'}) && touch %s",targetPath,targetPath); if(ssh_exec(*conf,command,ssh_get_cnt(*conf))<=0){ return -1; }else{ struct stat f_status={}; map* usid=getMapFromMaps(*conf,"lenv","usid"); map* tmpMap=getMapFromMaps(*conf,"main","tmpPath"); char* tmpPath=zStrdup(localPath); tmpPath[strlen(tmpPath)-2]='m'; tmpPath[strlen(tmpPath)-1]='d'; free(command); logPath=(char*)malloc((strlen(tmpMap->value)+strlen(usid->value)+11)*sizeof(char)); sprintf(logPath,"%s/exec_out_%s",tmpMap->value,usid->value); int ts=stat(logPath, &f_status); if(ts==0) { char* fcontent=(char*)malloc(sizeof(char)*(f_status.st_size+1)); FILE* f=fopen(logPath,"rb"); fread(fcontent,f_status.st_size,1,f); fcontent[f_status.st_size-1]=0; fclose(f); free(logPath); struct stat f_status1={}; int ts1=stat(tmpPath, &f_status1); if(ts1==0) { char* fcontent1=(char*)malloc(sizeof(char)*(f_status.st_size+1)); FILE* f1=fopen(tmpPath,"rb"); fread(fcontent1,f_status1.st_size,1,f1); fcontent1[f_status1.st_size]=0; fclose(f1); free(tmpPath); if(strcmp(fcontent,fcontent1)==0){ free(fcontent); free(fcontent1); return 0; }else{ free(fcontent); free(fcontent1); return -1; } }else{ free(tmpPath); free(fcontent); return -1; } } free(logPath); free(tmpPath); } } return -1; } bool runUpload(maps** conf){ SSHCON *test=ssh_connect(*conf); if(test==NULL){ return false; } map* queueLengthMap=getMapFromMaps(*conf,"uploadQueue","length"); maps* queueMaps=getMaps(*conf,"uploadQueue"); if(queueLengthMap!=NULL){ int cnt=atoi(queueLengthMap->value); int i=0; for(i=0;icontent,"input",i), getMapArray(queueMaps->content,"localPath",i), getMapArray(queueMaps->content,"targetPath",i) }; if(fileMd5Check(conf,argv[1]->value,argv[2]->value)<0){ /**/zooLock* lck; if((lck=lockFile(*conf,argv[1]->value,'w'))!=NULL){/**/ if(ssh_copy(*conf,argv[1]->value,argv[2]->value,ssh_get_cnt(*conf))!=true){ const char* templateStr=_("Unable to copy over SSH the file requested for setting the value of %s."); char *tmpMessage=(char*)malloc((strlen(templateStr)+strlen(argv[0]->value)+1)*sizeof(char)); sprintf(tmpMessage,templateStr,argv[0]->value); setMapInMaps(*conf,"lenv","message",tmpMessage); free(tmpMessage); unlockFile(*conf,lck); return false; } /**/unlockFile(*conf,lck); }else{ setMapInMaps(*conf,"lenv","message",_("Unable to lock the file for upload!")); return false; }/**/ } } } while (libssh2_session_disconnect(test->session, "Normal Shutdown, Thank you for using the ZOO-Project sshapi") == LIBSSH2_ERROR_EAGAIN); #ifdef WIN32 closesocket(test->sock_id); #else close(test->sock_id); #endif libssh2_session_free(test->session); free(test); test=NULL; sessions[ssh_get_cnt(*conf)-1]=NULL; maps* tmp=getMaps(*conf,"lenv"); addIntToMap(tmp->content,"nb_sessions",ssh_get_cnt(*conf)-1); return true; }