/* * Copyright (C) 2011, 2012, 2013 Citrix Systems * Copyright (C) 2014 Vivocha S.p.A. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "../mainrelay.h" #include "dbd_mysql.h" #if !defined(TURN_NO_MYSQL) #include /////////////////////////////////////////////////////////////////////////////////////////////////////////// static int donot_print_connection_success = 0; struct _Myconninfo { char *host; char *dbname; char *user; char *password; unsigned int port; unsigned int connect_timeout; unsigned int read_timeout; /* SSL ==>> */ char *key; char *ca; char *cert; char *capath; char *cipher; /* <<== SSL : see http://dev.mysql.com/doc/refman/5.0/en/mysql-ssl-set.html */ }; typedef struct _Myconninfo Myconninfo; static void MyconninfoFree(Myconninfo *co) { if(co) { if(co->host) free(co->host); if(co->dbname) free(co->dbname); if(co->user) free(co->user); if(co->password) free(co->password); if(co->key) free(co->key); if(co->ca) free(co->ca); if(co->cert) free(co->cert); if(co->capath) free(co->capath); if(co->cipher) free(co->cipher); bzero(co,sizeof(Myconninfo)); } } char* decryptPassword(char* in, const unsigned char* mykey){ char *out; unsigned char iv[8] = {0}; //changed AES_KEY key; unsigned char outdata[256]; //changed AES_set_encrypt_key(mykey, 128, &key); int newTotalSize=decodedTextSize(in); int bytes_to_decode = strlen(in); unsigned char *encryptedText = base64decode(in, bytes_to_decode); //changed char last[1024]=""; struct ctr_state state; init_ctr(&state, iv); memset(outdata,'\0', sizeof(outdata)); #if OPENSSL_VERSION_NUMBER >= 0x10100000L CRYPTO_ctr128_encrypt(encryptedText, outdata, newTotalSize, &key, state.ivec, state.ecount, &state.num,(block128_f)AES_encrypt); #else AES_ctr128_encrypt(encryptedText, outdata, newTotalSize, &key, state.ivec, state.ecount, &state.num); #endif strcat(last,(char*)outdata); out=(char*)malloc(sizeof(char)*strlen(last)); strcpy(out,last); return out; } static Myconninfo *MyconninfoParse(char *userdb, char **errmsg) { Myconninfo *co = (Myconninfo*)malloc(sizeof(Myconninfo)); bzero(co,sizeof(Myconninfo)); if(userdb) { char *s0=strdup(userdb); char *s = s0; while(s && *s) { while(*s && (*s==' ')) ++s; char *snext = strstr(s," "); if(snext) { *snext = 0; ++snext; } char* seq = strstr(s,"="); if(!seq) { MyconninfoFree(co); co = NULL; if(errmsg) { *errmsg = strdup(s); } break; } *seq = 0; if(!strcmp(s,"host")) co->host = strdup(seq+1); else if(!strcmp(s,"ip")) co->host = strdup(seq+1); else if(!strcmp(s,"addr")) co->host = strdup(seq+1); else if(!strcmp(s,"ipaddr")) co->host = strdup(seq+1); else if(!strcmp(s,"hostaddr")) co->host = strdup(seq+1); else if(!strcmp(s,"dbname")) co->dbname = strdup(seq+1); else if(!strcmp(s,"db")) co->dbname = strdup(seq+1); else if(!strcmp(s,"database")) co->dbname = strdup(seq+1); else if(!strcmp(s,"user")) co->user = strdup(seq+1); else if(!strcmp(s,"uname")) co->user = strdup(seq+1); else if(!strcmp(s,"name")) co->user = strdup(seq+1); else if(!strcmp(s,"username")) co->user = strdup(seq+1); else if(!strcmp(s,"password")) co->password = strdup(seq+1); else if(!strcmp(s,"pwd")) co->password = strdup(seq+1); else if(!strcmp(s,"passwd")) co->password = strdup(seq+1); else if(!strcmp(s,"secret")) co->password = strdup(seq+1); else if(!strcmp(s,"port")) co->port = (unsigned int)atoi(seq+1); else if(!strcmp(s,"p")) co->port = (unsigned int)atoi(seq+1); else if(!strcmp(s,"connect_timeout")) co->connect_timeout = (unsigned int)atoi(seq+1); else if(!strcmp(s,"timeout")) co->connect_timeout = (unsigned int)atoi(seq+1); else if(!strcmp(s,"read_timeout")) co->read_timeout = (unsigned int)atoi(seq+1); else if(!strcmp(s,"key")) co->key = strdup(seq+1); else if(!strcmp(s,"ssl-key")) co->key = strdup(seq+1); else if(!strcmp(s,"ca")) co->ca = strdup(seq+1); else if(!strcmp(s,"ssl-ca")) co->ca = strdup(seq+1); else if(!strcmp(s,"capath")) co->capath = strdup(seq+1); else if(!strcmp(s,"ssl-capath")) co->capath = strdup(seq+1); else if(!strcmp(s,"cert")) co->cert = strdup(seq+1); else if(!strcmp(s,"ssl-cert")) co->cert = strdup(seq+1); else if(!strcmp(s,"cipher")) co->cipher = strdup(seq+1); else if(!strcmp(s,"ssl-cipher")) co->cipher = strdup(seq+1); else { MyconninfoFree(co); co = NULL; if(errmsg) { *errmsg = strdup(s); } break; } s = snext; } free(s0); } if(co) { if(!(co->dbname)) co->dbname=strdup("0"); if(!(co->host)) co->host=strdup("127.0.0.1"); if(!(co->user)) co->user=strdup(""); if(!(co->password)) co->password=strdup(""); } return co; } static MYSQL *get_mydb_connection(void) { persistent_users_db_t *pud = get_persistent_users_db(); MYSQL *mydbconnection = (MYSQL*)pthread_getspecific(connection_key); if(mydbconnection) { if(mysql_ping(mydbconnection)) { mysql_close(mydbconnection); mydbconnection=NULL; (void) pthread_setspecific(connection_key, mydbconnection); } } if(!mydbconnection) { char *errmsg=NULL; Myconninfo *co=MyconninfoParse(pud->userdb, &errmsg); if(!co) { if(errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection <%s>, connection string format error: %s\n",pud->userdb,errmsg); free(errmsg); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection <%s>, connection string format error\n",pud->userdb); } } else if(errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection <%s>, connection string format error: %s\n",pud->userdb,errmsg); free(errmsg); MyconninfoFree(co); } else if(!(co->dbname)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "MySQL Database name is not provided: <%s>\n",pud->userdb); MyconninfoFree(co); } else { mydbconnection = mysql_init(NULL); if(!mydbconnection) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot initialize MySQL DB connection\n"); } else { if(co->connect_timeout) mysql_options(mydbconnection,MYSQL_OPT_CONNECT_TIMEOUT,&(co->connect_timeout)); if(co->read_timeout) mysql_options(mydbconnection,MYSQL_OPT_READ_TIMEOUT,&(co->read_timeout)); if(co->ca || co->capath || co->cert || co->cipher || co->key) { mysql_ssl_set(mydbconnection, co->key, co->cert, co->ca, co->capath, co->cipher); } if(turn_params.secret_key_file[0]){ co->password = decryptPassword(co->password, turn_params.secret_key); } MYSQL *conn = mysql_real_connect(mydbconnection, co->host, co->user, co->password, co->dbname, co->port, NULL, CLIENT_IGNORE_SIGPIPE); if(!conn) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open MySQL DB connection: <%s>, runtime error\n",pud->userdb); mysql_close(mydbconnection); mydbconnection=NULL; } else if(mysql_select_db(mydbconnection, co->dbname)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot connect to MySQL DB: %s\n",co->dbname); mysql_close(mydbconnection); mydbconnection=NULL; } else if(!donot_print_connection_success) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MySQL DB connection success: %s\n",pud->userdb); if(turn_params.secret_key_file[0]) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Encryption with AES is activated.\n"); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Connection is secure.\n"); } else TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Connection is not secure.\n"); donot_print_connection_success = 1; } } MyconninfoFree(co); } if(mydbconnection) { (void) pthread_setspecific(connection_key, mydbconnection); } } return mydbconnection; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// static int mysql_get_auth_secrets(secrets_list_t *sl, uint8_t *realm) { int ret = -1; MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement)-1,"select value from turn_secret where realm='%s'",realm); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)==1) { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0]) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { size_t sz = lengths[0]; char auth_secret[TURN_LONG_STRING_SIZE]; bcopy(row[0],auth_secret,sz); auth_secret[sz]=0; add_to_secrets_list(sl,auth_secret); } } } } ret = 0; } if(mres) mysql_free_result(mres); } } return ret; } static int mysql_get_user_key(uint8_t *usname, uint8_t *realm, hmackey_t key) { int ret = -1; MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; /* direct user input eliminated - there is no SQL injection problem (since version 4.4.5.3) */ snprintf(statement,sizeof(statement),"select hmackey from turnusers_lt where name='%s' and realm='%s'",usname,realm); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=1) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { MYSQL_ROW row = mysql_fetch_row(mres); if(row && row[0]) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { size_t sz = get_hmackey_size(SHATYPE_DEFAULT)*2; if(lengths[0]kid,kid); bcopy(row[0],key->ikm_key,lengths[0]); key->ikm_key[lengths[0]]=0; char stimestamp[128]; bcopy(row[1],stimestamp,lengths[1]); stimestamp[lengths[1]]=0; key->timestamp = (uint64_t)strtoull(stimestamp,NULL,10); char slifetime[128]; bcopy(row[2],slifetime,lengths[2]); slifetime[lengths[2]]=0; key->lifetime = (uint32_t)strtoul(slifetime,NULL,10); bcopy(row[3],key->as_rs_alg,lengths[3]); key->as_rs_alg[lengths[3]]=0; bcopy(row[4],key->realm,lengths[4]); key->realm[lengths[4]]=0; ret = 0; } } } if(mres) mysql_free_result(mres); } } return ret; } static int mysql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts,secrets_list_t *realms) { oauth_key_data_raw key_; oauth_key_data_raw *key=&key_; int ret = -1; char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg,realm,kid from oauth_key order by kid"); MYSQL * myc = get_mydb_connection(); if(myc) { int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=6) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { MYSQL_ROW row = mysql_fetch_row(mres); while(row) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { bcopy(row[0],key->ikm_key,lengths[0]); key->ikm_key[lengths[0]]=0; char stimestamp[128]; bcopy(row[1],stimestamp,lengths[1]); stimestamp[lengths[1]]=0; key->timestamp = (uint64_t)strtoull(stimestamp,NULL,10); char slifetime[128]; bcopy(row[2],slifetime,lengths[2]); slifetime[lengths[2]]=0; key->lifetime = (uint32_t)strtoul(slifetime,NULL,10); bcopy(row[3],key->as_rs_alg,lengths[3]); key->as_rs_alg[lengths[3]]=0; bcopy(row[4],key->realm,lengths[4]); key->realm[lengths[4]]=0; bcopy(row[5],key->kid,lengths[5]); key->kid[lengths[5]]=0; if(kids) { add_to_secrets_list(kids,key->kid); add_to_secrets_list(teas,key->as_rs_alg); add_to_secrets_list(realms,key->realm); { char ts[256]; snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp); add_to_secrets_list(tss,ts); } { char lt[256]; snprintf(lt,sizeof(lt)-1,"%lu",(unsigned long)key->lifetime); add_to_secrets_list(lts,lt); } } else { printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s, realm=%s\n", key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->as_rs_alg,key->realm); } } row = mysql_fetch_row(mres); } } if(mres) mysql_free_result(mres); } } return ret; } static int mysql_set_user_key(uint8_t *usname, uint8_t *realm, const char *key) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"insert into turnusers_lt (realm,name,hmackey) values('%s','%s','%s')",realm,usname,key); int res = mysql_query(myc, statement); if(!res) { ret = 0; } else { snprintf(statement,sizeof(statement),"update turnusers_lt set hmackey='%s' where name='%s' and realm='%s'",key,usname,realm); res = mysql_query(myc, statement); if(!res) { ret = 0; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user key information: %s\n",mysql_error(myc)); } } } return ret; } static int mysql_set_oauth_key(oauth_key_data_raw *key) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg,realm) values('%s','%s',%llu,%lu,'%s','%s')", key->kid,key->ikm_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime, key->as_rs_alg,key->realm); int res = mysql_query(myc, statement); if(res) { snprintf(statement,sizeof(statement),"update oauth_key set ikm_key='%s',timestamp=%lu,lifetime=%lu, as_rs_alg='%s', realm='%s' where kid='%s'",key->ikm_key,(unsigned long)key->timestamp,(unsigned long)key->lifetime, key->as_rs_alg,key->realm,key->kid); res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth key information: %s\n",mysql_error(myc)); } else { ret = 0; } } else { ret = 0; } } return ret; } static int mysql_del_user(uint8_t *usname, uint8_t *realm) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"delete from turnusers_lt where name='%s' and realm='%s'",usname,realm); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting user key information: %s\n",mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_del_oauth_key(const uint8_t *kid) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"delete from oauth_key where kid = '%s'",(const char*)kid); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth key information: %s\n",mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_list_users(uint8_t *realm, secrets_list_t *users, secrets_list_t *realms) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; MYSQL * myc = get_mydb_connection(); if(myc) { if(realm[0]) { snprintf(statement,sizeof(statement),"select name, realm from turnusers_lt where realm='%s' order by name",realm); } else { snprintf(statement,sizeof(statement),"select name, realm from turnusers_lt order by realm,name"); } int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=2) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0]) { if(users) { add_to_secrets_list(users,row[0]); if(realms) { if(row[1]) { add_to_secrets_list(realms,row[1]); } else { add_to_secrets_list(realms,(char*)realm); } } } else { printf("%s[%s]\n", row[0], row[1]); } } } } ret = 0; } if(mres) mysql_free_result(mres); } } return ret; } static int mysql_list_secrets(uint8_t *realm, secrets_list_t *secrets, secrets_list_t *realms) { int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; char statement[TURN_LONG_STRING_SIZE]; if (realm[0]) { snprintf(statement, sizeof(statement), "select value,realm from turn_secret where realm='%s' order by value", realm); } else { snprintf(statement, sizeof(statement), "select value,realm from turn_secret order by realm,value"); } donot_print_connection_success=1; MYSQL * myc = get_mydb_connection(); if(myc) { int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=2) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { const char* kval = row[0]; if(kval) { const char* rval = row[1]; if(secrets) { add_to_secrets_list(secrets,kval); if(realms) { if(rval && *rval) { add_to_secrets_list(realms,rval); } else { add_to_secrets_list(realms,(char*)realm); } } } else { printf("%s[%s]\n",kval,rval); } } } } ret = 0; } if(mres) mysql_free_result(mres); } } return ret; } static int mysql_del_secret(uint8_t *secret, uint8_t *realm) { int ret = -1; donot_print_connection_success=1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { if(!secret || (secret[0]==0)) snprintf(statement,sizeof(statement),"delete from turn_secret where realm='%s'",realm); else snprintf(statement,sizeof(statement),"delete from turn_secret where value='%s' and realm='%s'",secret,realm); mysql_query(myc, statement); ret = 0; } return ret; } static int mysql_set_secret(uint8_t *secret, uint8_t *realm) { int ret = -1; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { snprintf(statement,sizeof(statement),"insert into turn_secret (realm,value) values('%s','%s')",realm,secret); int res = mysql_query(myc, statement); if (res) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information: %s\n", mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_set_permission_ip(const char *kind, uint8_t *realm, const char* ip, int del) { int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { if(del) { snprintf(statement, sizeof(statement), "delete from %s_peer_ip where realm = '%s' and ip_range = '%s'", kind, (char*)realm, ip); } else { snprintf(statement, sizeof(statement), "insert into %s_peer_ip (realm,ip_range) values('%s','%s')", kind, (char*)realm, ip); } int res = mysql_query(myc, statement); if (res) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Error inserting permission ip information: %s\n", mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_add_origin(uint8_t *origin, uint8_t *realm) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { snprintf(statement,sizeof(statement),"insert into turn_origin_to_realm (origin,realm) values('%s','%s')",origin,realm); int res = mysql_query(myc, statement); if (res) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Error inserting origin information: %s\n", mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_del_origin(uint8_t *origin) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { snprintf(statement,sizeof(statement),"delete from turn_origin_to_realm where origin='%s'",origin); int res = mysql_query(myc, statement); if (res) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Error deleting origin information: %s\n", mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_list_origins(uint8_t *realm, secrets_list_t *origins, secrets_list_t *realms) { int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; donot_print_connection_success = 1; MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; if(realm && realm[0]) { snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm where realm='%s' order by origin",realm); } else { snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm order by realm,origin"); } int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=2) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0] && row[1]) { const char* kval = row[0]; const char* rval = row[1]; if(origins) { add_to_secrets_list(origins,kval); if(realms) { if(rval && *rval) { add_to_secrets_list(realms,rval); } else { add_to_secrets_list(realms,(char*)realm); } } } else { printf("%s ==>> %s\n",kval,rval); } } } } ret = 0; } if(mres) mysql_free_result(mres); } } return ret; } static int mysql_set_realm_option_one(uint8_t *realm, unsigned long value, const char* opt) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if (myc) { { snprintf(statement,sizeof(statement),"delete from turn_realm_option where realm='%s' and opt='%s'",realm,opt); mysql_query(myc, statement); } if(value>0) { snprintf(statement,sizeof(statement),"insert into turn_realm_option (realm,opt,value) values('%s','%s','%lu')",realm,opt,(unsigned long)value); int res = mysql_query(myc, statement); if (res) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Error inserting realm option information: %s\n", mysql_error(myc)); } else { ret = 0; } } } return ret; } static int mysql_list_realm_options(uint8_t *realm) { int ret = -1; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if(myc) { if(realm && realm[0]) { snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option where realm='%s' order by realm,opt",realm); } else { snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option order by realm,opt"); } int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=3) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0] && row[1] && row[2]) { printf("%s[%s]=%s\n",row[1],row[0],row[2]); } } } ret = 0; } if(mres) mysql_free_result(mres); } } return ret; } static void mysql_auth_ping(void * rch) { UNUSED_ARG(rch); MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; STRCPY(statement,"select value from turn_secret"); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { mysql_free_result(mres); } } } } static int mysql_get_ip_list(const char *kind, ip_range_list_t * list) { int ret = -1; MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement),"select ip_range,realm from %s_peer_ip",kind); int res = mysql_query(myc, statement); if(res) { static int wrong_table_reported = 0; if(!wrong_table_reported) { wrong_table_reported = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information; probably, the tables 'allowed_peer_ip' and/or 'denied_peer_ip' have to be upgraded to include the realm column.\n"); } snprintf(statement, sizeof(statement), "select ip_range,'' from %s_peer_ip", kind); res = mysql_query(myc, statement); } if(res == 0) { MYSQL_RES *mres = mysql_store_result(myc); if(mres && mysql_field_count(myc)==2) { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0]) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { size_t sz = lengths[0]; char kval[TURN_LONG_STRING_SIZE]; bcopy(row[0],kval,sz); kval[sz]=0; sz = lengths[1]; char rval[TURN_LONG_STRING_SIZE]; bcopy(row[1],rval,sz); rval[sz]=0; add_ip_list_range(kval,rval,list); } } } } ret = 0; } if(mres) mysql_free_result(mres); } } return ret; } static void mysql_reread_realms(secrets_list_t * realms_list) { MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; { snprintf(statement,sizeof(statement),"select origin,realm from turn_origin_to_realm"); int res = mysql_query(myc, statement); if(res == 0) { MYSQL_RES *mres = mysql_store_result(myc); if(mres && mysql_field_count(myc)==2) { ur_string_map *o_to_realm_new = ur_string_map_create(free); for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0] && row[1]) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { size_t sz = lengths[0]; char oval[513]; bcopy(row[0],oval,sz); oval[sz]=0; char *rval=strdup(row[1]); get_realm(rval); ur_string_map_value_type value = (ur_string_map_value_type)rval; ur_string_map_put(o_to_realm_new, (ur_string_map_key_type) oval, value); } } } } update_o_to_realm(o_to_realm_new); } if(mres) mysql_free_result(mres); } } { size_t i = 0; size_t rlsz = 0; lock_realms(); rlsz = realms_list->sz; unlock_realms(); for (i = 0; isecrets[i]; realm_params_t* rp = get_realm(realm); lock_realms(); rp->options.perf_options.max_bps = turn_params.max_bps; unlock_realms(); lock_realms(); rp->options.perf_options.total_quota = turn_params.total_quota; unlock_realms(); lock_realms(); rp->options.perf_options.user_quota = turn_params.user_quota; unlock_realms(); } } snprintf(statement,sizeof(statement),"select realm,opt,value from turn_realm_option"); int res = mysql_query(myc, statement); if(res == 0) { MYSQL_RES *mres = mysql_store_result(myc); if(mres && mysql_field_count(myc)==3) { for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { if(row[0] && row[1] && row[2]) { unsigned long *lengths = mysql_fetch_lengths(mres); if(lengths) { char rval[513]; size_t sz = lengths[0]; bcopy(row[0],rval,sz); rval[sz]=0; char oval[513]; sz = lengths[1]; bcopy(row[1],oval,sz); oval[sz]=0; char vval[513]; sz = lengths[2]; bcopy(row[2],vval,sz); vval[sz]=0; realm_params_t* rp = get_realm(rval); if(!strcmp(oval,"max-bps")) rp->options.perf_options.max_bps = (band_limit_t)strtoul(vval,NULL,10); else if(!strcmp(oval,"total-quota")) rp->options.perf_options.total_quota = (vint)atoi(vval); else if(!strcmp(oval,"user-quota")) rp->options.perf_options.user_quota = (vint)atoi(vval); else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown realm option: %s\n", oval); } } } } } } if(mres) mysql_free_result(mres); } } } ///////////////////////////////////////////////////// static int mysql_get_admin_user(const uint8_t *usname, uint8_t *realm, password_t pwd) { int ret = -1; realm[0]=0; pwd[0]=0; MYSQL * myc = get_mydb_connection(); if(myc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement,sizeof(statement),"select realm,password from admin_user where name='%s'",usname); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=2) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { MYSQL_ROW row = mysql_fetch_row(mres); if(row && row[0]) { strncpy((char*)realm,row[0],STUN_MAX_REALM_SIZE); strncpy((char*)pwd,row[1],STUN_MAX_PWD_SIZE); ret = 0; } } if(mres) mysql_free_result(mres); } } return ret; } static int mysql_set_admin_user(const uint8_t *usname, const uint8_t *realm, const password_t pwd) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; donot_print_connection_success=1; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"insert into admin_user (realm,name,password) values('%s','%s','%s')",realm,usname,pwd); int res = mysql_query(myc, statement); if(!res) { ret = 0; } else { snprintf(statement,sizeof(statement),"update admin_user set realm='%s',password='%s' where name='%s'",realm,pwd,usname); res = mysql_query(myc, statement); if(!res) { ret = 0; } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user key information: %s\n",mysql_error(myc)); } } } return ret; } static int mysql_del_admin_user(const uint8_t *usname) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; donot_print_connection_success=1; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"delete from admin_user where name='%s'",usname); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting admin user information: %s\n",mysql_error(myc)); } else { ret = 0; } } return ret; } static int mysql_list_admin_users(int no_print) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; donot_print_connection_success=1; MYSQL * myc = get_mydb_connection(); if(myc) { snprintf(statement,sizeof(statement),"select name, realm from admin_user order by realm,name"); int res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); } else if(mysql_field_count(myc)!=2) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { ret = 0; for(;;) { MYSQL_ROW row = mysql_fetch_row(mres); if(!row) { break; } else { ++ret; if(row[0] && !no_print) { if(row[1] && row[1][0]) { printf("%s[%s]\n",row[0],row[1]); } else { printf("%s\n",row[0]); } } } } } if(mres) mysql_free_result(mres); } } return ret; } static void mysql_disconnect(void) { MYSQL *mydbconnection = (MYSQL*)pthread_getspecific(connection_key); if (mydbconnection) { mysql_close(mydbconnection); mydbconnection=NULL; } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MySQL connection was closed.\n"); } ////////////////////////////////////////////////////// static const turn_dbdriver_t driver = { &mysql_get_auth_secrets, &mysql_get_user_key, &mysql_set_user_key, &mysql_del_user, &mysql_list_users, &mysql_list_secrets, &mysql_del_secret, &mysql_set_secret, &mysql_add_origin, &mysql_del_origin, &mysql_list_origins, &mysql_set_realm_option_one, &mysql_list_realm_options, &mysql_auth_ping, &mysql_get_ip_list, &mysql_set_permission_ip, &mysql_reread_realms, &mysql_set_oauth_key, &mysql_get_oauth_key, &mysql_del_oauth_key, &mysql_list_oauth_keys, &mysql_get_admin_user, &mysql_set_admin_user, &mysql_del_admin_user, &mysql_list_admin_users, &mysql_disconnect }; const turn_dbdriver_t * get_mysql_dbdriver(void) { return &driver; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// #else const turn_dbdriver_t * get_mysql_dbdriver(void) { return NULL; } #endif