/* ------------------------------------------------------------- ** ** Extract a comma delimited address/port from buf. buf is unchecked ** user input so make sure we don't do anything stupid here... ** ------------------------------------------------------------- */ struct sockaddr_in extract_address(const sstr * buf) { int i, a[6]; struct sockaddr_in tmp; sstr *p = sstr_dup(buf); memset(&tmp, 0, sizeof(tmp)); sstr_split(p, NULL, 0, sstr_pbrk2(p, "01234456789")); for(i = 0; i < 6; i++) { a[i] = sstr_atoi(p); if(a[i] < 0 || a[i] > 255) { sstr_free(p); write_log(ATTACK, "PORT/PASV command number out of range"); return tmp; } if(i != 5 && sstr_token(p, NULL, ",", 0) == -1) { sstr_free(p); return (tmp); } } tmp = com2n(a[0], a[1], a[2], a[3], a[4], a[5]); sstr_free(p); return (tmp); }
void send_ccommand(const char *cmd, const char *arg) { sstr *scmd, *sarg; scmd = sstr_dup2(cmd); sarg = sstr_dup2(arg); send_command(scmd, sarg); sstr_free(scmd); sstr_free(sarg); }
int store_addressrange(sstr * str, struct in_addr *addr, struct in_addr *msk) { sstr *tok; int sep, i; tok = sstr_init(0); sep = sstr_token(str, tok, " /->\r\n", 0); if(name2addr(tok, addr)==-1) return(-1); if (sep == '/') { sep = sstr_token(str, tok, " /->\r\n", 0); if (sstr_chr(tok, '.')==-1) { /* x.x.x.x/yy */ i = sstr_atoi(tok); msk->s_addr = htonl(0xFFFFFFFF << (32 - i)); } else if(name2addr(tok, msk)==-1) return(-1); } else { /*No mask given */ if (addr->s_addr == INADDR_ANY) msk->s_addr = INADDR_ANY; else msk->s_addr = INADDR_BROADCAST; } sstr_free(tok); return (0); }
int store_acl(sstr * str, struct acl_list *acls) { sstr *tok; struct acl_item item; tok = sstr_init(0); sstr_token(str, tok, " \t\n", 0); if (!sstr_casecmp2(tok, "Allow")) item.action = ALLOW; else if (!sstr_casecmp2(tok, "Deny")) item.action = DENY; else return (-1); sstr_free(tok); parse_match(str, &item); acls->list = realloc(acls->list, (acls->num + 1) * sizeof(struct acl_item)); if(!acls->list) return(-1); memcpy(acls->list + acls->num, &item, sizeof(struct acl_item)); acls->num++; return (0); }
int read_config() { struct option_array *opt; sstr *buf; if (!fp) { if (config.config_file == NULL) fp = fopen(CONFIG_FILE, "r"); else fp = fopen(config.config_file, "r"); if (fp == NULL) return (-1); line_no=0; } buf = sstr_init(0); while (sstr_fgets(buf, fp) != NULL) { line_no++; switch (parse_line(buf)) { case -1: sstr_free(buf); if (sublevel == 0) fclose(fp); return (-1); case 0: break; case 1: /* end subsection */ sstr_free(buf); return (0); } }; sstr_free(buf); fclose(fp); fp = NULL; opt = opts; while (opt->name != NULL && !opt->essential) opt++; if (opt->name != NULL) { fprintf(stderr, "Essential option \"%s\" not specified.\n", opt->name); return (-1); } process_opts(); #ifdef DEBUG if(!config.inetd) print_config(); #endif return (0); }
void send_cmessage(int code, const char *msg) { sstr *smsg; smsg = sstr_dup2(msg); send_message(code, smsg); sstr_free(smsg); }
int make_tmpdir(void) { sstr *name; struct stat tmp; name = sstr_init(0); sstr_apprintf(name, "%s/tmp", config.chroot); if(stat(sstr_buf(name), &tmp) == -1) { if(mkdir(sstr_buf(name), S_IRWXU) == -1) { write_log(ERROR, "Unable to make tmp dir %s", sstr_buf(name)); sstr_free(name); return (-1); } chown(sstr_buf(name), config.uid, config.gid); sstr_free(name); } return 0; }
/* ------------------------------------------------------------- ** ** Functions to read / parse the config file. ** ------------------------------------------------------------- */ int process_cmdline(int argc, char *argv[]) { int i; struct option_array *opt; sstr *arg; char *optp, *argp; set_defaults(); if (argc <= 1) return 0; arg = sstr_init(0); i = 1; optp = argv[i] + 1; while (i < argc) { if (argv[i][0] != '-') usage(-1); opt = opts; if (*optp == '-') usage(-1); /*?TODO longopts */ else { while (opt->name != NULL && opt->cmdline != *optp) opt++; if (opt->name == NULL) usage(*optp); if (strlen(cmdline_set) < 99) cmdline_set[strlen(cmdline_set)] = *optp; if (opt->type == BOOL) { sstr_cpy2(arg, "yes"); set_opt(opt, arg); if (*(++optp) == 0) { i++; optp = argv[i] + 1; } } else { argp = optp + 1; if (*argp == 0) { if (i >= argc - 1 || *(argp = argv[++i]) == '-') usage(*optp); } sstr_cpy2(arg, argp); set_opt(opt, arg); i++; optp = argv[i] + 1; } } } sstr_free(arg); return (0); }
/* Escape non printable characters, and any characters from extras in the url * with the %xx equivalent. If % must be escaped then include it in extras. */ int urlescape(sstr * url, char *extras) { int i; sstr *tmp = sstr_init(0); for(i = 0; i < sstr_len(url); i++) { char c = sstr_getchar(url, i); if(strchr(extras, c) || !isprint(c)) { sstr_ncat(tmp, url, i); sstr_apprintf(tmp, "%%%x", c); sstr_split(url, NULL, 0, i + 1); i = -1; } } sstr_cat(tmp, url); sstr_cpy(url, tmp); sstr_free(tmp); return 0; }
void write_log(int priority, const char *msg, ...) { char *buf = NULL; int sz = MAX_LINE_LEN, n; va_list argptr; time_t tstamp; if(priority > config.loglevel) return; do { /* Modified from printf(3) man page */ if((buf = realloc(buf, sz)) == NULL) die(ERROR, "Out of memory.", 0, 0, -1); va_start(argptr, msg); n = vsnprintf(buf, sz, msg, argptr); va_end(argptr); if(n == -1) /* glibc 2.0 */ sz *= 2; /* Try a bigger buffer */ else if(n >= sz) /* C99 compliant / glibc 2.1 */ sz = n + 1; /* precisely what is needed */ else break; /*It worked */ } while(1); if(config.logfile) { sstr *s; s = sstr_init(0); time(&tstamp); sstr_cpy2(s, ctime(&tstamp)); sstr_setchar(s, sstr_chr(s, '\n'), ' '); sstr_apprintf(s, "frox[%d] %s\n", getpid(), buf); sstr_write(2, s, 0); sstr_free(s); } else { if(priority == ERROR || priority == ATTACK) { syslog(LOG_ERR | LOG_DAEMON, "%s\n", buf); } else { syslog(LOG_NOTICE | LOG_DAEMON, "%s\n", buf); } } free(buf); }
/* ------------------------------------------------------------- ** ** init ssl context ** ------------------------------------------------------------- */ void ssl_init(void) { sstr *msg; int i; if(!config.usessl) return; if(info->greeting == FAKED || info->greeting == AWAITED) { get_message(&i, &msg); if(i != 220) { die(INFO, "Unable to contact server in ntp", 421, "Server Unable to accept connection", 0); } info->greetingmsg = sstr_dup(msg); info->greeting = info->greeting == FAKED ? DONE : SUPPRESSED; } send_ccommand("AUTH", config.datassl ? "TLS-P" : "TLS"); get_message(&i, &msg); if(i != 234) { write_log(IMPORT, "SSL connection refused. " "Using unencrypted connection"); return; } SSL_load_error_strings(); SSL_library_init(); ctx = SSL_CTX_new(SSLv23_client_method()); if(!ctx) die(ERROR, "Unable to initialise SSL", 0, 0, -1); info->ssl_sc = ssl_initfd(info->server_control.fd, SSL_CTRL); if(info->ssl_sc) write_log(IMPORT, "SSL initialised on control connection"); else die(ERROR, "Unable to initialise SSL", 0, 0, -1); if(info->greeting == SUPPRESSED) { send_message(220, info->greetingmsg); info->greeting = DONE; sstr_free(info->greetingmsg); } }
void parse_match(sstr *str, struct acl_item *item) { sstr *tok; tok = sstr_init(0); item->user = NULL; sstr_token(str, tok, " \t\n", 0); item->src = strdup(sstr_buf(tok)); sstr_token(str, NULL, " \t\n", 0); /*Removes the " - " from the ACL*/ if(sstr_chr(str, '@')!=-1) { sstr_token(str, tok, "@", 0); item->user = strdup(sstr_buf(tok)); config.fakentp = TRUE; } sstr_token(str, tok, " \t\n", 0); item->dst = strdup(sstr_buf(tok)); item->ntp_ports = get_portlist(str); sstr_free(tok); }
void set_defaults(void) { sstr *tmp; tmp = sstr_init(0); config.resolvhack = NULL; config.device = NULL; sstr_cpy2(tmp, "ControlPorts 40000-50000"); parse_line(tmp); sstr_cpy2(tmp, "PassivePorts 40000-50000"); parse_line(tmp); sstr_cpy2(tmp, "ActivePorts 40000-50000"); parse_line(tmp); config.timeout = 300; config.user = NULL; config.group = NULL; config.nonasciiok = FALSE; config.bdefend = TRUE; config.loglevel = 15; config.xferlogging = TRUE; #ifdef TRANS_DATA #endif #ifdef USE_CACHE config.cachemod = NULL; #endif #ifdef USE_CCP config.ccpcmd = NULL; config.oldccp = TRUE; #endif #ifdef DO_VSCAN config.vscanner = NULL; config.vscanok = 0; config.vscanpm = 0; #endif #ifdef DO_SSL config.datassl = TRUE; config.anonssl = TRUE; #endif sstr_free(tmp); }
/* ------------------------------------------------------------- ** ** Send message to client ** ------------------------------------------------------------- */ void send_message(int code, sstr * msg) { sstr *buf; if(!ccp_allowmsg(&code, msg)) return; buf = sstr_init(MAX_LINE_LEN + 10); if(code != 0) sstr_apprintf(buf, "%d%c", abs(code), code > 0 ? ' ' : '-'); sstr_cat(buf, msg); sstr_ncat2(buf, "\r\n", 2); #ifdef DEBUG fprintf(stderr, " S: \033[34m%s\033[37m", sstr_buf(buf)); #else write_log(VERBOSE, " S: %s", sstr_buf(buf)); #endif sstr_write(info->client_control.fd, buf, 0); sstr_free(buf); }
u_int16_t *get_portlist(sstr * str) { sstr *tok; int i = 0, sep; u_int16_t *ports = NULL; tok = sstr_init(0); do { ports = realloc(ports, (i + 2) * sizeof(u_int16_t)); if(!ports) die(ERROR, "Out of memory reading config file", 0, 0, -1); sep = sstr_token(str, tok, " \t,-\r\n", 0); if (sep == -1) break; if (!sstr_cmp2(tok, "*")) { ports[i] = 1; ports[i + 1] = 0xFFFF; i += 2; break; } ports[i] = ports[i + 1] = sstr_atoi(tok); if (sep == '-') { sep = sstr_token(str, tok, " \t,\n", 0); if (sep == -1) ports[i + 1] = 0xFFFF; else ports[i + 1] = sstr_atoi(tok); } i += 2; } while (sep != -1); ports[i] = ports[i + 1] = 0; sstr_free(tok); return (ports); }
/* ------------------------------------------------------------- ** ** Send command to server ** ------------------------------------------------------------- */ void send_command(sstr * cmd, sstr * arg) { sstr *buf; buf = sstr_init(MAX_LINE_LEN + 10); sstr_cat(buf, cmd); if(sstr_len(arg) != 0) { sstr_ncat2(buf, " ", 1); sstr_cat(buf, arg); } sstr_ncat2(buf, "\r\n", 2); #ifdef DEBUG fprintf(stderr, " C: \033[31m%s\033[37m", sstr_buf(buf)); #else write_log(VERBOSE, " C: %s", sstr_buf(buf)); #endif if(info->ssl_sc) ssl_write(info->ssl_sc, buf); else sstr_write(info->server_control.fd, buf, 0); sstr_free(buf); }
static int _cmd_to_proto(char **target, const char *format, va_list ap) { const char *c = format; char *cmd = NULL; /* final command */ int pos; /* position in final command */ sstr curarg, newarg; int touched = 0; /* was the current argument touched? */ char **curargv = NULL, **newargv = NULL; int argc = 0; int totlen = 0; int j; /* Abort if there is not target to set */ if (target == NULL) return -1; /* Build the command string accordingly to protocol */ curarg = sstr_empty(); if (curarg == NULL) return -1; while(*c != '\0') { if (*c != '%' || c[1] == '\0') { if (*c == ' ') { if (touched) { newargv = realloc(curargv,sizeof(char*)*(argc+1)); if (newargv == NULL) goto err; curargv = newargv; curargv[argc++] = curarg; totlen += bulklen(sstr_len(curarg)); /* curarg is put in argv so it can be overwritten. */ curarg = sstr_empty(); if (curarg == NULL) goto err; touched = 0; } } else { newarg = sstr_catlen(curarg,c,1); if (newarg == NULL) goto err; curarg = newarg; touched = 1; } } else { char *arg; size_t size; /* Set newarg so it can be checked even if it is not touched. */ newarg = curarg; switch(c[1]) { case 's': arg = va_arg(ap,char*); size = strlen(arg); if (size > 0) newarg = sstr_catlen(curarg,arg,size); break; case 'b': arg = va_arg(ap,char*); size = va_arg(ap,size_t); if (size > 0) newarg = sstr_catlen(curarg,arg,size); break; case '%': newarg = sstr_cat(curarg,"%"); break; default: /* Try to detect printf format */ { static const char intfmts[] = "diouxX"; char _format[16]; const char *_p = c+1; size_t _l = 0; va_list _cpy; /* Flags */ if (*_p != '\0' && *_p == '#') _p++; if (*_p != '\0' && *_p == '0') _p++; if (*_p != '\0' && *_p == '-') _p++; if (*_p != '\0' && *_p == ' ') _p++; if (*_p != '\0' && *_p == '+') _p++; /* Field width */ while (*_p != '\0' && isdigit(*_p)) _p++; /* Precision */ if (*_p == '.') { _p++; while (*_p != '\0' && isdigit(*_p)) _p++; } /* Copy va_list before consuming with va_arg */ va_copy(_cpy,ap); /* Integer conversion (without modifiers) */ if (strchr(intfmts,*_p) != NULL) { va_arg(ap,int); goto fmt_valid; } /* Double conversion (without modifiers) */ if (strchr("eEfFgGaA",*_p) != NULL) { va_arg(ap,double); goto fmt_valid; } /* Size: char */ if (_p[0] == 'h' && _p[1] == 'h') { _p += 2; if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { va_arg(ap,int); /* char gets promoted to int */ goto fmt_valid; } goto fmt_invalid; } /* Size: short */ if (_p[0] == 'h') { _p += 1; if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { va_arg(ap,int); /* short gets promoted to int */ goto fmt_valid; } goto fmt_invalid; } /* Size: long long */ if (_p[0] == 'l' && _p[1] == 'l') { _p += 2; if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { va_arg(ap,long long); goto fmt_valid; } goto fmt_invalid; } /* Size: long */ if (_p[0] == 'l') { _p += 1; if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { va_arg(ap,long); goto fmt_valid; } goto fmt_invalid; } fmt_invalid: va_end(_cpy); goto err; fmt_valid: _l = (_p+1)-c; if (_l < sizeof(_format)-2) { memcpy(_format,c,_l); _format[_l] = '\0'; newarg = sstr_catvprintf(curarg,_format,_cpy); /* Update current position (note: outer blocks * increment c twice so compensate here) */ c = _p-1; } va_end(_cpy); break; } } if (newarg == NULL) goto err; curarg = newarg; touched = 1; c++; } c++; } /* Add the last argument if needed */ if (touched) { newargv = realloc(curargv,sizeof(char*)*(argc+1)); if (newargv == NULL) goto err; curargv = newargv; curargv[argc++] = curarg; totlen += bulklen(sstr_len(curarg)); } else { sstr_free(curarg); } /* Clear curarg because it was put in curargv or was free'd. */ curarg = NULL; /* Add bytes needed to hold multi bulk count */ totlen += 1+intlen(argc)+2; /* Build the command at protocol level */ cmd = malloc(totlen+1); if (cmd == NULL) goto err; pos = sprintf(cmd,"*%d\r\n",argc); for (j = 0; j < argc; j++) { pos += sprintf(cmd+pos,"$%zu\r\n",sstr_len(curargv[j])); memcpy(cmd+pos,curargv[j],sstr_len(curargv[j])); pos += sstr_len(curargv[j]); sstr_free(curargv[j]); cmd[pos++] = '\r'; cmd[pos++] = '\n'; } assert(pos == totlen); cmd[pos] = '\0'; free(curargv); *target = cmd; return totlen; err: while(argc--) sstr_free(curargv[argc]); free(curargv); if (curarg != NULL) sstr_free(curarg); /* No need to check cmd since it is the last statement that can fail, * but do it anyway to be as defensive as possible. */ if (cmd != NULL) free(cmd); return -1; }
int set_opt(struct option_array *opt, sstr * str) { sstr *tok; int sep; if (rereading && !opt->reloadable) return (0); tok = sstr_init(0); sstr_ncat2(str, "\n", 1); /*For sstr_token */ opt->essential = FALSE; switch (opt->type) { case BOOL: sstr_token(str, tok, " \t\r\n", 0); if (!sstr_casecmp2(tok, "yes")) *(int *) opt->var = TRUE; else if (!sstr_casecmp2(tok, "no")) *(int *) opt->var = FALSE; else return (-1); break; case FILENAME: case STRING: sstr_token(str, tok, " \t\r\n", SSTR_QTOK); *(char **) opt->var = strdup(sstr_buf(tok)); break; case DIRECTRY: sstr_token(str, tok, " \t\r\n", SSTR_QTOK); if (sstr_getchar(tok, sstr_len(tok) - 1) != '/') sstr_ncat2(tok, "/", 1); *(char **) opt->var = strdup(sstr_buf(tok)); break; case ADDRESS: sstr_token(str, tok, " \t\r\n", 0); return name2addr(tok, (struct in_addr *) opt->var); break; case ADDRPRT: sep = sstr_token(str, tok, " :\t\r\n", 0); if( name2addr(tok, &((struct sockaddr_in *) opt->var)->sin_addr)==-1) return(-1); if (sep == ':') ((struct sockaddr_in *) opt->var)->sin_port = ntohs(sstr_atoi(str)); ((struct sockaddr_in *) opt->var)->sin_family = AF_INET; break; case INT: sstr_token(str, tok, " \t\r\n", 0); *(int *) opt->var = sstr_atoi(tok); break; case PRTRNGE: sstr_token(str, tok, " \t-,\r\n", 0); ((int *) opt->var)[0] = sstr_atoi(tok); ((int *) opt->var)[1] = sstr_atoi(str); if(!valid_uint16(((int *) opt->var)[0]) || !valid_uint16(((int *) opt->var)[1])) { fprintf(stderr, "Port out of range\n"); return -1; } if(((int *) opt->var)[0] >= ((int *) opt->var)[1]) { fprintf(stderr, "Port range is inverted\n"); return -1; } break; case ACL: return store_acl(str, (struct acl_list *) opt->var); break; case SUBSECT: return store_subsect(str, (struct subsect_list *) opt->var); } sstr_free(tok); return (0); }