/** * @PROJECT CGI Bash Shell Interface * @COPYRIGHT See COPYING in the top level directory * @FILE mimetype.c * @PURPOSE Mimetype and file upload handler * @DEVELOPERS Nathan Angelacos * Rafal Kupiec */ #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "buffer.h" #include "error.h" #include "subshell.h" #include "buffer.h" #include "mimetype.h" #include "cbsi.h" void empty_stdin(void) { char c[2000]; while(read(STDIN_FILENO, &c, 2000)) { } } void mime_var_init(mime_var_t* obj) { obj->name = NULL; obj->filename = NULL; obj->type = NULL; obj->tempname = NULL; buffer_init(&(obj->value)); obj->fh = 0; } void mime_var_destroy(mime_var_t* obj) { int status; struct sigaction new_action; if(obj->name) { free(obj->name); obj->name = NULL; } if(obj->filename) { free(obj->filename); obj->filename = NULL; } if(obj->type) { free(obj->type); obj->type = NULL; } if(obj->tempname) { free(obj->tempname); obj->tempname = NULL; } buffer_destroy(&(obj->value)); if(obj->fh) { close(abs(obj->fh)); obj->fh = 0; } } char* mime_substr(char* start, int len) { char* ptr; if(!start) { return NULL; } if(len < 0) { return NULL; } ptr = xmalloc(len + 2); memcpy(ptr, start, len); return ptr; } void mime_tag_add(mime_var_t* obj, char* str) { char* a = NULL; char* b = NULL; static char* tag[] = { "name=\"", "filename=\"", "Content-Type: " }; a = strcasestr(str, tag[0]); if(a) { a += strlen(tag[0]); b = strchr(a, '"'); if(!obj->name) { obj->name = mime_substr(a, b - a); } } a = strcasestr(str, tag[1]); if(a) { a += strlen(tag[1]); b = strchr(a, '"'); if(!obj->filename) { obj->filename = mime_substr(a, b - a); } } a = strcasestr(str, tag[2]); if(a) { a += strlen(tag[2]); b = a + strlen(a); if(!obj->type) { obj->type = mime_substr(a, b - a); } } } void mime_var_putenv(list_t* env, mime_var_t* obj) { buffer_t buf; buffer_init(&buf); if(obj->filename) { buffer_add(&buf, obj->name, strlen(obj->name)); buffer_add(&buf, "=", 1); buffer_add(&buf, (char*) obj->value.data, strlen((char*) obj->value.data) + 1); myputenv(env, (char*) buf.data, global.file_prefix); buffer_reset(&buf); buffer_add(&buf, obj->name, strlen(obj->name)); buffer_add(&buf, "=", 1); buffer_add(&buf, obj->filename, strlen(obj->filename) + 1); myputenv(env, (char*) buf.data, global.filename_prefix); buffer_reset (&buf); } else if(obj->name) { buffer_add(&(obj->value), "", 1); buffer_add(&buf, obj->name, strlen(obj->name)); buffer_add(&buf, "=", 1); buffer_add(&buf, (char*) obj->value.data, strlen((char*) obj->value.data) + 1); myputenv(env, (char*) buf.data, global.file_prefix); buffer_reset(&buf); } buffer_destroy(&buf); } void mime_exec(mime_var_t* obj, char* fifo) { int pid; char* type; char* filename; char* name; char *c; int fh; struct sigaction new_action; pid = fork(); if(pid == -1) { empty_stdin(); die_with_message(NULL, NULL, g_err_msg[E_SUBSHELL_FAIL]); } if(pid == 0) { if(obj->type) { type = xmalloc(13 + strlen(obj->type) + 1); sprintf(type, "CONTENT_TYPE=%s", obj->type); putenv(type); } if(obj->filename) { filename = xmalloc (9 + strlen(obj->filename) + 1); sprintf(filename, "FILENAME=%s", obj->filename); putenv(filename); } if(obj->name) { name = xmalloc(5 + strlen(obj->name) + 1); sprintf(name, "NAME=%s", obj->name); putenv(name); } fh = open(fifo, O_RDONLY); while(read(fh, &c, 1)) { } exit(-1); } else { new_action.sa_handler = SIG_IGN; sigemptyset(&new_action.sa_mask); new_action.sa_flags = 0; sigaction(SIGPIPE, &new_action, NULL); } } void mime_var_open_target(mime_var_t* obj) { char* tmpname; token_t* curtoken; curtoken = global.uploadlist; int ok; if(global.uploadkb == 0) { empty_stdin(); die_with_message(NULL, NULL, "File uploads are not allowed."); } ok = -1; tmpname = xmalloc(strlen(global.uploaddir) + 8); strcpy(tmpname, global.uploaddir); strcat(tmpname, "/XXXXXX"); obj->fh = mkstemp(tmpname); if(obj->fh == -1) { ok = 0; } buffer_add(&(obj->value), tmpname, strlen(tmpname)); if(!ok) { empty_stdin(); die_with_message(NULL, NULL, g_err_msg[E_FILE_OPEN_FAIL], tmpname); } curtoken = push_token_on_list(curtoken, NULL, tmpname, strlen(tmpname) + 1); if(global.uploadlist == NULL) { global.uploadlist = curtoken; } } void mime_var_writer(mime_var_t* obj, char* str, int len) { int err; if(!obj->filename) { buffer_add(&(obj->value), str, len); } if((!obj->fh) && (obj->filename)) { mime_var_open_target(obj); } if(obj->fh > 0) { err = write(obj->fh, str, len); if(err == -1) { obj->fh = abs(obj->fh) * -1; } } } int rfc2388_handler(list_t* env) { enum mime_state_t { DISCARD, BOUNDARY, HEADER, CONTENT }; int state; int i, x; unsigned long max_len, content_length; sbuffer_t sbuf; char* crlf = "\r\n"; char* boundary; char* str; buffer_t buf; mime_var_t var; str = getenv("CONTENT_TYPE"); i = strlen(str) - 9; while((i >= 0) && (memcmp("boundary=", str + i, 9))) { i--; } if(i == -1) { empty_stdin(); die_with_message(NULL, NULL, "No Mime Boundary Information Found"); } i = i + 9; if(str[i] == '"') { i++; } boundary = xmalloc(strlen(str + i) + 5); memcpy(boundary, crlf, 2); memcpy(boundary + 2, "--", 2); memcpy(boundary + 4, str + i, strlen(str + i) + 1); if((i > 0) && (str[i - 1] == '"')) { while((boundary[i]) && (boundary[i] != '"')) { i++; } boundary[i] = '\0'; } max_len = ((global.uploadkb == 0) ? 2048 : abs(global.uploadkb)) * 1024; content_length = 0; sbuffer_init(&sbuf, 1024 * 128); sbuf.fh = STDIN; if(getenv("CONTENT_LENGTH")) { sbuf.maxread = strtoul(getenv("CONTENT_LENGTH"), NULL, 10); } buffer_init(&buf); buffer_add(&buf, "", 1); buffer_reset(&buf); state = DISCARD; str = boundary + 2; do { x = sbuffer_read(&sbuf, str); content_length += sbuf.len; if(content_length >= max_len && global.uploadkb != -1) { empty_stdin(); free(boundary); sbuffer_destroy(&sbuf); buffer_destroy(&buf); if(var.name) { mime_var_destroy(&var); } die_with_message(NULL, NULL, "Attempted to send content larger than allowed limits."); } switch(state) { case DISCARD: if(x) { state = BOUNDARY; str = crlf; buffer_reset(&buf); } break; case BOUNDARY: if(x) { buffer_add(&buf, sbuf.segment, sbuf.len); if(!memcmp(buf.data, boundary + 2, 2)) { str = boundary + 2; state = DISCARD; } else { buffer_reset(&buf); mime_var_init(&var); state = HEADER; str = crlf; } } else { buffer_add(&buf, sbuf.segment, sbuf.len); } break; case HEADER: buffer_add(&buf, sbuf.segment, sbuf.len); if(x) { if(sbuf.len == 0) { buffer_reset(&buf); state = CONTENT; str = boundary; } else { buffer_add(&buf, "", 1); mime_tag_add(&var, (char*) buf.data); buffer_reset(&buf); } } break; case CONTENT: mime_var_writer(&var, (char*) sbuf.segment, sbuf.len); if(x) { buffer_reset(&buf); mime_var_putenv(env, &var); mime_var_destroy(&var); state = BOUNDARY; str = crlf; } break; } } while(!sbuf.eof); free(boundary); sbuffer_destroy(&sbuf); buffer_destroy(&buf); return 0; }