Newer
Older
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <openssl/sha.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
SLDaemon::SLDaemon() : socket_fs_(0) {}
SLDaemon &SLDaemon::Instance() {
if (instance_ == 0)
instance_ = new SLDaemon();
return *instance_;
}
void SLDaemon::check_options() const {
// Check validy of arguement
throw Error(ERR_BADPARAM, "No valid server address specified");
throw Error(ERR_BADPARAM, "No valid server port specified");
throw Error(ERR_BADPARAM, "No valid login specified");
throw Error(ERR_BADPARAM, "No valid pass specified");
throw Error(ERR_BADPARAM, "No valid scripts directory specified");
throw Error(ERR_BADPARAM, "No valid retry delay specified");
throw Error(ERR_BADPARAM, "Unable to write into script directory");
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
net_connect:
try {
connect();
auth();
while (1) {
line = recvln();
// call right handler
try {
if (!strcmp(line, "EXIT\n"))
cmd_exit();
else if (!strcmp(line, "VERSION\n"))
cmd_version();
else if (!strcmp(line, "STATUS\n"))
cmd_status();
else if (!strcmp(line, "LIST\n"))
cmd_list();
else if (!strcmp(line, "KILLALL\n"))
cmd_killall();
else if (!strcmp(line, "KILL\n"))
cmd_kill(line);
else if (!strncmp(line, "EXEC ", 4))
cmd_exec(line);
else if (!strncmp(line, "FILE ", 5))
cmd_file(line);
else if (!strcmp(line, "UPDATE\n"))
cmd_update();
else
sendln("Invalid command.");
}
catch (const Error &e) {
delete [] line;
if (e.code() == ERR_NET)
throw;
else {
sendln(e.message());
std::cerr << "!! " << e.message() << std::endl;
}
}
delete[] line;
}
// recupere les erreurs de reseaux.
catch (const Error &e) {
if (e.code() != ERR_NET)
throw;
}
}
//******************************************************************************
// network functions
//******************************************************************************
void SLDaemon::connect() {
struct sockaddr_in daddr;
struct hostent *h;
// create socket
socket_fd_ = socket(PF_INET, SOCK_STREAM, 0);
if (socket_fd_ == -1)
throw Error(ERR_NET, strerror(errno));
daddr.sin_addr = *((struct in_addr *) h->h_addr);
memset(daddr.sin_zero, '\0', sizeof daddr.sin_zero);
if (verbose())
std::cout << "Connecting to " << h->h_name << " (" << inet_ntoa(*(struct in_addr*)h->h_addr)
<< ") on port " << options.port << "..." << std::endl;
// connect
if (::connect(socket_fd_, (struct sockaddr *) &daddr, sizeof daddr) == -1)
throw Error(ERR_NET, strerror(errno));
if (verbose())
std::cout << "Connected to " << h->h_name << " (" << inet_ntoa(*(struct in_addr*)h->h_addr)
<< ") on port " << options.port << "..." << std::endl;
if ((socket_fs_ = fdopen(socket_fd_, "r+")) == 0)
if (fclose(socket_fs_))
throw Error(ERR_NET, strerror(errno));
if (verbose())
std::cout << "Connection closed." << std::endl;
socket_fs_ = 0;
}
void SLDaemon::send(long int i) {
fprintf(socket_fs_, "%li", i);
void SLDaemon::send(const string str, bool buf) {
send(str.c_str(), str.length(), buf);
}
void SLDaemon::send(const char *data, size_t len, bool buf) {
if (fwrite(data, 1, len, socket_fs_) != len)
throw Error(ERR_NET, strerror(errno));
if (!buf && fflush(socket_fs_))
throw Error(ERR_NET, strerror(errno));
void SLDaemon::sendln(const string &s) {
if (!fprintf(socket_fs_, "%s\n", s.c_str()))
throw Error(ERR_NET, strerror(errno));
if (verbose())
std::cout << SND_DATA << s << std::endl;
}
char *SLDaemon::recv(size_t size) {
char *data = new char[size];
if (fread(data, 1, size, socket_fs_) != size) {
char *data = recv(size);
FILE *fs = fopen(filename.c_str(), "w");
throw Error(ERR_FILE, (string) "recv: fopen: " + strerror(errno));
throw Error(ERR_FILE, (string) "recv: fwrite: " + strerror(errno));
throw Error(ERR_FILE, (string) "recv: fclose: " + strerror(errno));
char *SLDaemon::recvln() {
char *line = new char[MAX_LINE_SIZE];
if (fgets(line, MAX_LINE_SIZE, socket_fs_) == 0) {
if(feof(socket_fs_))
throw Error(ERR_NET, (string) "recvln: Connexion close");
throw Error(ERR_FILE, (string) "recvln: " + strerror(errno));
return line;
}
void SLDaemon::flush() {
if (fflush(socket_fs_))
throw Error(ERR_NET, strerror(errno));
//******************************************************************************
// protocol functions
//******************************************************************************
void SLDaemon::auth() {
unsigned char md[SHA_DIGEST_LENGTH];
char buf[MAX_LINE_SIZE];
char *buf2 = "";
SHA1((const unsigned char *) options.pass.c_str(), options.pass.length(), md);
b64 = BIO_new(BIO_f_base64());
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, md, SHA_DIGEST_LENGTH);
BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);
if (bptr->length > 0) {
buf2 = new char[bptr->length];
memcpy(buf2, bptr->data, bptr->length-1);
buf2[bptr->length-1] = 0;
}
snprintf(buf, MAX_LINE_SIZE, "HOST %s", options.login.c_str());
snprintf(buf, MAX_LINE_SIZE, "PASS %s", buf2);
sendln(buf);
if (bptr->length > 0)
delete[] buf2;
//******************************************************************************
// command functions
//******************************************************************************
exit(ERR_OK);
}
void SLDaemon::cmd_version() {
void SLDaemon::cmd_exec(const char *line) {
if (sscanf(line, "EXEC %512s\n", buf) != 1) { //FIXME: Bad static magic number
sendln("EXEC: Syntax error.");
return;
}
// Check transversal path attack
if (strchr(buf, '/')) {
sendln("EXEC: Invalid character in job name.");
return;
}
// Check if file exist
struct stat st;
int ret = lstat(path.c_str(), &st);
if (ret) {
char msg[MAX_LINE_SIZE];
snprintf(msg, MAX_LINE_SIZE, "EXEC: %s: %s.", buf, strerror(errno));
sendln(msg);
return;
}
// check file owner
if (st.st_uid != getuid()) {
sendln((string) "EXEC: Unable to fork: " + strerror(errno) + ".");
sendln(buf);
// allow sun to start
if (kill(pid, SIGCONT)) {
perror("sld: cmd_exec");
jobs_.erase(j);
}
// Wait father registration stuff before doing something else
// so we suspend execution
if (kill(getpid(), SIGSTOP)) {
perror("sld: cmd_exec");
return;
}
// try to run job
execl(path.c_str(), basename(path.c_str()), 0);
}
}
void SLDaemon::cmd_file(const char *line) {
char filename[MAX_LINE_SIZE];
if (sscanf(line, "FILE %512s\n", filename) != 1) { //FIXME: bad magic size
sendln("FILE: Syntax error.");
return;
}
// Check transversal path attack
if (strchr(filename, '/')) {
sendln("FILE: Invalid character in filename.");
buf = recvln();
ret = sscanf(buf, "SIZE %i\n", &size);
sendln("FILE: Invalid size parameter.");
ret = sscanf(buf, "MD5 %512s\n", md5); //FIXME: bad magic size
return;
}
//get data
try {
recv(size, target);
}
catch (const Error &err) {
if (err.code() == ERR_FILE) {
return;
}
throw;
}
// check MD5
if (SLDaemon::md5(target) != string(md5)) {
sendln("FILE: file " + target + ": Invalid MD5.");
// proceed chown
if (chown(target.c_str(), getuid(), getgid())) {
sendln("FILE: chown of " + target + ": Unable to chown.");
unlink(target.c_str());
return;
}
// proceed chmod
if (chmod(target.c_str(), S_IRUSR | S_IWUSR | S_IXUSR)) {
sendln("FILE: chmod of " + target + ": Unable to chmod.");
sendln("FILE: Transfer of " + target + ": OK.");
void SLDaemon::cmd_update() {
char *buf;
int ret;
// get filename
buf = recvln();
ret = sscanf(buf, "MD5 %512s\n", md5); //FIXME: bad magic size
sendln("UPDATE: Invalid md5 parameter.");
// find tempory filename
char tempsld[PATH_MAX] = "/tmp/sldXXXXXX";
int tempsld_fd = mkstemp(tempsld);
if (tempsld_fd == 0) {
sendln((string) "UPDATE: mkstemp: " + strerror(errno) + ".");
return;
}
close(tempsld_fd);
sendln("UPDATE: " + err.message() + ".");
unlink(tempsld);
if (SLDaemon::md5(tempsld) != string(md5)) {
sendln((string) "UPDATE: file " + tempsld + ": Invalid MD5.");
unlink(tempsld);
return;
}
// copy file to its right destination
unlink(target.c_str());
cp(tempsld, target.c_str());
// check MD5
if (SLDaemon::md5(target.c_str()) != string(md5)) {
sendln("UPDATE: file " + target + ": Invalid MD5.");
unlink(tempsld);
// proceed chown
if (chown(target.c_str(), getuid(), getgid())) {
sendln("FILE: chown of " + target + ": Unable to chown.");
unlink(target.c_str());
return;
}
// proceed chmod
if (chmod(target.c_str(), S_IRUSR | S_IWUSR | S_IXUSR)) {
sendln("FILE: chmod of " + target + ": Unable to chmod.");
sendln("UPDATE: Transfer of " + target + ": OK.");
FILE *fls = popen(string("ls -1A " + options.scriptdir).c_str(), "r");
sendln("LIST: Unable to list " + options.scriptdir + ".");
return;
}
char buf[255];
size_t len;
try {
while ((len = fread(buf, 1, 255, fls)) > 0)
send(buf, len);
}
catch (...) {
pclose(fls);
throw;
}
}
void SLDaemon::cmd_status() {
t_job::iterator i;
time_t t = time(0);
if (jobs_.size() == 0)
return;
sendln("STATUS of");
for (i = jobs_.begin(); i != jobs_.end(); ++i) {
" job: %s, pid: %d, start at: %ld, since: %ld seconds.",
}
}
void SLDaemon::cmd_kill(const char *line) {
t_job::iterator i;
// retrieve pid
int pid;
if (sscanf(line, "KILL %i\n", &pid) != 1) {
return;
}
for (i = jobs_.begin(); i != jobs_.end(); ++i)
if (pid == i->pid) {
int ret = kill(i->pid, SIGKILL);
snprintf(buf, MAX_LINE_SIZE, "KILL: kill -SIGKILL %d (%s), return %d (%s).",
i->pid, i->name.c_str(), ret, ((ret == -1) ? strerror(errno) : "OK" ));
}
}
void SLDaemon::cmd_killall() {
t_job::iterator i;
for (i = jobs_.begin(); i != jobs_.end(); ++i) {
int ret = kill(i->pid, SIGKILL);
snprintf(buf, MAX_LINE_SIZE, "KILL: kill -SIGKILL %d (%s), return %d (%s).",
i->pid, i->name.c_str(), ret, ((ret == -1) ? strerror(errno) : "OK" ));
}
//******************************************************************************
// others functions
//******************************************************************************
MD5_CTX ctx;
FILE *fs;
size_t len;
char buf[512];
char md[MD5_DIGEST_LENGTH];
char digest[MD5_DIGEST_LENGTH * 2 + 1];
if (!MD5_Init(&ctx))
return "";
return "";
while ((len = fread(buf, 1, 512, fs)) > 0)
if (!MD5_Update(&ctx, buf, len))
break;
if (!MD5_Final((unsigned char*)md, &ctx))
return "";
for(len = 0; len < MD5_DIGEST_LENGTH; ++len) {
sprintf(digest + (len * 2), "%02x", (unsigned char) md[len]);
}
digest[MD5_DIGEST_LENGTH * 2] = 0;
return string(digest);
void SLDaemon::clean_dir(const string &dir) const {
DIR *ds;
struct dirent *de;
ds = opendir(dir.c_str());
if (ds == 0)
throw Error(ERR_FILE, "clear_dir: Unable to read dir script");
while ((de = readdir(ds))) {
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
continue;
if (remove(string(dir + "/" + de->d_name).c_str())) {
closedir(ds);
throw Error(ERR_FILE,(string) "clear_dir: remove("+ de->d_name + "): " + strerror(errno));
}
}
closedir(ds);
}
//******************************************************************************
// Class Job functions
//******************************************************************************
SLDaemon::Job::Job(int p, const string &s, time_t t) : pid(p), name(s), start_time(t) {}
bool SLDaemon::Job::operator()(const SLDaemon::Job &a, const SLDaemon::Job &b) {
return a.pid < b.pid;
}
//******************************************************************************
// Friends Class functions
//******************************************************************************
void sigchild(int) {
int status;
SLDaemon &d = SLDaemon::Instance();
typedef SLDaemon::t_job t_job;
t_job::iterator i;
t_job &jobs_ = d.jobs_;
pid_t pid;
// Retrieve a pid
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
time_t t = time(0);
for (i = jobs_.begin(); i != jobs_.end(); ++i) {
if (i->pid == pid) {
char buf[MAX_LINE_SIZE];
snprintf(buf, MAX_LINE_SIZE,
"JOB: %s, pid: %d, end at: %ld, since: %ld, return: %d\n",
i->name.c_str(), i->pid, t, t - i->start_time,
WEXITSTATUS(status));
write(d.socket_fd_, buf, strlen(buf));