int main(int argc, char **argv) { int port = 0; int messagenumber = 5; char local_addr[256]; int c; int mclient = 1; char peer_address[129] = "\0"; int peer_port = PEER_DEFAULT_PORT; char rest_api_separator = ':'; int use_null_cipher=0; set_logfile("stdout"); set_execdir(); set_system_parameters(0); ns_bzero(local_addr, sizeof(local_addr)); while ((c = getopt(argc, argv, "a:d:p:l:n:L:m:e:r:u:w:i:k:z:W:C:E:F:o:bZvsyhcxXgtTSAPDNOUMRIGBJ")) != -1) { switch (c){ case 'J': { oauth = 1; oauth_key_data okd_array[3]; convert_oauth_key_data_raw(&okdr_array[0], &okd_array[0]); convert_oauth_key_data_raw(&okdr_array[1], &okd_array[1]); convert_oauth_key_data_raw(&okdr_array[2], &okd_array[2]); char err_msg[1025] = "\0"; size_t err_msg_size = sizeof(err_msg) - 1; if (convert_oauth_key_data(&okd_array[0], &okey_array[0], err_msg, err_msg_size) < 0) { fprintf(stderr, "%s\n", err_msg); exit(-1); } if (convert_oauth_key_data(&okd_array[1], &okey_array[1], err_msg, err_msg_size) < 0) { fprintf(stderr, "%s\n", err_msg); exit(-1); } if (convert_oauth_key_data(&okd_array[2], &okey_array[2], err_msg, err_msg_size) < 0) { fprintf(stderr, "%s\n", err_msg); exit(-1); } } break; case 'a': bps = (band_limit_t)strtoul(optarg,NULL,10); break; case 'o': STRCPY(origin,optarg); break; case 'B': random_disconnect = 1; break; case 'G': extra_requests = 1; break; case 'F': STRCPY(cipher_suite,optarg); break; case 'I': no_permissions = 1; break; case 'M': mobility = 1; break; case 'E': { char* fn = find_config_file(optarg,1); if(!fn) { fprintf(stderr,"ERROR: file %s not found\n",optarg); exit(-1); } STRCPY(ca_cert_file,fn); } break; case 'O': dos = 1; break; case 'C': rest_api_separator=*optarg; break; case 'D': mandatory_channel_padding = 1; break; case 'N': negative_test = 1; break; case 'R': negative_protocol_test = 1; break; case 'z': RTP_PACKET_INTERVAL = atoi(optarg); break; case 'Z': dual_allocation = 1; break; case 'u': STRCPY(g_uname, optarg); break; case 'w': STRCPY(g_upwd, optarg); break; case 'g': dont_fragment = 1; break; case 'd': STRCPY(client_ifname, optarg); break; case 'x': default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; break; case 'X': default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4; break; case 'l': clmessage_length = atoi(optarg); break; case 's': do_not_use_channel = 1; break; case 'n': messagenumber = atoi(optarg); break; case 'p': port = atoi(optarg); break; case 'L': STRCPY(local_addr, optarg); break; case 'e': STRCPY(peer_address, optarg); break; case 'r': peer_port = atoi(optarg); break; case 'v': clnet_verbose = TURN_VERBOSE_NORMAL; break; case 'h': hang_on = 1; break; case 'c': no_rtcp = 1; break; case 'm': mclient = atoi(optarg); break; case 'y': c2c = 1; break; case 't': use_tcp = 1; break; case 'b': use_sctp = 1; use_tcp = 1; break; case 'P': passive_tcp = 1; /* implies 'T': */ /* no break */ case 'T': relay_transport = STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE; break; case 'U': use_null_cipher = 1; /* implies 'S' */ /* no break */ case 'S': use_secure = 1; break; case 'W': g_use_auth_secret_with_timestamp = 1; STRCPY(g_auth_secret,optarg); break; case 'i': { char* fn = find_config_file(optarg,1); if(!fn) { fprintf(stderr,"ERROR: file %s not found\n",optarg); exit(-1); } STRCPY(cert_file,fn); free(fn); } break; case 'k': { char* fn = find_config_file(optarg,1); if(!fn) { fprintf(stderr,"ERROR: file %s not found\n",optarg); exit(-1); } STRCPY(pkey_file,fn); free(fn); } break; default: fprintf(stderr, "%s\n", Usage); exit(1); } } if(dual_allocation) { no_rtcp = 1; } if(g_use_auth_secret_with_timestamp) { { char new_uname[1025]; const unsigned long exp_time = 3600 * 24; /* one day */ if(g_uname[0]) { snprintf(new_uname,sizeof(new_uname),"%lu%c%s",(unsigned long)time(NULL) + exp_time,rest_api_separator, (char*)g_uname); } else { snprintf(new_uname,sizeof(new_uname),"%lu", (unsigned long)time(NULL) + exp_time); } STRCPY(g_uname,new_uname); } { u08bits hmac[MAXSHASIZE]; unsigned int hmac_len; switch(shatype) { case SHATYPE_SHA256: hmac_len = SHA256SIZEBYTES; break; case SHATYPE_SHA384: hmac_len = SHA384SIZEBYTES; break; case SHATYPE_SHA512: hmac_len = SHA512SIZEBYTES; break; default: hmac_len = SHA1SIZEBYTES; }; hmac[0]=0; if(stun_calculate_hmac(g_uname, strlen((char*)g_uname), (u08bits*)g_auth_secret, strlen(g_auth_secret), hmac, &hmac_len, shatype)>=0) { size_t pwd_length = 0; char *pwd = base64_encode(hmac,hmac_len,&pwd_length); if(pwd) { if(pwd_length>0) { ns_bcopy(pwd,g_upwd,pwd_length); g_upwd[pwd_length]=0; } } free(pwd); } } } if(is_TCP_relay()) { dont_fragment = 0; no_rtcp = 1; c2c = 1; use_tcp = 1; do_not_use_channel = 1; } if(port == 0) { if(use_secure) port = DEFAULT_STUN_TLS_PORT; else port = DEFAULT_STUN_PORT; } if (clmessage_length < (int) sizeof(message_info)) clmessage_length = (int) sizeof(message_info); const int max_header = 100; if(clmessage_length > (int)(STUN_BUFFER_SIZE-max_header)) { fprintf(stderr,"Message length was corrected to %d\n",(STUN_BUFFER_SIZE-max_header)); clmessage_length = (int)(STUN_BUFFER_SIZE-max_header); } if (optind >= argc) { fprintf(stderr, "%s\n", Usage); exit(-1); } if (!c2c) { if (make_ioa_addr((const u08bits*) peer_address, peer_port, &peer_addr) < 0) { return -1; } if(peer_addr.ss.sa_family == AF_INET6) { default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6; } else if(peer_addr.ss.sa_family == AF_INET) { default_address_family = STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4; } } /* SSL Init ==>> */ if(use_secure) { SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); const char *csuite = "ALL"; //"AES256-SHA" "DH" if(use_null_cipher) csuite = "eNULL"; else if(cipher_suite[0]) csuite=cipher_suite; if(use_tcp) { root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(SSLv23_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(SSLv3_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(TLSv1_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #if TLSv1_1_SUPPORTED root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(TLSv1_1_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #if TLSv1_2_SUPPORTED root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(TLSv1_2_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #endif #endif } else { #if !DTLS_SUPPORTED fprintf(stderr,"ERROR: DTLS is not supported.\n"); exit(-1); #else if(OPENSSL_VERSION_NUMBER < 0x10000000L) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: OpenSSL version is rather old, DTLS may not be working correctly.\n"); } root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(DTLSv1_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #if DTLSv1_2_SUPPORTED root_tls_ctx[root_tls_ctx_num] = SSL_CTX_new(DTLSv1_2_client_method()); SSL_CTX_set_cipher_list(root_tls_ctx[root_tls_ctx_num], csuite); root_tls_ctx_num++; #endif #endif } int sslind = 0; for(sslind = 0; sslind<root_tls_ctx_num; sslind++) { if(cert_file[0]) { if (!SSL_CTX_use_certificate_chain_file(root_tls_ctx[sslind], cert_file)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nERROR: no certificate found!\n"); exit(-1); } } if (!SSL_CTX_use_PrivateKey_file(root_tls_ctx[sslind], pkey_file, SSL_FILETYPE_PEM)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nERROR: no private key found!\n"); exit(-1); } if(cert_file[0]) { if (!SSL_CTX_check_private_key(root_tls_ctx[sslind])) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "\nERROR: invalid private key!\n"); exit(-1); } } if (ca_cert_file[0]) { if (!SSL_CTX_load_verify_locations(root_tls_ctx[sslind], ca_cert_file, NULL )) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "ERROR: cannot load CA from file: %s\n", ca_cert_file); } /* Set to require peer (client) certificate verification */ SSL_CTX_set_verify(root_tls_ctx[sslind], SSL_VERIFY_PEER, NULL ); /* Set the verification depth to 9 */ SSL_CTX_set_verify_depth(root_tls_ctx[sslind], 9); } else { SSL_CTX_set_verify(root_tls_ctx[sslind], SSL_VERIFY_NONE, NULL ); } if(!use_tcp) SSL_CTX_set_read_ahead(root_tls_ctx[sslind], 1); } } start_mclient(argv[optind], port, client_ifname, local_addr, messagenumber, mclient); return 0; }
/* Create and initialize an SSL configuration for a route. This configuration is used by all requests for a given route. An application can have different SSL configurations for different routes. There is also a default SSL configuration that is used when a route does not define a configuration and one for clients. */ static OpenConfig *createOpenSslConfig(MprSocket *sp) { MprSsl *ssl; OpenConfig *cfg; X509_STORE *store; SSL_CTX *context; cchar *key; uchar resume[16]; int verifyMode; ssl = sp->ssl; assert(ssl); if ((ssl->config = mprAllocObj(OpenConfig, manageOpenConfig)) == 0) { return 0; } cfg = ssl->config; if ((context = SSL_CTX_new(SSLv23_method())) == 0) { mprLog("error openssl", 0, "Unable to create SSL context"); return 0; } SSL_CTX_set_app_data(context, (void*) ssl); if (ssl->verifyPeer && !(ssl->caFile || ssl->caPath)) { sp->errorMsg = sfmt("Cannot verify peer due to undefined CA certificates"); SSL_CTX_free(context); return 0; } /* Configure the certificates */ if (ssl->certFile) { if (SSL_CTX_use_certificate_chain_file(context, ssl->certFile) <= 0) { if (SSL_CTX_use_certificate_file(context, ssl->certFile, SSL_FILETYPE_ASN1) <= 0) { mprLog("error openssl", 0, "Cannot open certificate file: %s", ssl->certFile); SSL_CTX_free(context); return 0; } } key = (ssl->keyFile == 0) ? ssl->certFile : ssl->keyFile; if (key) { if (SSL_CTX_use_PrivateKey_file(context, key, SSL_FILETYPE_PEM) <= 0) { /* attempt ASN1 for self-signed format */ if (SSL_CTX_use_PrivateKey_file(context, key, SSL_FILETYPE_ASN1) <= 0) { mprLog("error openssl", 0, "Cannot open private key file: %s", key); SSL_CTX_free(context); return 0; } } if (!SSL_CTX_check_private_key(context)) { mprLog("error openssl", 0, "Check of private key file failed: %s", key); SSL_CTX_free(context); return 0; } } } if (ssl->ciphers) { ssl->ciphers = mapCipherNames(ssl->ciphers); } if (!ssl->ciphers && (sp->flags & MPR_SOCKET_SERVER)) { ssl->ciphers = sclone(OPENSSL_DEFAULT_CIPHERS); } if (ssl->ciphers) { mprLog("info openssl", 5, "Using SSL ciphers: %s", ssl->ciphers); if (SSL_CTX_set_cipher_list(context, ssl->ciphers) != 1) { sp->errorMsg = sfmt("Unable to set cipher list \"%s\". %s", ssl->ciphers, getOssError(sp)); SSL_CTX_free(context); return 0; } } verifyMode = ssl->verifyPeer ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE; if (verifyMode != SSL_VERIFY_NONE) { if (!(ssl->caFile || ssl->caPath)) { sp->errorMsg = sclone("No defined certificate authority file"); SSL_CTX_free(context); return 0; } if ((!SSL_CTX_load_verify_locations(context, (char*) ssl->caFile, (char*) ssl->caPath)) || (!SSL_CTX_set_default_verify_paths(context))) { sp->errorMsg = sfmt("Unable to set certificate locations: %s: %s", ssl->caFile, ssl->caPath); SSL_CTX_free(context); return 0; } if (ssl->caFile) { STACK_OF(X509_NAME) *certNames; certNames = SSL_load_client_CA_file(ssl->caFile); if (certNames) { /* Define the list of CA certificates to send to the client before they send their client certificate for validation */ SSL_CTX_set_client_CA_list(context, certNames); } } store = SSL_CTX_get_cert_store(context); if (ssl->revokeList && !X509_STORE_load_locations(store, ssl->revokeList, 0)) { mprLog("error openssl", 0, "Cannot load certificate revoke list: %s", ssl->revokeList); SSL_CTX_free(context); return 0; } if (sp->flags & MPR_SOCKET_SERVER) { SSL_CTX_set_verify_depth(context, ssl->verifyDepth); } } /* Define callbacks */ SSL_CTX_set_verify(context, verifyMode, verifyPeerCertificate); /* Configure DH parameters */ SSL_CTX_set_tmp_dh_callback(context, dhcallback); cfg->dhKey = getDhKey(); /* Define default OpenSSL options */ SSL_CTX_set_options(context, SSL_OP_ALL); /* Ensure we generate a new private key for each connection */ SSL_CTX_set_options(context, SSL_OP_SINGLE_DH_USE); /* Define a session reuse context */ RAND_bytes(resume, sizeof(resume)); SSL_CTX_set_session_id_context(context, resume, sizeof(resume)); /* Elliptic Curve initialization */ #if SSL_OP_SINGLE_ECDH_USE #ifdef SSL_CTX_set_ecdh_auto SSL_CTX_set_ecdh_auto(context, 1); #else { EC_KEY *ecdh; cchar *name; int nid; name = ME_MPR_SSL_CURVE; if ((nid = OBJ_sn2nid(name)) == 0) { sp->errorMsg = sfmt("Unknown curve name \"%s\"", name); SSL_CTX_free(context); return 0; } if ((ecdh = EC_KEY_new_by_curve_name(nid)) == 0) { sp->errorMsg = sfmt("Unable to create curve \"%s\"", name); SSL_CTX_free(context); return 0; } SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE); SSL_CTX_set_tmp_ecdh(context, ecdh); EC_KEY_free(ecdh); } #endif #endif SSL_CTX_set_mode(context, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_AUTO_RETRY | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); #ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING SSL_CTX_set_options(context, SSL_OP_MSIE_SSLV2_RSA_PADDING); #endif #ifdef SSL_MODE_RELEASE_BUFFERS SSL_CTX_set_mode(context, SSL_MODE_RELEASE_BUFFERS); #endif #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE SSL_CTX_set_mode(context, SSL_OP_CIPHER_SERVER_PREFERENCE); #endif /* Select the required protocols Disable SSLv2 and SSLv3 by default -- they are insecure. */ SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); SSL_CTX_set_options(context, SSL_OP_NO_SSLv3); #ifdef SSL_OP_NO_TLSv1 if (!(ssl->protocols & MPR_PROTO_TLSV1)) { SSL_CTX_set_options(context, SSL_OP_NO_TLSv1); } #endif #ifdef SSL_OP_NO_TLSv1_1 if (!(ssl->protocols & MPR_PROTO_TLSV1_1)) { SSL_CTX_set_options(context, SSL_OP_NO_TLSv1_1); } #endif #ifdef SSL_OP_NO_TLSv1_2 if (!(ssl->protocols & MPR_PROTO_TLSV1_2)) { SSL_CTX_set_options(context, SSL_OP_NO_TLSv1_2); } #endif /* Options set via main.me mpr.ssl.* */ #if defined(SSL_OP_NO_TICKET) /* Ticket based session reuse is enabled by default */ #if defined(ME_MPR_SSL_TICKET) if (ME_MPR_SSL_TICKET) { SSL_CTX_clear_options(context, SSL_OP_NO_TICKET); } else { SSL_CTX_set_options(context, SSL_OP_NO_TICKET); } #else SSL_CTX_clear_options(context, SSL_OP_NO_TICKET); #endif #endif #if defined(SSL_OP_NO_COMPRESSION) /* Use of compression is not secure. Disabled by default. */ #if defined(ME_MPR_SSL_COMPRESSION) if (ME_MPR_SSL_COMPRESSION) { SSL_CTX_clear_options(context, SSL_OP_NO_COMPRESSION); } else { SSL_CTX_set_options(context, SSL_OP_NO_COMPRESSION); } #else /* CRIME attack targets compression */ SSL_CTX_clear_options(context, SSL_OP_NO_COMPRESSION); #endif #endif #if defined(SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION) /* Force a new session on renegotiation. Default to true. */ #if defined(ME_MPR_SSL_RENEGOTIATE) if (ME_MPR_SSL_RENEGOTIATE) { SSL_CTX_clear_options(context, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); } else { SSL_CTX_set_options(context, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); } #else SSL_CTX_set_options(context, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); #endif #endif #if defined(SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) /* Disables a countermeasure against a SSL 3.0/TLS 1.0 protocol vulnerability affecting CBC ciphers. Defaults to true. */ #if defined(ME_MPR_SSL_EMPTY_FRAGMENTS) if (ME_MPR_SSL_EMPTY_FRAGMENTS) { /* SSL_OP_ALL disables empty fragments. Only needed for ancient browsers like IE-6 on SSL-3.0/TLS-1.0 */ SSL_CTX_clear_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); } else { SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); } #else SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); #endif #endif #if defined(ME_MPR_SSL_CACHE) /* Set the number of sessions supported. Default in OpenSSL is 20K. */ SSL_CTX_sess_set_cache_size(context, ME_MPR_SSL_CACHE); #else SSL_CTX_sess_set_cache_size(context, 1024); #endif cfg->context = context; return cfg; }