| /* |
| * stunnel TLS offloading and load-balancing proxy |
| * Copyright (C) 1998-2015 Michal Trojnara <Michal.Trojnara@mirt.net> |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2 of the License, or (at your |
| * option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * See the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, see <http://www.gnu.org/licenses>. |
| * |
| * Linking stunnel statically or dynamically with other modules is making |
| * a combined work based on stunnel. Thus, the terms and conditions of |
| * the GNU General Public License cover the whole combination. |
| * |
| * In addition, as a special exception, the copyright holder of stunnel |
| * gives you permission to combine stunnel with free software programs or |
| * libraries that are released under the GNU LGPL and with code included |
| * in the standard release of OpenSSL under the OpenSSL License (or |
| * modified versions of such code, with unchanged license). You may copy |
| * and distribute such a system following the terms of the GNU GPL for |
| * stunnel and the licenses of the other code concerned. |
| * |
| * Note that people who make modified versions of stunnel are not obligated |
| * to grant this special exception for their modified versions; it is their |
| * choice whether to do so. The GNU General Public License gives permission |
| * to release a modified version without this exception; this exception |
| * also makes it possible to release a modified version which carries |
| * forward this exception. |
| */ |
| |
| #include "common.h" |
| #include "prototypes.h" |
| |
| #if defined(_WIN32_WCE) && !defined(CONFDIR) |
| #define CONFDIR "\\stunnel" |
| #endif |
| |
| #define CONFLINELEN (16*1024) |
| |
| typedef enum { |
| CMD_BEGIN, /* initialize defaults */ |
| CMD_EXEC, /* process command */ |
| CMD_END, /* end of section */ |
| CMD_FREE, /* TODO: deallocate memory */ |
| CMD_DEFAULT, /* print default value */ |
| CMD_HELP /* print help */ |
| } CMD; |
| |
| NOEXPORT int options_file(char *, CONF_TYPE, SERVICE_OPTIONS **); |
| NOEXPORT int options_include(char *, SERVICE_OPTIONS **); |
| #ifdef USE_WIN32 |
| struct dirent { |
| char d_name[MAX_PATH]; |
| }; |
| int scandir(const char *, struct dirent ***, |
| int (*)(const struct dirent *), |
| int (*)(const struct dirent **, const struct dirent **)); |
| int alphasort(const struct dirent **, const struct dirent **); |
| #endif |
| NOEXPORT char *parse_global_option(CMD, char *, char *); |
| NOEXPORT char *parse_service_option(CMD, SERVICE_OPTIONS *, char *, char *); |
| |
| #ifndef OPENSSL_NO_TLSEXT |
| NOEXPORT char *sni_init(SERVICE_OPTIONS *); |
| #endif /* !defined(OPENSSL_NO_TLSEXT) */ |
| |
| NOEXPORT char *parse_debug_level(char *, SERVICE_OPTIONS *); |
| |
| #ifndef OPENSSL_NO_PSK |
| NOEXPORT PSK_KEYS *psk_read(char *); |
| NOEXPORT void psk_free(PSK_KEYS *); |
| #endif /* !defined(OPENSSL_NO_PSK) */ |
| |
| typedef struct { |
| char *name; |
| long value; |
| } SSL_OPTION; |
| |
| static const SSL_OPTION ssl_opts[] = { |
| {"MICROSOFT_SESS_ID_BUG", SSL_OP_MICROSOFT_SESS_ID_BUG}, |
| {"NETSCAPE_CHALLENGE_BUG", SSL_OP_NETSCAPE_CHALLENGE_BUG}, |
| #ifdef SSL_OP_LEGACY_SERVER_CONNECT |
| {"LEGACY_SERVER_CONNECT", SSL_OP_LEGACY_SERVER_CONNECT}, |
| #endif |
| {"NETSCAPE_REUSE_CIPHER_CHANGE_BUG", |
| SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG}, |
| #ifdef SSL_OP_TLSEXT_PADDING |
| {"TLSEXT_PADDING", SSL_OP_TLSEXT_PADDING}, |
| #endif |
| {"MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER}, |
| #ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG |
| {"SAFARI_ECDHE_ECDSA_BUG", SSL_OP_SAFARI_ECDHE_ECDSA_BUG}, |
| #endif |
| {"SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG}, |
| {"TLS_D5_BUG", SSL_OP_TLS_D5_BUG}, |
| {"TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG}, |
| #ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING |
| {"MSIE_SSLV2_RSA_PADDING", SSL_OP_MSIE_SSLV2_RSA_PADDING}, |
| #endif |
| {"SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG}, |
| #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS |
| {"DONT_INSERT_EMPTY_FRAGMENTS", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS}, |
| #endif |
| {"ALL", (long)SSL_OP_ALL}, |
| #ifdef SSL_OP_NO_QUERY_MTU |
| {"NO_QUERY_MTU", SSL_OP_NO_QUERY_MTU}, |
| #endif |
| #ifdef SSL_OP_COOKIE_EXCHANGE |
| {"COOKIE_EXCHANGE", SSL_OP_COOKIE_EXCHANGE}, |
| #endif |
| #ifdef SSL_OP_NO_TICKET |
| {"NO_TICKET", SSL_OP_NO_TICKET}, |
| #endif |
| #ifdef SSL_OP_CISCO_ANYCONNECT |
| {"CISCO_ANYCONNECT", SSL_OP_CISCO_ANYCONNECT}, |
| #endif |
| #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
| {"NO_SESSION_RESUMPTION_ON_RENEGOTIATION", |
| SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION}, |
| #endif |
| #ifdef SSL_OP_NO_COMPRESSION |
| {"NO_COMPRESSION", SSL_OP_NO_COMPRESSION}, |
| #endif |
| #ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION |
| {"ALLOW_UNSAFE_LEGACY_RENEGOTIATION", |
| SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION}, |
| #endif |
| #ifdef SSL_OP_SINGLE_ECDH_USE |
| {"SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE}, |
| #endif |
| {"SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE}, |
| {"EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA}, |
| #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE |
| {"CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE}, |
| #endif |
| {"TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG}, |
| {"NO_SSLv2", SSL_OP_NO_SSLv2}, |
| {"NO_SSLv3", SSL_OP_NO_SSLv3}, |
| {"NO_TLSv1", SSL_OP_NO_TLSv1}, |
| #ifdef SSL_OP_NO_TLSv1_1 |
| {"NO_TLSv1.1", SSL_OP_NO_TLSv1_1}, |
| #endif |
| #ifdef SSL_OP_NO_TLSv1_2 |
| {"NO_TLSv1.2", SSL_OP_NO_TLSv1_2}, |
| #endif |
| {"PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1}, |
| {"PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2}, |
| {"NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG}, |
| #ifdef SSL_OP_NON_EXPORT_FIRST |
| {"NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST}, |
| #endif |
| {"NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG}, |
| #ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG |
| {"CRYPTOPRO_TLSEXT_BUG", (long)SSL_OP_CRYPTOPRO_TLSEXT_BUG}, |
| #endif |
| {NULL, 0} |
| }; |
| |
| NOEXPORT long parse_ssl_option(char *); |
| NOEXPORT void print_ssl_options(void); |
| |
| NOEXPORT int print_socket_options(void); |
| NOEXPORT char *print_option(int, OPT_UNION *); |
| NOEXPORT int parse_socket_option(char *); |
| |
| #ifndef OPENSSL_NO_OCSP |
| NOEXPORT unsigned long parse_ocsp_flag(char *); |
| #endif /* !defined(OPENSSL_NO_OCSP) */ |
| |
| #ifndef OPENSSL_NO_ENGINE |
| NOEXPORT void engine_reset_list(void); |
| NOEXPORT char *engine_auto(void); |
| NOEXPORT char *engine_open(const char *); |
| NOEXPORT char *engine_ctrl(const char *, const char *); |
| NOEXPORT char *engine_default(const char *); |
| NOEXPORT char *engine_init(void); |
| NOEXPORT void engine_next(void); |
| NOEXPORT ENGINE *engine_get_by_id(const char *); |
| NOEXPORT ENGINE *engine_get_by_num(const int); |
| #endif /* !defined(OPENSSL_NO_ENGINE) */ |
| |
| NOEXPORT void print_syntax(void); |
| |
| NOEXPORT void name_list_append(NAME_LIST **, char *); |
| #ifndef USE_WIN32 |
| NOEXPORT char **argalloc(char *); |
| #endif |
| |
| char *configuration_file= |
| #ifdef CONFDIR |
| CONFDIR |
| #ifdef USE_WIN32 |
| "\\" |
| #else |
| "/" |
| #endif |
| #endif |
| "stunnel.conf"; |
| |
| GLOBAL_OPTIONS global_options; |
| SERVICE_OPTIONS service_options; |
| |
| static GLOBAL_OPTIONS new_global_options; |
| static SERVICE_OPTIONS new_service_options; |
| |
| static char *option_not_found= |
| "Specified option name is not valid here"; |
| |
| static char *stunnel_cipher_list= |
| "HIGH:+3DES:+DH:!aNULL:!SSLv2"; |
| |
| /**************************************** parse commandline parameters */ |
| |
| int options_cmdline(char *name, char *parameter) { |
| CONF_TYPE type=CONF_FILE; |
| |
| #ifdef USE_WIN32 |
| (void)parameter; /* skip warning about unused parameter */ |
| #endif |
| if(!name) { |
| /* leave the previous value of configuration_file */ |
| } else if(!strcasecmp(name, "-help")) { |
| parse_global_option(CMD_HELP, NULL, NULL); |
| parse_service_option(CMD_HELP, NULL, NULL, NULL); |
| log_flush(LOG_MODE_INFO); |
| return 1; |
| } else if(!strcasecmp(name, "-version")) { |
| parse_global_option(CMD_DEFAULT, NULL, NULL); |
| parse_service_option(CMD_DEFAULT, NULL, NULL, NULL); |
| log_flush(LOG_MODE_INFO); |
| return 1; |
| } else if(!strcasecmp(name, "-sockets")) { |
| print_socket_options(); |
| log_flush(LOG_MODE_INFO); |
| return 1; |
| } else if(!strcasecmp(name, "-options")) { |
| print_ssl_options(); |
| log_flush(LOG_MODE_INFO); |
| return 1; |
| } else |
| #ifndef USE_WIN32 |
| if(!strcasecmp(name, "-fd")) { |
| if(!parameter) { |
| s_log(LOG_ERR, "No file descriptor specified"); |
| print_syntax(); |
| return 1; |
| } |
| configuration_file=parameter; |
| type=CONF_FD; |
| } else |
| #endif |
| configuration_file=name; |
| configuration_file=str_dup(configuration_file); |
| str_detach(configuration_file); /* do not track this allocation */ |
| |
| return options_parse(type); |
| } |
| |
| /**************************************** parse configuration file */ |
| |
| int options_parse(CONF_TYPE type) { |
| SERVICE_OPTIONS *section; |
| char *errstr; |
| |
| options_defaults(); |
| section=&new_service_options; |
| if(options_file(configuration_file, type, §ion)) |
| return 1; |
| |
| if(new_service_options.next) { /* daemon mode: initialize sections */ |
| for(section=new_service_options.next; section; section=section->next) { |
| s_log(LOG_INFO, "Initializing service [%s]", section->servname); |
| errstr=parse_service_option(CMD_END, section, NULL, NULL); |
| if(errstr) |
| break; |
| } |
| } else { /* inetd mode: need to initialize global options */ |
| errstr=parse_global_option(CMD_END, NULL, NULL); |
| if(errstr) { |
| s_log(LOG_ERR, "Global options: %s", errstr); |
| return 1; |
| } |
| s_log(LOG_INFO, "Initializing inetd mode configuration"); |
| section=&new_service_options; |
| errstr=parse_service_option(CMD_END, section, NULL, NULL); |
| } |
| if(errstr) { |
| s_log(LOG_ERR, "Service [%s]: %s", section->servname, errstr); |
| return 1; |
| } |
| |
| s_log(LOG_NOTICE, "Configuration successful"); |
| return 0; |
| } |
| |
| NOEXPORT int options_file(char *path, CONF_TYPE type, SERVICE_OPTIONS **section) { |
| DISK_FILE *df; |
| char line_text[CONFLINELEN], *errstr; |
| char config_line[CONFLINELEN], *config_opt, *config_arg; |
| int i, line_number=0; |
| #ifndef USE_WIN32 |
| int fd; |
| char *tmp_str; |
| #endif |
| |
| s_log(LOG_NOTICE, "Reading configuration from %s %s", |
| type==CONF_FD ? "descriptor" : "file", path); |
| #ifndef USE_WIN32 |
| if(type==CONF_FD) { /* file descriptor */ |
| fd=(int)strtol(path, &tmp_str, 10); |
| if(tmp_str==path || *tmp_str) { /* not a number */ |
| s_log(LOG_ERR, "Invalid file descriptor number"); |
| print_syntax(); |
| return 1; |
| } |
| df=file_fdopen(fd); |
| } else |
| #endif |
| df=file_open(path, FILE_MODE_READ); |
| if(!df) { |
| s_log(LOG_ERR, "Cannot open configuration file"); |
| if(type!=CONF_RELOAD) |
| print_syntax(); |
| return 1; |
| } |
| |
| while(file_getline(df, line_text, CONFLINELEN)>=0) { |
| memcpy(config_line, line_text, CONFLINELEN); |
| ++line_number; |
| config_opt=config_line; |
| if(line_number==1) { |
| if(config_opt[0]==(char)0xef && |
| config_opt[1]==(char)0xbb && |
| config_opt[2]==(char)0xbf) { |
| s_log(LOG_NOTICE, "UTF-8 byte order mark detected"); |
| config_opt+=3; |
| } else { |
| s_log(LOG_NOTICE, "UTF-8 byte order mark not detected"); |
| } |
| } |
| |
| while(isspace((unsigned char)*config_opt)) |
| ++config_opt; /* remove initial whitespaces */ |
| for(i=(int)strlen(config_opt)-1; i>=0 && isspace((unsigned char)config_opt[i]); --i) |
| config_opt[i]='\0'; /* remove trailing whitespaces */ |
| if(config_opt[0]=='\0' || config_opt[0]=='#' || config_opt[0]==';') /* empty or comment */ |
| continue; |
| |
| if(config_opt[0]=='[' && config_opt[strlen(config_opt)-1]==']') { /* new section */ |
| SERVICE_OPTIONS *new_section; |
| |
| if(!new_service_options.next) { /* initialize global options */ |
| errstr=parse_global_option(CMD_END, NULL, NULL); |
| if(errstr) { |
| s_log(LOG_ERR, "%s:%d: \"%s\": %s", |
| path, line_number, line_text, errstr); |
| file_close(df); |
| return 1; |
| } |
| } |
| ++config_opt; |
| config_opt[strlen(config_opt)-1]='\0'; |
| new_section=str_alloc(sizeof(SERVICE_OPTIONS)); |
| memcpy(new_section, &new_service_options, sizeof(SERVICE_OPTIONS)); |
| new_section->servname=str_dup(config_opt); |
| new_section->session=NULL; |
| new_section->next=NULL; |
| (*section)->next=new_section; |
| *section=new_section; |
| continue; |
| } |
| |
| config_arg=strchr(config_line, '='); |
| if(!config_arg) { |
| s_log(LOG_ERR, "%s:%d: \"%s\": No '=' found", |
| path, line_number, line_text); |
| file_close(df); |
| return 1; |
| } |
| *config_arg++='\0'; /* split into option name and argument value */ |
| for(i=(int)strlen(config_opt)-1; i>=0 && isspace((unsigned char)config_opt[i]); --i) |
| config_opt[i]='\0'; /* remove trailing whitespaces */ |
| while(isspace((unsigned char)*config_arg)) |
| ++config_arg; /* remove initial whitespaces */ |
| |
| if(!strcasecmp(config_opt, "include")) { |
| if(options_include(config_arg, section)) { |
| s_log(LOG_ERR, "%s:%d: Failed to include directory \"%s\"", |
| path, line_number, config_arg); |
| file_close(df); |
| return 1; |
| } |
| continue; |
| } |
| |
| errstr=option_not_found; |
| /* try global options first (e.g. for 'debug') */ |
| if(!new_service_options.next) |
| errstr=parse_global_option(CMD_EXEC, config_opt, config_arg); |
| if(errstr==option_not_found) |
| errstr=parse_service_option(CMD_EXEC, *section, config_opt, config_arg); |
| if(errstr) { |
| s_log(LOG_ERR, "%s:%d: \"%s\": %s", |
| path, line_number, line_text, errstr); |
| file_close(df); |
| return 1; |
| } |
| } |
| file_close(df); |
| return 0; |
| } |
| |
| NOEXPORT int options_include(char *directory, SERVICE_OPTIONS **section) { |
| struct dirent **namelist; |
| int i, num, err=0; |
| |
| num=scandir(directory, &namelist, NULL, alphasort); |
| if(num<0) { |
| ioerror("scandir"); |
| return 1; |
| } |
| for(i=0; i<num; ++i) { |
| if(!err) { |
| struct stat sb; |
| char *name=str_printf( |
| #ifdef USE_WIN32 |
| "%s\\%s", |
| #else |
| "%s/%s", |
| #endif |
| directory, namelist[i]->d_name); |
| stat(name, &sb); |
| if(S_ISREG(sb.st_mode)) |
| err=options_file(name, CONF_FILE, section); |
| else |
| s_log(LOG_DEBUG, "\"%s\" is not a file", name); |
| str_free(name); |
| } |
| free(namelist[i]); |
| } |
| free(namelist); |
| return err; |
| } |
| |
| #ifdef USE_WIN32 |
| |
| int scandir(const char *dirp, struct dirent ***namelist, |
| int (*filter)(const struct dirent *), |
| int (*compar)(const struct dirent **, const struct dirent **)) { |
| WIN32_FIND_DATA data; |
| HANDLE h; |
| unsigned num=0, allocated=0; |
| LPTSTR path, pattern; |
| char *name; |
| DWORD saved_errno; |
| |
| (void)filter; /* skip warning about unused parameter */ |
| (void)compar; /* skip warning about unused parameter */ |
| path=str2tstr(dirp); |
| pattern=str_tprintf(TEXT("%s\\*"), path); |
| str_free(path); |
| h=FindFirstFile(pattern, &data); |
| saved_errno=GetLastError(); |
| str_free(pattern); |
| SetLastError(saved_errno); |
| if(h==INVALID_HANDLE_VALUE) |
| return -1; |
| *namelist=NULL; |
| do { |
| if(num>=allocated) { |
| allocated+=16; |
| *namelist=realloc(*namelist, allocated*sizeof(**namelist)); |
| } |
| (*namelist)[num]=malloc(sizeof(struct dirent)); |
| if(!(*namelist)[num]) |
| return -1; |
| name=tstr2str(data.cFileName); |
| strncpy((*namelist)[num]->d_name, name, MAX_PATH-1); |
| (*namelist)[num]->d_name[MAX_PATH-1]='\0'; |
| str_free(name); |
| ++num; |
| } while(FindNextFile(h, &data)); |
| FindClose(h); |
| return (int)num; |
| } |
| |
| int alphasort(const struct dirent **a, const struct dirent **b) { |
| (void)a; /* skip warning about unused parameter */ |
| (void)b; /* skip warning about unused parameter */ |
| /* most Windows filesystem return sorted data */ |
| return 0; |
| } |
| |
| #endif |
| |
| void options_defaults() { |
| /* initialize globals *before* opening the config file */ |
| memset(&new_global_options, 0, sizeof(GLOBAL_OPTIONS)); /* reset global options */ |
| memset(&new_service_options, 0, sizeof(SERVICE_OPTIONS)); /* reset local options */ |
| new_service_options.next=NULL; |
| parse_global_option(CMD_BEGIN, NULL, NULL); |
| parse_service_option(CMD_BEGIN, &new_service_options, NULL, NULL); |
| } |
| |
| void options_apply() { /* apply default/validated configuration */ |
| /* FIXME: this operation may be unsafe, as client() threads use it */ |
| memcpy(&global_options, &new_global_options, sizeof(GLOBAL_OPTIONS)); |
| /* service_options are used for inetd mode and to enumerate services */ |
| memcpy(&service_options, &new_service_options, sizeof(SERVICE_OPTIONS)); |
| } |
| |
| /**************************************** global options */ |
| |
| NOEXPORT char *parse_global_option(CMD cmd, char *opt, char *arg) { |
| char *tmp_str; |
| #ifndef USE_WIN32 |
| struct group *gr; |
| struct passwd *pw; |
| #endif |
| |
| if(cmd==CMD_DEFAULT || cmd==CMD_HELP) { |
| s_log(LOG_NOTICE, " "); |
| s_log(LOG_NOTICE, "Global options:"); |
| } |
| |
| /* chroot */ |
| #ifdef HAVE_CHROOT |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.chroot_dir=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "chroot")) |
| break; |
| new_global_options.chroot_dir=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = directory to chroot stunnel process", "chroot"); |
| break; |
| } |
| #endif /* HAVE_CHROOT */ |
| |
| /* compression */ |
| #ifndef OPENSSL_NO_COMP |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.compression=COMP_NONE; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "compression")) |
| break; |
| if(SSLeay()>=0x00908051L && !strcasecmp(arg, "deflate")) |
| new_global_options.compression=COMP_DEFLATE; |
| else if(!strcasecmp(arg, "zlib")) |
| new_global_options.compression=COMP_ZLIB; |
| else |
| return "Specified compression type is not available"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = compression type", |
| "compression"); |
| break; |
| } |
| #endif /* !defined(OPENSSL_NO_COMP) */ |
| |
| /* debug */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_service_options.log_level=LOG_NOTICE; |
| #if !defined (USE_WIN32) && !defined (__vms) |
| new_global_options.log_facility=LOG_DAEMON; |
| #endif |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "debug")) |
| break; |
| return parse_debug_level(arg, &new_service_options); |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| #if !defined (USE_WIN32) && !defined (__vms) |
| s_log(LOG_NOTICE, "%-22s = %s", "debug", "daemon.notice"); |
| #else |
| s_log(LOG_NOTICE, "%-22s = %s", "debug", "notice"); |
| #endif |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = [facility].level (e.g. daemon.info)", "debug"); |
| break; |
| } |
| |
| /* EGD */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| #ifdef EGD_SOCKET |
| new_global_options.egd_sock=EGD_SOCKET; |
| #else |
| new_global_options.egd_sock=NULL; |
| #endif |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "EGD")) |
| break; |
| new_global_options.egd_sock=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| #ifdef EGD_SOCKET |
| s_log(LOG_NOTICE, "%-22s = %s", "EGD", EGD_SOCKET); |
| #endif |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = path to Entropy Gathering Daemon socket", "EGD"); |
| break; |
| } |
| |
| #ifndef OPENSSL_NO_ENGINE |
| |
| /* engine */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| engine_reset_list(); |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "engine")) |
| break; |
| if(!strcasecmp(arg, "auto")) |
| return engine_auto(); |
| else |
| return engine_open(arg); |
| case CMD_END: |
| engine_next(); |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = auto|engine_id", |
| "engine"); |
| break; |
| } |
| |
| /* engineCtrl */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "engineCtrl")) |
| break; |
| tmp_str=strchr(arg, ':'); |
| if(tmp_str) |
| *tmp_str++='\0'; |
| return engine_ctrl(arg, tmp_str); |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = cmd[:arg]", |
| "engineCtrl"); |
| break; |
| } |
| |
| /* engineDefault */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "engineDefault")) |
| break; |
| return engine_default(arg); |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = TASK_LIST", |
| "engineDefault"); |
| break; |
| } |
| |
| #endif /* !defined(OPENSSL_NO_ENGINE) */ |
| |
| /* fips */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| #ifdef USE_FIPS |
| new_global_options.option.fips=0; |
| #endif /* USE_FIPS */ |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "fips")) |
| break; |
| #ifdef USE_FIPS |
| if(!strcasecmp(arg, "yes")) |
| new_global_options.option.fips=1; |
| else if(!strcasecmp(arg, "no")) |
| new_global_options.option.fips=0; |
| else |
| return "The argument needs to be either 'yes' or 'no'"; |
| #else |
| if(strcasecmp(arg, "no")) |
| return "FIPS support is not available"; |
| #endif /* USE_FIPS */ |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| #ifdef USE_FIPS |
| s_log(LOG_NOTICE, "%-22s = yes|no FIPS 140-2 mode", |
| "fips"); |
| #endif /* USE_FIPS */ |
| break; |
| } |
| |
| /* foreground */ |
| #ifndef USE_WIN32 |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.option.foreground=0; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "foreground")) |
| break; |
| if(!strcasecmp(arg, "yes")) |
| new_global_options.option.foreground=1; |
| else if(!strcasecmp(arg, "no")) |
| new_global_options.option.foreground=0; |
| else |
| return "The argument needs to be either 'yes' or 'no'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = yes|no foreground mode (don't fork, log to stderr)", |
| "foreground"); |
| break; |
| } |
| #endif |
| |
| #ifdef ICON_IMAGE |
| |
| /* iconActive */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.icon[ICON_ACTIVE]=load_icon_default(ICON_ACTIVE); |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "iconActive")) |
| break; |
| if(!(new_global_options.icon[ICON_ACTIVE]=load_icon_file(arg))) |
| return "Failed to load the specified icon"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = icon when connections are established", "iconActive"); |
| break; |
| } |
| |
| /* iconError */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.icon[ICON_ERROR]=load_icon_default(ICON_ERROR); |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "iconError")) |
| break; |
| if(!(new_global_options.icon[ICON_ERROR]=load_icon_file(arg))) |
| return "Failed to load the specified icon"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = icon for invalid configuration file", "iconError"); |
| break; |
| } |
| |
| /* iconIdle */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.icon[ICON_IDLE]=load_icon_default(ICON_IDLE); |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "iconIdle")) |
| break; |
| if(!(new_global_options.icon[ICON_IDLE]=load_icon_file(arg))) |
| return "Failed to load the specified icon"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = icon when no connections were established", "iconIdle"); |
| break; |
| } |
| |
| #endif /* ICON_IMAGE */ |
| |
| /* log */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.log_file_mode=FILE_MODE_APPEND; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "log")) |
| break; |
| if(!strcasecmp(arg, "append")) |
| new_global_options.log_file_mode=FILE_MODE_APPEND; |
| else if(!strcasecmp(arg, "overwrite")) |
| new_global_options.log_file_mode=FILE_MODE_OVERWRITE; |
| else |
| return "The argument needs to be either 'append' or 'overwrite'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = append|overwrite log file", |
| "log"); |
| break; |
| } |
| |
| /* output */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.output_file=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "output")) |
| break; |
| new_global_options.output_file=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = file to append log messages", "output"); |
| break; |
| } |
| |
| /* pid */ |
| #ifndef USE_WIN32 |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.pidfile=NULL; /* do not create a pid file */ |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "pid")) |
| break; |
| if(arg[0]) /* is argument not empty? */ |
| new_global_options.pidfile=str_dup(arg); |
| else |
| new_global_options.pidfile=NULL; /* empty -> do not create a pid file */ |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = pid file", "pid"); |
| break; |
| } |
| #endif |
| |
| /* RNDbytes */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.random_bytes=RANDOM_BYTES; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "RNDbytes")) |
| break; |
| new_global_options.random_bytes=(long)strtol(arg, &tmp_str, 10); |
| if(tmp_str==arg || *tmp_str) /* not a number */ |
| return "Illegal number of bytes to read from random seed files"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = %d", "RNDbytes", RANDOM_BYTES); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = bytes to read from random seed files", "RNDbytes"); |
| break; |
| } |
| |
| /* RNDfile */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.rand_file=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "RNDfile")) |
| break; |
| new_global_options.rand_file=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| #ifdef RANDOM_FILE |
| s_log(LOG_NOTICE, "%-22s = %s", "RNDfile", RANDOM_FILE); |
| #endif |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = path to file with random seed data", "RNDfile"); |
| break; |
| } |
| |
| /* RNDoverwrite */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.option.rand_write=1; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "RNDoverwrite")) |
| break; |
| if(!strcasecmp(arg, "yes")) |
| new_global_options.option.rand_write=1; |
| else if(!strcasecmp(arg, "no")) |
| new_global_options.option.rand_write=0; |
| else |
| return "The argument needs to be either 'yes' or 'no'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = yes", "RNDoverwrite"); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = yes|no overwrite seed datafiles with new random data", |
| "RNDoverwrite"); |
| break; |
| } |
| |
| #ifndef USE_WIN32 |
| /* service */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_service_options.servname=str_dup("stunnel"); |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "service")) |
| break; |
| new_service_options.servname=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = service name", "service"); |
| break; |
| } |
| #endif |
| |
| #ifndef USE_WIN32 |
| /* setgid */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.gid=0; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "setgid")) |
| break; |
| gr=getgrnam(arg); |
| if(gr) { |
| new_global_options.gid=gr->gr_gid; |
| return NULL; /* OK */ |
| } |
| new_global_options.gid=(gid_t)strtol(arg, &tmp_str, 10); |
| if(tmp_str==arg || *tmp_str) /* not a number */ |
| return "Illegal GID"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = groupname for setgid()", "setgid"); |
| break; |
| } |
| #endif |
| |
| #ifndef USE_WIN32 |
| /* setuid */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.uid=0; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "setuid")) |
| break; |
| pw=getpwnam(arg); |
| if(pw) { |
| new_global_options.uid=pw->pw_uid; |
| return NULL; /* OK */ |
| } |
| new_global_options.uid=(uid_t)strtol(arg, &tmp_str, 10); |
| if(tmp_str==arg || *tmp_str) /* not a number */ |
| return "Illegal UID"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = username for setuid()", "setuid"); |
| break; |
| } |
| #endif |
| |
| /* socket */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "socket")) |
| break; |
| if(parse_socket_option(arg)) |
| return "Illegal socket option"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = a|l|r:option=value[:value]", "socket"); |
| s_log(LOG_NOTICE, "%25sset an option on accept/local/remote socket", ""); |
| break; |
| } |
| |
| /* syslog */ |
| #ifndef USE_WIN32 |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.option.syslog=1; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "syslog")) |
| break; |
| if(!strcasecmp(arg, "yes")) |
| new_global_options.option.syslog=1; |
| else if(!strcasecmp(arg, "no")) |
| new_global_options.option.syslog=0; |
| else |
| return "The argument needs to be either 'yes' or 'no'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = yes|no send logging messages to syslog", |
| "syslog"); |
| break; |
| } |
| #endif |
| |
| /* taskbar */ |
| #ifdef USE_WIN32 |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_global_options.option.taskbar=1; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "taskbar")) |
| break; |
| if(!strcasecmp(arg, "yes")) |
| new_global_options.option.taskbar=1; |
| else if(!strcasecmp(arg, "no")) |
| new_global_options.option.taskbar=0; |
| else |
| return "The argument needs to be either 'yes' or 'no'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = yes", "taskbar"); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = yes|no enable the taskbar icon", "taskbar"); |
| break; |
| } |
| #endif |
| |
| if(cmd==CMD_EXEC) |
| return option_not_found; |
| |
| if(cmd==CMD_END) { |
| /* FIPS needs to be initialized as early as possible */ |
| if(ssl_configure(&new_global_options)) /* configure global SSL settings */ |
| return "Failed to initialize SSL"; |
| } |
| return NULL; /* OK */ |
| } |
| |
| /**************************************** service-level options */ |
| |
| NOEXPORT char *parse_service_option(CMD cmd, SERVICE_OPTIONS *section, |
| char *opt, char *arg) { |
| char *tmp_str; |
| int endpoints=0; |
| long tmp_long; |
| |
| if(cmd==CMD_DEFAULT || cmd==CMD_HELP) { |
| s_log(LOG_NOTICE, " "); |
| s_log(LOG_NOTICE, "Service-level options:"); |
| } |
| |
| /* accept */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->option.accept=0; |
| memset(§ion->local_addr, 0, sizeof(SOCKADDR_UNION)); |
| section->local_addr.in.sin_family=AF_INET; |
| section->fd=INVALID_SOCKET; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "accept")) |
| break; |
| section->option.accept=1; |
| if(!name2addr(§ion->local_addr, arg, 1)) |
| return "Failed to resolve accepting address"; |
| return NULL; /* OK */ |
| case CMD_END: |
| if(section->option.accept) |
| ++endpoints; |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = [host:]port accept connections on specified host:port", |
| "accept"); |
| break; |
| } |
| |
| /* CApath */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| #if 0 |
| section->ca_dir=(char *)X509_get_default_cert_dir(); |
| #endif |
| section->ca_dir=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "CApath")) |
| break; |
| if(arg[0]) /* not empty */ |
| section->ca_dir=str_dup(arg); |
| else |
| section->ca_dir=NULL; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| #if 0 |
| s_log(LOG_NOTICE, "%-22s = %s", "CApath", |
| section->ca_dir ? section->ca_dir : "(none)"); |
| #endif |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = CA certificate directory for 'verify' option", |
| "CApath"); |
| break; |
| } |
| |
| /* CAfile */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| #if 0 |
| section->ca_file=(char *)X509_get_default_certfile(); |
| #endif |
| section->ca_file=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "CAfile")) |
| break; |
| if(arg[0]) /* not empty */ |
| section->ca_file=str_dup(arg); |
| else |
| section->ca_file=NULL; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| #if 0 |
| s_log(LOG_NOTICE, "%-22s = %s", "CAfile", |
| section->ca_file ? section->ca_file : "(none)"); |
| #endif |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = CA certificate file for 'verify' option", |
| "CAfile"); |
| break; |
| } |
| |
| /* cert */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->cert=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "cert")) |
| break; |
| section->cert=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| #ifndef OPENSSL_NO_PSK |
| if(section->psk_keys) |
| break; |
| #endif /* !defined(OPENSSL_NO_PSK) */ |
| #ifndef OPENSSL_NO_ENGINE |
| if(section->engine) |
| break; |
| #endif /* !defined(OPENSSL_NO_ENGINE) */ |
| if(!section->option.client && !section->cert) |
| return "SSL server needs a certificate"; |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; /* no default certificate */ |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = certificate chain", "cert"); |
| break; |
| } |
| |
| #if OPENSSL_VERSION_NUMBER>=0x10002000L |
| |
| /* checkEmail */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->check_email=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "checkEmail")) |
| break; |
| name_list_append(§ion->check_email, arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = peer certificate email address", |
| "checkEmail"); |
| break; |
| } |
| |
| /* checkHost */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->check_host=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "checkHost")) |
| break; |
| name_list_append(§ion->check_host, arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = peer certificate host name pattern", |
| "checkHost"); |
| break; |
| } |
| |
| /* checkIP */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->check_ip=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "checkIP")) |
| break; |
| name_list_append(§ion->check_ip, arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = peer certificate IP address", |
| "checkIP"); |
| break; |
| } |
| |
| #endif /* OPENSSL_VERSION_NUMBER>=0x10002000L */ |
| |
| /* ciphers */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->cipher_list=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "ciphers")) |
| break; |
| section->cipher_list=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| #ifdef USE_FIPS |
| if(new_global_options.option.fips) { |
| if(!new_service_options.cipher_list) |
| new_service_options.cipher_list="FIPS"; |
| } else |
| #endif /* USE_FIPS */ |
| { |
| if(!new_service_options.cipher_list) |
| new_service_options.cipher_list=stunnel_cipher_list; |
| } |
| |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| #ifdef USE_FIPS |
| s_log(LOG_NOTICE, "%-22s = %s %s", "ciphers", |
| "FIPS", "(with \"fips = yes\")"); |
| s_log(LOG_NOTICE, "%-22s = %s %s", "ciphers", |
| stunnel_cipher_list, "(with \"fips = no\")"); |
| #else |
| s_log(LOG_NOTICE, "%-22s = %s", "ciphers", stunnel_cipher_list); |
| #endif /* USE_FIPS */ |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = list of permitted SSL ciphers", "ciphers"); |
| break; |
| } |
| |
| /* client */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->option.client=0; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "client")) |
| break; |
| if(!strcasecmp(arg, "yes")) |
| section->option.client=1; |
| else if(!strcasecmp(arg, "no")) |
| section->option.client=0; |
| else |
| return "The argument needs to be either 'yes' or 'no'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = yes|no client mode (remote service uses SSL)", |
| "client"); |
| break; |
| } |
| |
| /* connect */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| addrlist_clear(§ion->connect_addr, 0); |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "connect")) |
| break; |
| name_list_append(§ion->connect_addr.names, arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| if(section->connect_addr.names) { |
| if(!section->option.delayed_lookup && |
| !addrlist_resolve(§ion->connect_addr)) { |
| s_log(LOG_INFO, |
| "Cannot resolve connect target - delaying DNS lookup"); |
| section->redirect_addr.num=0; |
| str_free(section->redirect_addr.names); |
| section->redirect_addr.names=NULL; |
| section->option.delayed_lookup=1; |
| } |
| ++endpoints; |
| } |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = [host:]port to connect", |
| "connect"); |
| break; |
| } |
| |
| /* CRLpath */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->crl_dir=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "CRLpath")) |
| break; |
| if(arg[0]) /* not empty */ |
| section->crl_dir=str_dup(arg); |
| else |
| section->crl_dir=NULL; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = CRL directory", "CRLpath"); |
| break; |
| } |
| |
| /* CRLfile */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->crl_file=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "CRLfile")) |
| break; |
| if(arg[0]) /* not empty */ |
| section->crl_file=str_dup(arg); |
| else |
| section->crl_file=NULL; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = CRL file", "CRLfile"); |
| break; |
| } |
| |
| #ifndef OPENSSL_NO_ECDH |
| |
| /* curve */ |
| #define DEFAULT_CURVE NID_X9_62_prime256v1 |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->curve=DEFAULT_CURVE; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "curve")) |
| break; |
| section->curve=OBJ_txt2nid(arg); |
| if(section->curve==NID_undef) |
| return "Curve name not supported"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = %s", "curve", OBJ_nid2ln(DEFAULT_CURVE)); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = ECDH curve name", "curve"); |
| break; |
| } |
| |
| #endif /* !defined(OPENSSL_NO_ECDH) */ |
| |
| /* debug */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| new_service_options.log_level=LOG_NOTICE; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "debug")) |
| break; |
| return parse_debug_level(arg, section); |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = %s", "debug", "notice"); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = level (e.g. info)", "debug"); |
| break; |
| } |
| |
| /* delay */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->option.delayed_lookup=0; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "delay")) |
| break; |
| if(!strcasecmp(arg, "yes")) |
| section->option.delayed_lookup=1; |
| else if(!strcasecmp(arg, "no")) |
| section->option.delayed_lookup=0; |
| else |
| return "The argument needs to be either 'yes' or 'no'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, |
| "%-22s = yes|no delay DNS lookup for 'connect' option", |
| "delay"); |
| break; |
| } |
| |
| #ifndef OPENSSL_NO_ENGINE |
| |
| /* engineId */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "engineId")) |
| break; |
| section->engine=engine_get_by_id(arg); |
| if(!section->engine) |
| return "Engine ID not found"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = ID of engine to read the key from", |
| "engineId"); |
| break; |
| } |
| |
| /* engineNum */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "engineNum")) |
| break; |
| { |
| int tmp_int=(int)strtol(arg, &tmp_str, 10); |
| if(tmp_str==arg || *tmp_str) /* not a number */ |
| return "Illegal engine number"; |
| section->engine=engine_get_by_num(tmp_int-1); |
| } |
| if(!section->engine) |
| return "Illegal engine number"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = number of engine to read the key from", |
| "engineNum"); |
| break; |
| } |
| |
| #endif /* !defined(OPENSSL_NO_ENGINE) */ |
| |
| /* exec */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->exec_name=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "exec")) |
| break; |
| section->exec_name=str_dup(arg); |
| #ifdef USE_WIN32 |
| section->exec_args=str_dup(arg); |
| #else |
| if(!section->exec_args) { |
| section->exec_args=str_alloc(2*sizeof(char *)); |
| section->exec_args[0]=section->exec_name; |
| section->exec_args[1]=NULL; /* to show that it's null-terminated */ |
| } |
| #endif |
| return NULL; /* OK */ |
| case CMD_END: |
| if(section->exec_name) |
| ++endpoints; |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = file execute local inetd-type program", |
| "exec"); |
| break; |
| } |
| |
| /* execArgs */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->exec_args=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "execArgs")) |
| break; |
| #ifdef USE_WIN32 |
| section->exec_args=str_dup(arg); |
| #else |
| section->exec_args=argalloc(arg); |
| #endif |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = arguments for 'exec' (including $0)", |
| "execArgs"); |
| break; |
| } |
| |
| /* failover */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->failover=FAILOVER_RR; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "failover")) |
| break; |
| if(!strcasecmp(arg, "rr")) |
| section->failover=FAILOVER_RR; |
| else if(!strcasecmp(arg, "prio")) |
| section->failover=FAILOVER_PRIO; |
| else |
| return "The argument needs to be either 'rr' or 'prio'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = rr|prio failover strategy", |
| "failover"); |
| break; |
| } |
| |
| /* ident */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->username=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "ident")) |
| break; |
| section->username=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = username for IDENT (RFC 1413) checking", "ident"); |
| break; |
| } |
| |
| /* key */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->key=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "key")) |
| break; |
| section->key=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| if(section->cert && !section->key) |
| section->key=str_dup(section->cert); |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = certificate private key", "key"); |
| break; |
| } |
| |
| #ifdef USE_LIBWRAP |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->option.libwrap=0; /* disable libwrap by default */ |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "libwrap")) |
| break; |
| if(!strcasecmp(arg, "yes")) |
| section->option.libwrap=1; |
| else if(!strcasecmp(arg, "no")) |
| section->option.libwrap=0; |
| else |
| return "The argument needs to be either 'yes' or 'no'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = yes|no use /etc/hosts.allow and /etc/hosts.deny", |
| "libwrap"); |
| break; |
| } |
| #endif /* USE_LIBWRAP */ |
| |
| /* local */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->option.local=0; |
| memset(§ion->source_addr, 0, sizeof(SOCKADDR_UNION)); |
| section->source_addr.in.sin_family=AF_INET; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "local")) |
| break; |
| section->option.local=1; |
| if(!hostport2addr(§ion->source_addr, arg, "0", 1)) |
| return "Failed to resolve local address"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = IP address to be used as source for remote" |
| " connections", "local"); |
| break; |
| } |
| |
| /* logId */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->log_id=LOG_ID_SEQENTIAL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "logId")) |
| break; |
| if(!strcasecmp(arg, "sequential")) |
| section->log_id=LOG_ID_SEQENTIAL; |
| else if(!strcasecmp(arg, "unique")) |
| section->log_id=LOG_ID_UNIQUE; |
| else if(!strcasecmp(arg, "thread")) |
| section->log_id=LOG_ID_THREAD; |
| else |
| return "Invalid connection identifier type"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = %s", "logId", "sequential"); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = connection identifier type", |
| "logId"); |
| break; |
| } |
| |
| #ifndef OPENSSL_NO_OCSP |
| |
| /* OCSP */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->ocsp_url=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "ocsp")) |
| break; |
| section->ocsp_url=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = OCSP server URL", "ocsp"); |
| break; |
| } |
| |
| /* OCSPaia */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->option.aia=0; /* disable AIA by default */ |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "OCSPaia")) |
| break; |
| if(!strcasecmp(arg, "yes")) |
| section->option.aia=1; |
| else if(!strcasecmp(arg, "no")) |
| section->option.aia=0; |
| else |
| return "The argument needs to be either 'yes' or 'no'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = yes|no check the AIA responders from certificates", |
| "OCSPaia"); |
| break; |
| } |
| |
| /* OCSPflag */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->ocsp_flags=0; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "OCSPflag")) |
| break; |
| { |
| unsigned long tmp_ulong=parse_ocsp_flag(arg); |
| if(!tmp_ulong) |
| return "Illegal OCSP flag"; |
| section->ocsp_flags|=tmp_ulong; |
| } |
| return NULL; |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = OCSP server flags", "OCSPflag"); |
| break; |
| } |
| |
| #endif /* !defined(OPENSSL_NO_OCSP) */ |
| |
| /* options */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->ssl_options_set|=SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3; |
| #if OPENSSL_VERSION_NUMBER>=0x009080dfL |
| section->ssl_options_clear=0; |
| #endif /* OpenSSL 0.9.8m or later */ |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "options")) |
| break; |
| #if OPENSSL_VERSION_NUMBER>=0x009080dfL |
| if(*arg=='-') { |
| tmp_long=parse_ssl_option(arg+1); |
| if(!tmp_long) |
| return "Illegal SSL option"; |
| section->ssl_options_clear|=tmp_long; |
| return NULL; /* OK */ |
| } |
| #endif /* OpenSSL 0.9.8m or later */ |
| tmp_long=parse_ssl_option(arg); |
| if(!tmp_long) |
| return "Illegal SSL option"; |
| section->ssl_options_set|=tmp_long; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = %s", "options", "NO_SSLv2"); |
| s_log(LOG_NOTICE, "%-22s = %s", "options", "NO_SSLv3"); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = SSL option", "options"); |
| s_log(LOG_NOTICE, "%25sset an SSL option", ""); |
| break; |
| } |
| |
| /* protocol */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->protocol=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "protocol")) |
| break; |
| section->protocol=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| /* this also initializes section->option.connect_before_ssl */ |
| tmp_str=protocol(NULL, section, PROTOCOL_CHECK); |
| if(tmp_str) |
| return tmp_str; |
| if(section->protocol && !strcasecmp(section->protocol, "socks")) { |
| ++endpoints; |
| } |
| #ifdef SSL_OP_NO_TICKET |
| /* disable RFC4507 support introduced in OpenSSL 0.9.8f */ |
| /* session tickets do not support SSL_SESSION_*_ex_data() */ |
| if(!section->option.connect_before_ssl) /* address cache can be used */ |
| section->ssl_options_set|=SSL_OP_NO_TICKET; |
| #endif |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = protocol to negotiate before SSL initialization", |
| "protocol"); |
| s_log(LOG_NOTICE, "%25scurrently supported: cifs, connect, imap,", ""); |
| s_log(LOG_NOTICE, "%25s nntp, pgsql, pop3, proxy, smtp", ""); |
| break; |
| } |
| |
| /* protocolAuthentication */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->protocol_authentication="basic"; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "protocolAuthentication")) |
| break; |
| section->protocol_authentication=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = authentication type for protocol negotiations", |
| "protocolAuthentication"); |
| break; |
| } |
| |
| /* protocolHost */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->protocol_host=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "protocolHost")) |
| break; |
| section->protocol_host=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = host:port for protocol negotiations", |
| "protocolHost"); |
| break; |
| } |
| |
| /* protocolPassword */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->protocol_password=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "protocolPassword")) |
| break; |
| section->protocol_password=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = password for protocol negotiations", |
| "protocolPassword"); |
| break; |
| } |
| |
| /* protocolUsername */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->protocol_username=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "protocolUsername")) |
| break; |
| section->protocol_username=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = username for protocol negotiations", |
| "protocolUsername"); |
| break; |
| } |
| |
| #ifndef OPENSSL_NO_PSK |
| |
| /* PSKidentity */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->psk_identity=NULL; |
| section->psk_selected=NULL; |
| section->psk_sorted.val=NULL; |
| section->psk_sorted.num=0; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "PSKidentity")) |
| break; |
| section->psk_identity=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| if(!section->psk_keys) /* PSK not configured */ |
| break; |
| psk_sort(§ion->psk_sorted, section->psk_keys); |
| if(section->option.client) { |
| if(section->psk_identity) { |
| section->psk_selected= |
| psk_find(§ion->psk_sorted, section->psk_identity); |
| if(!section->psk_selected) |
| return "No key found for the specified PSK identity"; |
| } else { /* take the first specified identity as default */ |
| section->psk_selected=section->psk_keys; |
| } |
| } else { |
| if(section->psk_identity) |
| s_log(LOG_NOTICE, |
| "PSK identity is ignored in the server mode"); |
| } |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = identity for PSK authentication", |
| "PSKidentity"); |
| break; |
| } |
| |
| /* PSKsecrets */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->psk_keys=NULL; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "PSKsecrets")) |
| break; |
| section->psk_keys=psk_read(arg); |
| if(!section->psk_keys) |
| return "Failed to read PSK secrets"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| psk_free(section->psk_keys); |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = secrets for PSK authentication", |
| "PSKsecrets"); |
| break; |
| } |
| |
| #endif /* !defined(OPENSSL_NO_PSK) */ |
| |
| /* pty */ |
| #ifndef USE_WIN32 |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->option.pty=0; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "pty")) |
| break; |
| if(!strcasecmp(arg, "yes")) |
| section->option.pty=1; |
| else if(!strcasecmp(arg, "no")) |
| section->option.pty=0; |
| else |
| return "The argument needs to be either 'yes' or 'no'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = yes|no allocate pseudo terminal for 'exec' option", |
| "pty"); |
| break; |
| } |
| #endif |
| |
| /* redirect */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| addrlist_clear(§ion->redirect_addr, 0); |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "redirect")) |
| break; |
| #ifdef SSL_OP_NO_TICKET |
| /* disable RFC4507 support introduced in OpenSSL 0.9.8f */ |
| /* session tickets do not support SSL_SESSION_*_ex_data() */ |
| section->ssl_options_set|=SSL_OP_NO_TICKET; |
| #endif |
| name_list_append(§ion->redirect_addr.names, arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| if(section->redirect_addr.names) { |
| if(!section->option.delayed_lookup && |
| !addrlist_resolve(§ion->redirect_addr)) { |
| s_log(LOG_INFO, |
| "Cannot resolve redirect target - delaying DNS lookup"); |
| section->connect_addr.num=0; |
| str_free(section->connect_addr.names); |
| section->connect_addr.names=NULL; |
| section->option.delayed_lookup=1; |
| } |
| if(section->verify_level<1) |
| return "\"verify\" needs to be 1 or higher for \"redirect\" to work"; |
| } |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, |
| "%-22s = [host:]port to redirect on authentication failures", |
| "redirect"); |
| break; |
| } |
| |
| /* renegotiation */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->option.renegotiation=1; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "renegotiation")) |
| break; |
| if(!strcasecmp(arg, "yes")) |
| section->option.renegotiation=1; |
| else if(!strcasecmp(arg, "no")) |
| section->option.renegotiation=0; |
| else |
| return "The argument needs to be either 'yes' or 'no'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = yes|no support renegotiation", |
| "renegotiation"); |
| break; |
| } |
| |
| /* reset */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->option.reset=1; /* enabled by default */ |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "reset")) |
| break; |
| if(!strcasecmp(arg, "yes")) |
| section->option.reset=1; |
| else if(!strcasecmp(arg, "no")) |
| section->option.reset=0; |
| else |
| return "The argument needs to be either 'yes' or 'no'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = yes|no send TCP RST on error", |
| "retry"); |
| break; |
| } |
| |
| /* retry */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->option.retry=0; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "retry")) |
| break; |
| if(!strcasecmp(arg, "yes")) |
| section->option.retry=1; |
| else if(!strcasecmp(arg, "no")) |
| section->option.retry=0; |
| else |
| return "The argument needs to be either 'yes' or 'no'"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = yes|no retry connect+exec section", |
| "retry"); |
| break; |
| } |
| |
| /* sessionCacheSize */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->session_size=1000L; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "sessionCacheSize")) |
| break; |
| section->session_size=strtol(arg, &tmp_str, 10); |
| if(tmp_str==arg || *tmp_str) /* not a number */ |
| return "Illegal session cache size"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = %ld", "sessionCacheSize", 1000L); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = session cache size", "sessionCacheSize"); |
| break; |
| } |
| |
| /* sessionCacheTimeout */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->session_timeout=300L; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "sessionCacheTimeout") && strcasecmp(opt, "session")) |
| break; |
| section->session_timeout=strtol(arg, &tmp_str, 10); |
| if(tmp_str==arg || *tmp_str) /* not a number */ |
| return "Illegal session cache timeout"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = %ld seconds", "sessionCacheTimeout", 300L); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = session cache timeout (in seconds)", |
| "sessionCacheTimeout"); |
| break; |
| } |
| |
| /* sessiond */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->option.sessiond=0; |
| memset(§ion->sessiond_addr, 0, sizeof(SOCKADDR_UNION)); |
| section->sessiond_addr.in.sin_family=AF_INET; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "sessiond")) |
| break; |
| section->option.sessiond=1; |
| #ifdef SSL_OP_NO_TICKET |
| /* disable RFC4507 support introduced in OpenSSL 0.9.8f */ |
| /* this prevents session callbacks from beeing executed */ |
| section->ssl_options_set|=SSL_OP_NO_TICKET; |
| #endif |
| if(!name2addr(§ion->sessiond_addr, arg, 0)) |
| return "Failed to resolve sessiond server address"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = [host:]port use sessiond at host:port", |
| "sessiond"); |
| break; |
| } |
| |
| #ifndef OPENSSL_NO_TLSEXT |
| /* sni */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->servername_list_head=NULL; |
| section->servername_list_tail=NULL; |
| section->option.sni=0; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "sni")) |
| break; |
| section->sni=str_dup(arg); |
| return NULL; /* OK */ |
| case CMD_END: |
| tmp_str=sni_init(section); |
| if(tmp_str) |
| return tmp_str; |
| if(section->option.sni) |
| ++endpoints; |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = master_service:host_name for an SNI virtual service", |
| "sni"); |
| break; |
| } |
| #endif /* !defined(OPENSSL_NO_TLSEXT) */ |
| |
| /* sslVersion */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| #if OPENSSL_VERSION_NUMBER>=0x10100000L |
| section->client_method=(SSL_METHOD *)TLS_client_method(); |
| section->server_method=(SSL_METHOD *)TLS_server_method(); |
| #else |
| section->client_method=(SSL_METHOD *)SSLv23_client_method(); |
| section->server_method=(SSL_METHOD *)SSLv23_server_method(); |
| #endif |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "sslVersion")) |
| break; |
| if(!strcasecmp(arg, "all")) { |
| #if OPENSSL_VERSION_NUMBER>=0x10100000L |
| section->client_method=(SSL_METHOD *)TLS_client_method(); |
| section->server_method=(SSL_METHOD *)TLS_server_method(); |
| #else |
| section->client_method=(SSL_METHOD *)SSLv23_client_method(); |
| section->server_method=(SSL_METHOD *)SSLv23_server_method(); |
| #endif |
| } else if(!strcasecmp(arg, "SSLv2")) { |
| #ifndef OPENSSL_NO_SSL2 |
| section->client_method=(SSL_METHOD *)SSLv2_client_method(); |
| section->server_method=(SSL_METHOD *)SSLv2_server_method(); |
| #else /* defined(OPENSSL_NO_SSL2) */ |
| return "SSLv2 not supported"; |
| #endif /* !defined(OPENSSL_NO_SSL2) */ |
| } else if(!strcasecmp(arg, "SSLv3")) { |
| #ifndef OPENSSL_NO_SSL3 |
| section->client_method=(SSL_METHOD *)SSLv3_client_method(); |
| section->server_method=(SSL_METHOD *)SSLv3_server_method(); |
| #else /* defined(OPENSSL_NO_SSL3) */ |
| return "SSLv3 not supported"; |
| #endif /* !defined(OPENSSL_NO_SSL3) */ |
| } else if(!strcasecmp(arg, "TLSv1")) { |
| #ifndef OPENSSL_NO_TLS1 |
| section->client_method=(SSL_METHOD *)TLSv1_client_method(); |
| section->server_method=(SSL_METHOD *)TLSv1_server_method(); |
| #else /* defined(OPENSSL_NO_TLS1) */ |
| return "TLSv1 not supported"; |
| #endif /* !defined(OPENSSL_NO_TLS1) */ |
| } else if(!strcasecmp(arg, "TLSv1.1")) { |
| #ifndef OPENSSL_NO_TLS1_1 |
| section->client_method=(SSL_METHOD *)TLSv1_1_client_method(); |
| section->server_method=(SSL_METHOD *)TLSv1_1_server_method(); |
| #else /* defined(OPENSSL_NO_TLS1_1) */ |
| return "TLSv1.1 not supported"; |
| #endif /* !defined(OPENSSL_NO_TLS1_1) */ |
| } else if(!strcasecmp(arg, "TLSv1.2")) { |
| #ifndef OPENSSL_NO_TLS1_2 |
| section->client_method=(SSL_METHOD *)TLSv1_2_client_method(); |
| section->server_method=(SSL_METHOD *)TLSv1_2_server_method(); |
| #else /* defined(OPENSSL_NO_TLS1_2) */ |
| return "TLSv1.2 not supported"; |
| #endif /* !defined(OPENSSL_NO_TLS1_2) */ |
| } else |
| return "Incorrect version of SSL protocol"; |
| return NULL; /* OK */ |
| case CMD_END: |
| #ifdef USE_FIPS |
| if(new_global_options.option.fips) { |
| #ifndef OPENSSL_NO_SSL2 |
| if(section->option.client ? |
| section->client_method==(SSL_METHOD *)SSLv2_client_method() : |
| section->server_method==(SSL_METHOD *)SSLv2_server_method()) |
| return "\"sslVersion = SSLv2\" not supported in FIPS mode"; |
| #endif /* !defined(OPENSSL_NO_SSL2) */ |
| #ifndef OPENSSL_NO_SSL3 |
| if(section->option.client ? |
| section->client_method==(SSL_METHOD *)SSLv3_client_method() : |
| section->server_method==(SSL_METHOD *)SSLv3_server_method()) |
| return "\"sslVersion = SSLv3\" not supported in FIPS mode"; |
| #endif /* !defined(OPENSSL_NO_SSL3) */ |
| } |
| #endif /* USE_FIPS */ |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = all|SSLv2|SSLv3|TLSv1" |
| #if OPENSSL_VERSION_NUMBER>=0x10001000L |
| "|TLSv1.1|TLSv1.2" |
| #endif |
| " SSL method", "sslVersion"); |
| break; |
| } |
| |
| #ifndef USE_FORK |
| /* stack */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->stack_size=DEFAULT_STACK_SIZE; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "stack")) |
| break; |
| section->stack_size=(size_t)strtol(arg, &tmp_str, 10); |
| if(tmp_str==arg || *tmp_str) /* not a number */ |
| return "Illegal thread stack size"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = %d bytes", "stack", DEFAULT_STACK_SIZE); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = thread stack size (in bytes)", "stack"); |
| break; |
| } |
| #endif |
| |
| /* TIMEOUTbusy */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->timeout_busy=300; /* 5 minutes */ |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "TIMEOUTbusy")) |
| break; |
| section->timeout_busy=(int)strtol(arg, &tmp_str, 10); |
| if(tmp_str==arg || *tmp_str) /* not a number */ |
| return "Illegal busy timeout"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = %d seconds", "TIMEOUTbusy", 300); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = seconds to wait for expected data", "TIMEOUTbusy"); |
| break; |
| } |
| |
| /* TIMEOUTclose */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->timeout_close=60; /* 1 minute */ |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "TIMEOUTclose")) |
| break; |
| section->timeout_close=(int)strtol(arg, &tmp_str, 10); |
| if(tmp_str==arg || *tmp_str) /* not a number */ |
| return "Illegal close timeout"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = %d seconds", "TIMEOUTclose", 60); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = seconds to wait for close_notify", |
| "TIMEOUTclose"); |
| break; |
| } |
| |
| /* TIMEOUTconnect */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->timeout_connect=10; /* 10 seconds */ |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "TIMEOUTconnect")) |
| break; |
| section->timeout_connect=(int)strtol(arg, &tmp_str, 10); |
| if(tmp_str==arg || *tmp_str) /* not a number */ |
| return "Illegal connect timeout"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = %d seconds", "TIMEOUTconnect", 10); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = seconds to connect remote host", "TIMEOUTconnect"); |
| break; |
| } |
| |
| /* TIMEOUTidle */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->timeout_idle=43200; /* 12 hours */ |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "TIMEOUTidle")) |
| break; |
| section->timeout_idle=(int)strtol(arg, &tmp_str, 10); |
| if(tmp_str==arg || *tmp_str) /* not a number */ |
| return "Illegal idle timeout"; |
| return NULL; /* OK */ |
| case CMD_END: |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = %d seconds", "TIMEOUTidle", 43200); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, "%-22s = seconds to keep an idle connection", "TIMEOUTidle"); |
| break; |
| } |
| |
| /* transparent */ |
| #ifndef USE_WIN32 |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->option.transparent_src=0; |
| section->option.transparent_dst=0; |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "transparent")) |
| break; |
| if(!strcasecmp(arg, "none") || !strcasecmp(arg, "no")) { |
| section->option.transparent_src=0; |
| section->option.transparent_dst=0; |
| } else if(!strcasecmp(arg, "source") || !strcasecmp(arg, "yes")) { |
| section->option.transparent_src=1; |
| section->option.transparent_dst=0; |
| #ifdef SO_ORIGINAL_DST |
| } else if(!strcasecmp(arg, "destination")) { |
| section->option.transparent_src=0; |
| section->option.transparent_dst=1; |
| } else if(!strcasecmp(arg, "both")) { |
| section->option.transparent_src=1; |
| section->option.transparent_dst=1; |
| #endif |
| } else |
| return "Selected transparent proxy mode is not available"; |
| return NULL; /* OK */ |
| case CMD_END: |
| if(section->option.transparent_dst) |
| ++endpoints; |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, |
| "%-22s = none|source|destination|both transparent proxy mode", |
| "transparent"); |
| break; |
| } |
| #endif |
| |
| /* verify */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| section->verify_level=-1; /* do not even request a certificate */ |
| break; |
| case CMD_EXEC: |
| if(strcasecmp(opt, "verify")) |
| break; |
| section->verify_level=(int)strtol(arg, &tmp_str, 10); |
| if(tmp_str==arg || *tmp_str) /* not a number */ |
| return "Bad verify level"; |
| if(section->verify_level<0 || section->verify_level>4) |
| return "Bad verify level"; |
| return NULL; /* OK */ |
| case CMD_END: |
| if(section->verify_level>0 && !section->ca_file && !section->ca_dir) |
| return "Either \"CAfile\" or \"CApath\" has to be configured"; |
| break; |
| case CMD_FREE: |
| break; |
| case CMD_DEFAULT: |
| s_log(LOG_NOTICE, "%-22s = none", "verify"); |
| break; |
| case CMD_HELP: |
| s_log(LOG_NOTICE, |
| "%-22s = level of peer certificate verification", "verify"); |
| s_log(LOG_NOTICE, |
| "%25slevel 0 - request and ignore peer cert", ""); |
| s_log(LOG_NOTICE, |
| "%25slevel 1 - only validate peer cert if present", ""); |
| s_log(LOG_NOTICE, |
| "%25slevel 2 - always require a valid peer cert", ""); |
| s_log(LOG_NOTICE, |
| "%25slevel 3 - verify peer with locally installed cert", ""); |
| s_log(LOG_NOTICE, |
| "%25slevel 4 - ignore CA chain and only verify peer cert", ""); |
| break; |
| } |
| |
| /* final checks */ |
| switch(cmd) { |
| case CMD_BEGIN: |
| break; |
| case CMD_EXEC: |
| return option_not_found; |
| case CMD_END: |
| if(new_service_options.next) { /* daemon mode checks */ |
| if(endpoints!=2) |
| return "Each service must define two endpoints"; |
| } else { /* inetd mode checks */ |
| if(section->option.accept) |
| return "'accept' option is only allowed in a [section]"; |
| /* no need to check for section->option.sni in inetd mode, |
| as it requires valid sections to be set */ |
| if(endpoints!=1) |
| return "Inetd mode must define one endpoint"; |
| } |
| if(context_init(section)) /* initialize SSL context */ |
| return "Failed to initialize SSL context"; |
| case CMD_FREE: |
| case CMD_DEFAULT: |
| case CMD_HELP: |
| break; |
| } |
| |
| return NULL; /* OK */ |
| } |
| |
| /**************************************** validate and initialize configuration */ |
| |
| #ifndef OPENSSL_NO_TLSEXT |
| NOEXPORT char *sni_init(SERVICE_OPTIONS *section) { |
| char *tmp_str; |
| SERVICE_OPTIONS *tmpsrv; |
| |
| /* server mode: update servername_list based on the SNI option */ |
| if(!section->option.client && section->sni) { |
| tmp_str=strchr(section->sni, ':'); |
| if(!tmp_str) |
| return "Invalid SNI parameter format"; |
| *tmp_str++='\0'; |
| for(tmpsrv=new_service_options.next; tmpsrv; tmpsrv=tmpsrv->next) |
| if(!strcmp(tmpsrv->servname, section->sni)) |
| break; |
| if(!tmpsrv) |
| return "SNI section name not found"; |
| if(tmpsrv->option.client) |
| return "SNI master service is a TLS client"; |
| if(tmpsrv->servername_list_tail) { |
| tmpsrv->servername_list_tail->next=str_alloc(sizeof(SERVERNAME_LIST)); |
| tmpsrv->servername_list_tail=tmpsrv->servername_list_tail->next; |
| } else { /* first virtual service */ |
| tmpsrv->servername_list_head= |
| tmpsrv->servername_list_tail= |
| str_alloc(sizeof(SERVERNAME_LIST)); |
| tmpsrv->ssl_options_set|= |
| SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; |
| } |
| tmpsrv->servername_list_tail->servername=str_dup(tmp_str); |
| tmpsrv->servername_list_tail->opt=section; |
| tmpsrv->servername_list_tail->next=NULL; |
| section->option.sni=1; |
| /* always negotiate a new session on renegotiation, as the SSL |
| * context settings (including access control) may be different */ |
| section->ssl_options_set|= |
| SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; |
| } |
| |
| /* client mode: setup SNI default based on 'protocolHost' and 'connect' options */ |
| if(section->option.client && !section->sni) { |
| /* setup host_name for SNI, prefer SNI and protocolHost if specified */ |
| if(section->protocol_host) /* 'protocolHost' option */ |
| section->sni=str_dup(section->protocol_host); |
| else if(section->connect_addr.names) /* 'connect' option */ |
| section->sni=str_dup(section->connect_addr.names->name); /* first hostname */ |
| if(section->sni) { /* either 'protocolHost' or 'connect' specified */ |
| tmp_str=strrchr(section->sni, ':'); |
| if(tmp_str) { /* 'host:port' -> drop ':port' */ |
| *tmp_str='\0'; |
| } else { /* 'port' -> default to 'localhost' */ |
| str_free(section->sni); |
| section->sni=str_dup("localhost"); |
| } |
| } |
| } |
| return NULL; |
| } |
| #endif /* !defined(OPENSSL_NO_TLSEXT) */ |
| |
| /**************************************** facility/debug level */ |
| |
| typedef struct { |
| char *name; |
| int value; |
| } facilitylevel; |
| |
| NOEXPORT char *parse_debug_level(char *arg, SERVICE_OPTIONS *section) { |
| char *arg_copy; |
| char *string; |
| facilitylevel *fl; |
| |
| /* facilities only make sense on unix */ |
| #if !defined (USE_WIN32) && !defined (__vms) |
| facilitylevel facilities[] = { |
| {"auth", LOG_AUTH}, {"cron", LOG_CRON}, {"daemon", LOG_DAEMON}, |
| {"kern", LOG_KERN}, {"lpr", LOG_LPR}, {"mail", LOG_MAIL}, |
| {"news", LOG_NEWS}, {"syslog", LOG_SYSLOG}, {"user", LOG_USER}, |
| {"uucp", LOG_UUCP}, {"local0", LOG_LOCAL0}, {"local1", LOG_LOCAL1}, |
| {"local2", LOG_LOCAL2}, {"local3", LOG_LOCAL3}, {"local4", LOG_LOCAL4}, |
| {"local5", LOG_LOCAL5}, {"local6", LOG_LOCAL6}, {"local7", LOG_LOCAL7}, |
| |
| /* some facilities are not defined on all Unices */ |
| #ifdef LOG_AUTHPRIV |
| {"authpriv", LOG_AUTHPRIV}, |
| #endif |
| #ifdef LOG_FTP |
| {"ftp", LOG_FTP}, |
| #endif |
| #ifdef LOG_NTP |
| {"ntp", LOG_NTP}, |
| #endif |
| {NULL, 0} |
| }; |
| #endif /* USE_WIN32, __vms */ |
| |
| facilitylevel levels[] = { |
| {"emerg", LOG_EMERG}, {"alert", LOG_ALERT}, |
| {"crit", LOG_CRIT}, {"err", LOG_ERR}, |
| {"warning", LOG_WARNING}, {"notice", LOG_NOTICE}, |
| {"info", LOG_INFO}, {"debug", LOG_DEBUG}, |
| {NULL, -1} |
| }; |
| |
| arg_copy=str_dup(arg); |
| string=arg_copy; |
| |
| /* facilities only make sense on Unix */ |
| #if !defined (USE_WIN32) && !defined (__vms) |
| if(section==&new_service_options && strchr(string, '.')) { |
| /* a facility was specified in the global options */ |
| new_global_options.log_facility=-1; |
| string=strtok(arg_copy, "."); /* break it up */ |
| |
| for(fl=facilities; fl->name; ++fl) { |
| if(!strcasecmp(fl->name, string)) { |
| new_global_options.log_facility=fl->value; |
| break; |
| } |
| } |
| if(new_global_options.log_facility==-1) |
| return "Illegal syslog facility"; |
| string=strtok(NULL, "."); /* set to the remainder */ |
| } |
| #endif /* USE_WIN32, __vms */ |
| |
| /* time to check the syslog level */ |
| if(string && strlen(string)==1 && *string>='0' && *string<='7') { |
| section->log_level=*string-'0'; |
| return NULL; /* OK */ |
| } |
| section->log_level=8; /* illegal level */ |
| for(fl=levels; fl->name; ++fl) { |
| if(!strcasecmp(fl->name, string)) { |
| section->log_level=fl->value; |
| break; |
| } |
| } |
| if(section->log_level==8) |
| return "Illegal debug level"; /* FAILED */ |
| return NULL; /* OK */ |
| } |
| |
| /**************************************** SSL options */ |
| |
| NOEXPORT long parse_ssl_option(char *arg) { |
| SSL_OPTION *option; |
| |
| for(option=(SSL_OPTION *)ssl_opts; option->name; ++option) |
| if(!strcasecmp(option->name, arg)) |
| return option->value; |
| return 0; /* FAILED */ |
| } |
| |
| NOEXPORT void print_ssl_options(void) { |
| SSL_OPTION *option; |
| |
| s_log(LOG_NOTICE, " "); |
| s_log(LOG_NOTICE, "Supported SSL options:"); |
| for(option=(SSL_OPTION *)ssl_opts; option->name; ++option) |
| s_log(LOG_NOTICE, "options = %s", option->name); |
| } |
| |
| /**************************************** read PSK file */ |
| |
| #ifndef OPENSSL_NO_PSK |
| |
| NOEXPORT PSK_KEYS *psk_read(char *key_file) { |
| DISK_FILE *df; |
| char line[CONFLINELEN], *key_val; |
| size_t key_len; |
| PSK_KEYS *head=NULL, *tail=NULL, *curr; |
| int line_number=0; |
| |
| if(file_permissions(key_file)) |
| return NULL; |
| df=file_open(key_file, FILE_MODE_READ); |
| if(!df) { |
| s_log(LOG_ERR, "Cannot open PSKsecrets file"); |
| return NULL; |
| } |
| while(file_getline(df, line, CONFLINELEN)>=0) { |
| ++line_number; |
| if(!line[0]) /* empty line */ |
| continue; |
| key_val=strchr(line, ':'); |
| if(!key_val) { |
| s_log(LOG_ERR, |
| "PSKsecrets line %d: Not in identity:key format", |
| line_number); |
| file_close(df); |
| psk_free(head); |
| return NULL; |
| } |
| *key_val++='\0'; |
| key_len=strlen(key_val); |
| if(strlen(line)+1>PSK_MAX_IDENTITY_LEN) { /* with the trailing '\0' */ |
| s_log(LOG_ERR, |
| "PSKsecrets line %d: Identity longer than %d characters", |
| line_number, PSK_MAX_IDENTITY_LEN); |
| file_close(df); |
| psk_free(head); |
| return NULL; |
| } |
| if(key_len>PSK_MAX_PSK_LEN) { |
| s_log(LOG_ERR, |
| "PSKsecrets line %d: Key longer than %d characters", |
| line_number, PSK_MAX_PSK_LEN); |
| file_close(df); |
| psk_free(head); |
| return NULL; |
| } |
| if(key_len<20) { |
| /* shorter keys are unlikely to have sufficient entropy */ |
| s_log(LOG_ERR, |
| "PSKsecrets line %d: Key shorter than 20 characters", |
| line_number); |
| file_close(df); |
| psk_free(head); |
| return NULL; |
| } |
| curr=str_alloc(sizeof(PSK_KEYS)); |
| curr->identity=str_dup(line); |
| curr->key_val=(unsigned char *)str_dup(key_val); |
| curr->key_len=key_len; |
| curr->next=NULL; |
| if(head) |
| tail->next=curr; |
| else |
| head=curr; |
| tail=curr; |
| } |
| file_close(df); |
| return head; |
| } |
| |
| NOEXPORT void psk_free(PSK_KEYS *head) { |
| PSK_KEYS *next; |
| |
| while(head) { |
| next=head->next; |
| str_free(head->identity); |
| str_free(head->key_val); |
| str_free(head); |
| head=next; |
| } |
| } |
| |
| #endif |
| |
| /**************************************** socket options */ |
| |
| static int on=1; |
| #define DEF_ON ((void *)&on) |
| |
| SOCK_OPT sock_opts[] = { |
| {"SO_DEBUG", SOL_SOCKET, SO_DEBUG, TYPE_FLAG, {NULL, NULL, NULL}}, |
| {"SO_DONTROUTE", SOL_SOCKET, SO_DONTROUTE, TYPE_FLAG, {NULL, NULL, NULL}}, |
| {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, TYPE_FLAG, {NULL, NULL, NULL}}, |
| {"SO_LINGER", SOL_SOCKET, SO_LINGER, TYPE_LINGER, {NULL, NULL, NULL}}, |
| {"SO_OOBINLINE", SOL_SOCKET, SO_OOBINLINE, TYPE_FLAG, {NULL, NULL, NULL}}, |
| {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, TYPE_INT, {NULL, NULL, NULL}}, |
| {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, TYPE_INT, {NULL, NULL, NULL}}, |
| #ifdef SO_RCVLOWAT |
| {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, TYPE_INT, {NULL, NULL, NULL}}, |
| #endif |
| #ifdef SO_SNDLOWAT |
| {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, TYPE_INT, {NULL, NULL, NULL}}, |
| #endif |
| #ifdef SO_RCVTIMEO |
| {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, TYPE_TIMEVAL, {NULL, NULL, NULL}}, |
| #endif |
| #ifdef SO_SNDTIMEO |
| {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, TYPE_TIMEVAL, {NULL, NULL, NULL}}, |
| #endif |
| {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, TYPE_FLAG, {DEF_ON, NULL, NULL}}, |