/** * Check the server name to find port info first * return 1 when found, else 0 * Warning: connect_info-> & login-> are all modified when needed */ static int parse_server_name_for_port(TDSCONNECTINFO * connect_info, TDSLOGIN * login) { char *pSep, *pEnd; char *server; /* seek the ':' in login server_name */ server = tds_dstr_cstr(&login->server_name); pEnd = server + strlen(server); for (pSep = server; pSep < pEnd; pSep++) if (*pSep == ':') break; if ((pSep < pEnd) && (pSep != server)) { /* yes, i found it! */ if (!tds_dstr_copyn(&connect_info->server_name, server, pSep - server)) /* end the server_name before the ':' */ return 0; /* FALSE */ /* modify connect_info-> && login->server_name & ->port */ login->port = connect_info->port = atoi(pSep + 1); *pSep = 0; /* connect_info->ip_addr needed */ { char tmp[256]; tds_lookup_host(tds_dstr_cstr(&connect_info->server_name), tmp); if (!tds_dstr_copy(&connect_info->ip_addr, tmp)) return 0; /* FALSE */ } return 1; /* TRUE */ } else return 0; /* FALSE */ }
/** * Handle conversions from TDS (N)CHAR to ODBC (W)CHAR */ static SQLLEN odbc_convert_char(TDS_STMT * stmt, TDSCOLUMN * curcol, TDS_CHAR * src, TDS_UINT srclen, int desttype, TDS_CHAR * dest, SQLULEN destlen) { const char *ib; char *ob; size_t il, ol, char_size; /* FIXME MARS not correct cause is the global tds but stmt->tds can be NULL on SQLGetData */ TDSSOCKET *tds = stmt->dbc->tds_socket; TDSICONV *conv = curcol->char_conv; if (!conv) conv = tds->conn->char_convs[client2server_chardata]; if (desttype == SQL_C_WCHAR) { /* SQL_C_WCHAR, convert to wide encode */ conv = tds_iconv_get(tds->conn, ODBC_WIDE_NAME, conv->to.charset.name); if (!conv) conv = tds_iconv_get(tds->conn, ODBC_WIDE_NAME, "ISO-8859-1"); #ifdef ENABLE_ODBC_WIDE } else { conv = tds_iconv_get(tds->conn, tds_dstr_cstr(&stmt->dbc->original_charset), conv->to.charset.name); if (!conv) conv = tds_iconv_get(tds->conn, tds_dstr_cstr(&stmt->dbc->original_charset), "ISO-8859-1"); if (!conv) conv = tds_iconv_get(tds->conn, "ISO-8859-1", "ISO-8859-1"); #endif } ib = src; il = srclen; ob = dest; ol = 0; char_size = desttype == SQL_C_CHAR ? 1 : SIZEOF_SQLWCHAR; if (destlen >= char_size) { ol = destlen - char_size; memset(&conv->suppress, 0, sizeof(conv->suppress)); conv->suppress.e2big = 1; /* TODO check return value */ tds_iconv(tds, conv, to_client, &ib, &il, &ob, &ol); ol = ob - dest; /* bytes written */ if (curcol) curcol->column_text_sqlgetdatapos += ib - src; /* terminate string */ memset(ob, 0, char_size); } /* returned size have to take into account buffer left unconverted */ if (il == 0 || (conv->from.charset.min_bytes_per_char == conv->from.charset.max_bytes_per_char && conv->to.charset.min_bytes_per_char == conv->to.charset.max_bytes_per_char)) { ol += il * conv->from.charset.min_bytes_per_char / conv->to.charset.min_bytes_per_char; } else { /* TODO convert and discard ?? or return proper SQL_NO_TOTAL values ?? */ return SQL_NO_TOTAL; } return ol; }
void tds7_send_result(TDSSOCKET * tds, TDSRESULTINFO * resinfo) { int i, j; TDSCOLUMN *curcol; /* TDS7+ uses TDS7_RESULT_TOKEN to send column names and info */ tds_put_byte(tds, TDS7_RESULT_TOKEN); /* send the number of columns */ tds_put_smallint(tds, resinfo->num_cols); /* send info about each column */ for (i = 0; i < resinfo->num_cols; i++) { /* usertype, flags, and type */ curcol = resinfo->columns[i]; tds_put_smallint(tds, curcol->column_usertype); tds_put_smallint(tds, curcol->column_flags); tds_put_byte(tds, curcol->column_type); /* smallint? */ /* bytes in "size" field varies */ if (is_blob_type(curcol->column_type)) { tds_put_int(tds, curcol->column_size); } else if (curcol->column_type>=128) { /*is_large_type*/ tds_put_smallint(tds, curcol->column_size); } else { tds_put_tinyint(tds, curcol->column_size); } /* some types have extra info */ if (is_numeric_type(curcol->column_type)) { tds_put_tinyint(tds, curcol->column_prec); tds_put_tinyint(tds, curcol->column_scale); } else if (is_blob_type(curcol->column_type)) { size_t len = tds_dstr_len(&curcol->table_name); const char *name = tds_dstr_cstr(&curcol->table_name); tds_put_smallint(tds, 2 * len); for (j = 0; name[j] != '\0'; j++){ tds_put_byte(tds, name[j]); tds_put_byte(tds, 0); } } /* finally the name, in UCS16 format */ tds_put_byte(tds, tds_dstr_len(&curcol->column_name)); for (j = 0; j < tds_dstr_len(&curcol->column_name); j++) { tds_put_byte(tds, tds_dstr_cstr(&curcol->column_name)[j]); tds_put_byte(tds, 0); } } }
/** * tds_read_config_info() will fill the tds connect_info structure based on configuration * information gathered in the following order: * 1) Program specified in TDSLOGIN structure * 2) The environment variables TDSVER, TDSDUMP, TDSPORT, TDSQUERY, TDSHOST * 3) A config file with the following search order: * a) a readable file specified by environment variable FREETDSCONF * b) a readable file in ~/.freetds.conf * c) a readable file in $prefix/etc/freetds.conf * 3) ~/.interfaces if exists * 4) $SYBASE/interfaces if exists * 5) TDS_DEF_* default values * * .tdsrc and freetds.conf have been added to make the package easier to * integration with various Linux and *BSD distributions. */ TDSCONNECTINFO * tds_read_config_info(TDSSOCKET * tds, TDSLOGIN * login, TDSLOCALE * locale) { TDSCONNECTINFO *connect_info; char *s; char *path; pid_t pid; int opened = 0; /* allocate a new structure with hard coded and build-time defaults */ connect_info = tds_alloc_connect(locale); if (!connect_info) return NULL; s = getenv("TDSDUMPCONFIG"); if (s) { if (*s) { opened = tdsdump_open(s); } else { pid = getpid(); if (asprintf(&path, "/tmp/tdsconfig.log.%d", pid) >= 0) { if (*path) { opened = tdsdump_open(path); } free(path); } } } tdsdump_log(TDS_DBG_INFO1, "%L Attempting to read conf files.\n"); if (!tds_read_conf_file(connect_info, tds_dstr_cstr(&login->server_name))) { /* fallback to interfaces file */ tdsdump_log(TDS_DBG_INFO1, "%L Failed in reading conf file. Trying interface files.\n"); tds_read_interfaces(tds_dstr_cstr(&login->server_name), connect_info); } if (parse_server_name_for_port(connect_info, login)) { tdsdump_log(TDS_DBG_INFO1, "%L Parsed servername, now %s on %d.\n", connect_info->server_name, login->port); } tds_fix_connect(connect_info); /* And finally the login structure */ tds_config_login(connect_info, login); if (opened) { tdsdump_close(); } return connect_info; }
static BOOL write_all_strings(DSNINFO * di) { char odbcini[FILENAME_MAX]; char tmp[100]; const char *section = tds_dstr_cstr(&di->dsn); strcpy(odbcini, "odbc.ini"); WRITESTR("Server", FIELD_STRING(server_name)); WRITESTR("Language", FIELD_STRING(language)); WRITESTR("Database", FIELD_STRING(database)); sprintf(tmp, "%u", di->login->port); WRITESTR("Port", tmp); sprintf(tmp, "%d.%d", TDS_MAJOR(di->login), TDS_MINOR(di->login)); WRITESTR("TDS_Version", tmp); sprintf(tmp, "%u", di->login->text_size); WRITESTR("TextSize", tmp); sprintf(tmp, "%u", di->login->block_size); WRITESTR("PacketSize", tmp); return TRUE; }
void tds_send_result(TDSSOCKET * tds, TDSRESULTINFO * resinfo) { TDSCOLUMN *curcol; int i, totlen; size_t len; tds_put_byte(tds, TDS_RESULT_TOKEN); totlen = 2; for (i = 0; i < resinfo->num_cols; i++) { curcol = resinfo->columns[i]; len = tds_dstr_len(&curcol->column_name); totlen += 8; totlen += len; curcol = resinfo->columns[i]; if (!is_fixed_type(curcol->column_type)) { totlen++; } } tds_put_smallint(tds, totlen); tds_put_smallint(tds, resinfo->num_cols); for (i = 0; i < resinfo->num_cols; i++) { curcol = resinfo->columns[i]; len = tds_dstr_len(&curcol->column_name); tds_put_byte(tds, tds_dstr_len(&curcol->column_name)); tds_put_n(tds, tds_dstr_cstr(&curcol->column_name), len); tds_put_byte(tds, '0'); tds_put_int(tds, curcol->column_usertype); tds_put_byte(tds, curcol->column_type); if (!is_fixed_type(curcol->column_type)) { tds_put_byte(tds, curcol->column_size); } tds_put_byte(tds, 0); } }
static int tds_read_conf_sections(FILE * in, const char *server, TDSLOGIN * login) { DSTR default_instance; int default_port; int found; tds_read_conf_section(in, "global", tds_parse_conf_section, login); if (!server[0]) return 0; rewind(in); tds_dstr_init(&default_instance); tds_dstr_dup(&default_instance, &login->instance_name); default_port = login->port; found = tds_read_conf_section(in, server, tds_parse_conf_section, login); /* * If both instance and port are specified and neither one came from the default, it's an error * TODO: If port/instance is specified in the non-default, it has priority over the default setting. * TODO: test this. */ if (!tds_dstr_isempty(&login->instance_name) && login->port && !(!tds_dstr_isempty(&default_instance) || default_port)) { tdsdump_log(TDS_DBG_ERROR, "error: cannot specify both port %d and instance %s.\n", login->port, tds_dstr_cstr(&login->instance_name)); /* tdserror(tds_get_ctx(tds), tds, TDSEPORTINSTANCE, 0); */ } tds_dstr_free(&default_instance); return found; }
static boolean reinit_results(TDSSOCKET * tds, size_t num_cols, const struct metadata_t meta[]) { TDSRESULTINFO *info; int i; assert(tds); assert(num_cols); assert(meta); tds_free_all_results(tds); tds->rows_affected = TDS_NO_COUNT; if ((info = alloc_results(num_cols)) == NULL) return false; tds_set_current_results(tds, info); if (tds->cur_cursor) { tds_free_results(tds->cur_cursor->res_info); tds->cur_cursor->res_info = info; tdsdump_log(TDS_DBG_INFO1, "set current_results to cursor->res_info\n"); } else { tds->res_info = info; tdsdump_log(TDS_DBG_INFO1, "set current_results (%u column%s) to tds->res_info\n", (unsigned) num_cols, (num_cols==1? "":"s")); } tdsdump_log(TDS_DBG_INFO1, "setting up %u columns\n", (unsigned) num_cols); for (i = 0; i < num_cols; i++) { set_result_column(tds, info->columns[i], meta[i].name, &meta[i].col); info->columns[i]->bcp_terminator = (char*) meta[i].pacross; /* overload available pointer */ } if (num_cols > 0) { static char dashes[31] = "------------------------------"; tdsdump_log(TDS_DBG_INFO1, " %-20s %-15s %-15s %-7s\n", "name", "size/wsize", "type/wtype", "utype"); tdsdump_log(TDS_DBG_INFO1, " %-20s %15s %15s %7s\n", dashes+10, dashes+30-15, dashes+30-15, dashes+30-7); } for (i = 0; i < num_cols; i++) { TDSCOLUMN *curcol = info->columns[i]; tdsdump_log(TDS_DBG_INFO1, " %-20s %7d/%-7d %7d/%-7d %7d\n", tds_dstr_cstr(&curcol->column_name), curcol->column_size, curcol->on_server.column_size, curcol->column_type, curcol->on_server.column_type, curcol->column_usertype); } #if 1 /* all done now allocate a row for tds_process_row to use */ if (TDS_FAILED(tds_alloc_row(info))) return false; #endif return true; }
/** * Help to build query to be sent to server. * Append column declaration to the query. * Only for TDS 7.0+. * \tds * \param[out] clause output string * \param bcpcol column to append * \param first true if column is the first * \return TDS_SUCCESS or TDS_FAIL. */ static TDSRET tds7_build_bulk_insert_stmt(TDSSOCKET * tds, TDSPBCB * clause, TDSCOLUMN * bcpcol, int first) { char column_type[40]; tdsdump_log(TDS_DBG_FUNC, "tds7_build_bulk_insert_stmt(%p, %p, %p, %d)\n", tds, clause, bcpcol, first); if (TDS_FAILED(tds_get_column_declaration(tds, bcpcol, column_type))) { tdserror(tds_get_ctx(tds), tds, TDSEBPROBADTYP, errno); tdsdump_log(TDS_DBG_FUNC, "error: cannot build bulk insert statement. unrecognized server datatype %d\n", bcpcol->on_server.column_type); return TDS_FAIL; } if (clause->cb < strlen(clause->pb) + tds_quote_id(tds, NULL, tds_dstr_cstr(&bcpcol->column_name), tds_dstr_len(&bcpcol->column_name)) + strlen(column_type) + ((first) ? 2u : 4u)) { char *temp = (char*) malloc(2 * clause->cb); if (!temp) { tdserror(tds_get_ctx(tds), tds, TDSEMEM, errno); return TDS_FAIL; } strcpy(temp, clause->pb); if (clause->from_malloc) free(clause->pb); clause->from_malloc = 1; clause->pb = temp; clause->cb *= 2; } if (!first) strcat(clause->pb, ", "); tds_quote_id(tds, strchr(clause->pb, 0), tds_dstr_cstr(&bcpcol->column_name), tds_dstr_len(&bcpcol->column_name)); strcat(clause->pb, " "); strcat(clause->pb, column_type); return TDS_SUCCESS; }
void dump_login(TDSLOGIN * login) { fprintf(stderr, "host %s\n", tds_dstr_cstr(&login->client_host_name)); fprintf(stderr, "user %s\n", tds_dstr_cstr(&login->user_name)); fprintf(stderr, "pass %s\n", tds_dstr_cstr(&login->password)); fprintf(stderr, "app %s\n", tds_dstr_cstr(&login->app_name)); fprintf(stderr, "srvr %s\n", tds_dstr_cstr(&login->server_name)); fprintf(stderr, "vers %d.%d\n", TDS_MAJOR(login), TDS_MINOR(login)); fprintf(stderr, "lib %s\n", tds_dstr_cstr(&login->library)); fprintf(stderr, "lang %s\n", tds_dstr_cstr(&login->language)); fprintf(stderr, "char %s\n", tds_dstr_cstr(&login->server_charset)); fprintf(stderr, "bsiz %d\n", login->block_size); }
static void dump_login(TDSLOGIN * login) { printf("host %s\n", tds_dstr_cstr(&login->client_host_name)); printf("user %s\n", tds_dstr_cstr(&login->user_name)); printf("pass %s\n", tds_dstr_cstr(&login->password)); printf("app %s\n", tds_dstr_cstr(&login->app_name)); printf("srvr %s\n", tds_dstr_cstr(&login->server_name)); printf("vers %d.%d\n", login->major_version, login->minor_version); printf("lib %s\n", tds_dstr_cstr(&login->library)); printf("lang %s\n", tds_dstr_cstr(&login->language)); printf("char %s\n", tds_dstr_cstr(&login->server_charset)); printf("bsiz %d\n", login->block_size); }
/** * Go looking for trouble. Return NULL if the info is okay, or an error message * if something needs to change. */ static const char * validate(DSNINFO * di) { if (!SQLValidDSN(tds_dstr_cstr(&di->dsn))) return "Invalid DSN"; if (!IS_TDS42(di->login) && !IS_TDS46(di->login) && !IS_TDS50(di->login) && !IS_TDS7_PLUS(di->login)) return "Bad Protocol version"; if (tds_dstr_isempty(&di->login->server_name)) return "Address is required"; if (di->login->port < 1 || di->login->port > 65535) return "Bad port - Try 1433 or 4000"; return NULL; }
static void tds_config_env_tdsdump(TDSLOGIN * login) { char *s; char *path; pid_t pid = 0; if ((s = getenv("TDSDUMP"))) { if (!strlen(s)) { pid = getpid(); if (asprintf(&path, pid_logpath, pid) >= 0) tds_dstr_set(&login->dump_file, path); } else { tds_dstr_copy(&login->dump_file, s); } tdsdump_log(TDS_DBG_INFO1, "Setting 'dump_file' to '%s' from $TDSDUMP.\n", tds_dstr_cstr(&login->dump_file)); } }
void tds_send_col_name(TDSSOCKET * tds, TDSRESULTINFO * resinfo) { int col, hdrsize = 0; TDSCOLUMN *curcol; tds_put_byte(tds, TDS_COLNAME_TOKEN); for (col = 0; col < resinfo->num_cols; col++) { curcol = resinfo->columns[col]; hdrsize += tds_dstr_len(&curcol->column_name) + 1; } tds_put_smallint(tds, hdrsize); for (col = 0; col < resinfo->num_cols; col++) { curcol = resinfo->columns[col]; tds_put_byte(tds, tds_dstr_len(&curcol->column_name)); /* exclude the null */ tds_put_n(tds, tds_dstr_cstr(&curcol->column_name), tds_dstr_len(&curcol->column_name)); } }
/** * Check the server name to find port info first * Warning: connection-> & login-> are all modified when needed * \return 1 when found, else 0 */ static int parse_server_name_for_port(TDSLOGIN * connection, TDSLOGIN * login) { const char *pSep; const char *server; /* seek the ':' in login server_name */ server = tds_dstr_cstr(&login->server_name); /* IPv6 address can be quoted */ if (server[0] == '[') { pSep = strstr(server, "]:"); if (pSep) ++pSep; } else { pSep = strrchr(server, ':'); } if (pSep && pSep != server) { /* yes, i found it! */ /* modify connection-> && login->server_name & ->port */ login->port = connection->port = atoi(pSep + 1); tds_dstr_empty(&connection->instance_name); } else { /* handle instance name */ pSep = strrchr(server, '\\'); if (!pSep || pSep == server) return 0; if (!tds_dstr_copy(&connection->instance_name, pSep + 1)) return 0; connection->port = 0; } if (!tds_dstr_copyn(&connection->server_name, server, pSep - server)) return 0; return 1; }
static TDSRET set_result_column(TDSSOCKET * tds, TDSCOLUMN * curcol, const char name[], const struct col_t *pvalue) { assert(curcol && pvalue); assert(name); curcol->column_usertype = pvalue->type; curcol->column_nullable = true; curcol->column_writeable = false; curcol->column_identity = false; tds_set_column_type(tds->conn, curcol, pvalue->type); /* sets "cardinal" type */ curcol->column_timestamp = (curcol->column_type == SYBBINARY && curcol->column_usertype == TDS_UT_TIMESTAMP); #if 0 curcol->funcs->get_info(tds, curcol); #endif curcol->on_server.column_size = curcol->column_size; if (!tds_dstr_copy(&curcol->column_name, name)) return TDS_FAIL; tdsdump_log(TDS_DBG_INFO1, "tds7_get_data_info: \n" "\tcolname = %s\n" "\ttype = %d (%s)\n" "\tserver's type = %d (%s)\n" "\tcolumn_varint_size = %d\n" "\tcolumn_size = %d (%d on server)\n", tds_dstr_cstr(&curcol->column_name), curcol->column_type, tds_prtype(curcol->column_type), curcol->on_server.column_type, tds_prtype(curcol->on_server.column_type), curcol->column_varint_size, curcol->column_size, curcol->on_server.column_size); return TDS_SUCCESS; }
void odbc_check_stmt_extra(TDS_STMT * stmt) { assert(stmt && stmt->htype == SQL_HANDLE_STMT); /* TODO deep check on connection */ assert(stmt->dbc); odbc_check_desc_extra(stmt->ard); odbc_check_desc_extra(stmt->ird); odbc_check_desc_extra(stmt->apd); odbc_check_desc_extra(stmt->ipd); assert(!stmt->prepared_query_is_func || stmt->prepared_query_is_rpc); assert(stmt->param_num <= stmt->param_count + 1); assert(stmt->num_param_rows >= 1); assert(stmt->curr_param_row >= 0); assert(stmt->curr_param_row <= stmt->num_param_rows); if (stmt->prepared_query_is_rpc) { const char *query = tds_dstr_cstr(&stmt->query); assert(query); assert(stmt->prepared_pos == NULL || (stmt->prepared_pos >= query && stmt->prepared_pos <= strchr(query,0))); } else { assert(stmt->prepared_pos == NULL); } /* TODO assert dbc has this statement in list */ }
static int tds_config_login(TDSLOGIN * connection, TDSLOGIN * login) { DSTR *res = &login->server_name; if (!tds_dstr_isempty(&login->server_name)) { if (1 || tds_dstr_isempty(&connection->server_name)) res = tds_dstr_dup(&connection->server_name, &login->server_name); } if (login->tds_version) connection->tds_version = login->tds_version; if (res && !tds_dstr_isempty(&login->language)) { res = tds_dstr_dup(&connection->language, &login->language); } if (res && !tds_dstr_isempty(&login->server_charset)) { res = tds_dstr_dup(&connection->server_charset, &login->server_charset); } if (res && !tds_dstr_isempty(&login->client_charset)) { res = tds_dstr_dup(&connection->client_charset, &login->client_charset); tdsdump_log(TDS_DBG_INFO1, "tds_config_login: %s is %s.\n", "client_charset", tds_dstr_cstr(&connection->client_charset)); } if (!login->use_utf16) connection->use_utf16 = login->use_utf16; if (res && !tds_dstr_isempty(&login->database)) { res = tds_dstr_dup(&connection->database, &login->database); tdsdump_log(TDS_DBG_INFO1, "tds_config_login: %s is %s.\n", "database_name", tds_dstr_cstr(&connection->database)); } if (res && !tds_dstr_isempty(&login->client_host_name)) { res = tds_dstr_dup(&connection->client_host_name, &login->client_host_name); } if (res && !tds_dstr_isempty(&login->app_name)) { res = tds_dstr_dup(&connection->app_name, &login->app_name); } if (res && !tds_dstr_isempty(&login->user_name)) { res = tds_dstr_dup(&connection->user_name, &login->user_name); } if (res && !tds_dstr_isempty(&login->password)) { /* for security reason clear memory */ tds_dstr_zero(&connection->password); res = tds_dstr_dup(&connection->password, &login->password); } if (res && !tds_dstr_isempty(&login->library)) { res = tds_dstr_dup(&connection->library, &login->library); } if (login->encryption_level) { connection->encryption_level = login->encryption_level; } if (login->suppress_language) { connection->suppress_language = 1; } if (login->bulk_copy) { connection->bulk_copy = 1; } if (login->block_size) { connection->block_size = login->block_size; } if (login->port) connection->port = login->port; if (login->connect_timeout) connection->connect_timeout = login->connect_timeout; if (login->query_timeout) connection->query_timeout = login->query_timeout; if (!login->check_ssl_hostname) connection->check_ssl_hostname = login->check_ssl_hostname; if (res && !tds_dstr_isempty(&login->db_filename)) { res = tds_dstr_dup(&connection->db_filename, &login->db_filename); } /* copy other info not present in configuration file */ connection->capabilities = login->capabilities; if (login->readonly_intent) connection->readonly_intent = login->readonly_intent; connection->use_new_password = login->use_new_password; if (res) res = tds_dstr_dup(&connection->new_password, &login->new_password); return res != NULL; }
static int tds_verify_certificate(gnutls_session_t session) { unsigned int status; int ret; TDSSOCKET *tds = (TDSSOCKET *) gnutls_transport_get_ptr(session); #ifdef ENABLE_DEVELOPING unsigned int list_size; const gnutls_datum_t *cert_list; #endif if (!tds->login) return GNUTLS_E_CERTIFICATE_ERROR; ret = gnutls_certificate_verify_peers2(session, &status); if (ret < 0) { tdsdump_log(TDS_DBG_ERROR, "Error verifying certificate: %s\n", gnutls_strerror(ret)); return GNUTLS_E_CERTIFICATE_ERROR; } #ifdef ENABLE_DEVELOPING cert_list = gnutls_certificate_get_peers(session, &list_size); if (cert_list) { gnutls_x509_crt_t cert; gnutls_datum_t cinfo; char buf[8192]; size_t size; gnutls_x509_crt_init(&cert); gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); /* This is the preferred way of printing short information about * a certificate. */ size = sizeof(buf); ret = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, buf, &size); if (ret == 0) { FILE *f = fopen("cert.dat", "wb"); if (f) { fwrite(buf, size, 1, f); fclose(f); } } ret = gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_ONELINE, &cinfo); if (ret == 0) { tdsdump_log(TDS_DBG_INFO1, "Certificate info: %s\n", cinfo.data); gnutls_free(cinfo.data); } gnutls_x509_crt_deinit(cert); } #endif /* Certificate is not trusted */ if (status != 0) { tdsdump_log(TDS_DBG_ERROR, "Certificate status: %u\n", status); return GNUTLS_E_CERTIFICATE_ERROR; } /* check hostname */ if (tds->login->check_ssl_hostname) { const gnutls_datum_t *cert_list; unsigned int list_size; gnutls_x509_crt_t cert; cert_list = gnutls_certificate_get_peers(session, &list_size); if (!cert_list) { tdsdump_log(TDS_DBG_ERROR, "Error getting TLS session peers\n"); return GNUTLS_E_CERTIFICATE_ERROR; } gnutls_x509_crt_init(&cert); gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); ret = gnutls_x509_crt_check_hostname(cert, tds_dstr_cstr(&tds->login->server_host_name)); gnutls_x509_crt_deinit(cert); if (!ret) { tdsdump_log(TDS_DBG_ERROR, "Certificate hostname does not match\n"); return GNUTLS_E_CERTIFICATE_ERROR; } } /* notify gnutls to continue handshake normally */ return 0; }
/** * tds_read_config_info() will fill the tds connection structure based on configuration * information gathered in the following order: * 1) Program specified in TDSLOGIN structure * 2) The environment variables TDSVER, TDSDUMP, TDSPORT, TDSQUERY, TDSHOST * 3) A config file with the following search order: * a) a readable file specified by environment variable FREETDSCONF * b) a readable file in ~/.freetds.conf * c) a readable file in $prefix/etc/freetds.conf * 3) ~/.interfaces if exists * 4) $SYBASE/interfaces if exists * 5) TDS_DEF_* default values * * .tdsrc and freetds.conf have been added to make the package easier to * integration with various Linux and *BSD distributions. */ TDSLOGIN * tds_read_config_info(TDSSOCKET * tds, TDSLOGIN * login, TDSLOCALE * locale) { TDSLOGIN *connection; char *s; char *path; pid_t pid; int opened = 0, found; struct addrinfo *addrs; /* allocate a new structure with hard coded and build-time defaults */ connection = tds_alloc_login(0); if (!connection || !tds_init_login(connection, locale)) { tds_free_login(connection); return NULL; } s = getenv("TDSDUMPCONFIG"); if (s) { if (*s) { opened = tdsdump_open(s); } else { pid = getpid(); if (asprintf(&path, pid_config_logpath, pid) >= 0) { if (*path) { opened = tdsdump_open(path); } free(path); } } } tdsdump_log(TDS_DBG_INFO1, "Getting connection information for [%s].\n", tds_dstr_cstr(&login->server_name)); /* (The server name is set in login.c.) */ /* Read the config files. */ tdsdump_log(TDS_DBG_INFO1, "Attempting to read conf files.\n"); found = tds_read_conf_file(connection, tds_dstr_cstr(&login->server_name)); if (!found) { if (parse_server_name_for_port(connection, login)) { found = tds_read_conf_file(connection, tds_dstr_cstr(&connection->server_name)); /* do it again to really override what found in freetds.conf */ if (found) { parse_server_name_for_port(connection, login); } else if (TDS_SUCCEED(tds_lookup_host_set(tds_dstr_cstr(&connection->server_name), &connection->ip_addrs))) { if (!tds_dstr_dup(&connection->server_host_name, &connection->server_name)) { tds_free_login(connection); return NULL; } found = 1; } } } if (!found) { /* fallback to interfaces file */ tdsdump_log(TDS_DBG_INFO1, "Failed in reading conf file. Trying interface files.\n"); if (!tds_read_interfaces(tds_dstr_cstr(&login->server_name), connection)) { tdsdump_log(TDS_DBG_INFO1, "Failed to find [%s] in configuration files; trying '%s' instead.\n", tds_dstr_cstr(&login->server_name), tds_dstr_cstr(&connection->server_name)); if (connection->ip_addrs == NULL) tdserror(tds_get_ctx(tds), tds, TDSEINTF, 0); } } /* Override config file settings with environment variables. */ tds_fix_login(connection); /* And finally apply anything from the login structure */ if (!tds_config_login(connection, login)) { tds_free_login(connection); return NULL; } if (opened) { char tmp[128]; tdsdump_log(TDS_DBG_INFO1, "Final connection parameters:\n"); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "server_name", tds_dstr_cstr(&connection->server_name)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "server_host_name", tds_dstr_cstr(&connection->server_host_name)); for (addrs = connection->ip_addrs; addrs != NULL; addrs = addrs->ai_next) tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "ip_addr", tds_addrinfo2str(addrs, tmp, sizeof(tmp))); if (connection->ip_addrs == NULL) tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "ip_addr", ""); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "instance_name", tds_dstr_cstr(&connection->instance_name)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "port", connection->port); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "major_version", TDS_MAJOR(connection)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "minor_version", TDS_MINOR(connection)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "block_size", connection->block_size); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "language", tds_dstr_cstr(&connection->language)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "server_charset", tds_dstr_cstr(&connection->server_charset)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "connect_timeout", connection->connect_timeout); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "client_host_name", tds_dstr_cstr(&connection->client_host_name)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "client_charset", tds_dstr_cstr(&connection->client_charset)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "use_utf16", connection->use_utf16); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "app_name", tds_dstr_cstr(&connection->app_name)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "user_name", tds_dstr_cstr(&connection->user_name)); /* tdsdump_log(TDS_DBG_PASSWD, "\t%20s = %s\n", "password", tds_dstr_cstr(&connection->password)); (no such flag yet) */ tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "library", tds_dstr_cstr(&connection->library)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "bulk_copy", (int)connection->bulk_copy); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "suppress_language", (int)connection->suppress_language); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "encrypt level", (int)connection->encryption_level); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "query_timeout", connection->query_timeout); /* tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "capabilities", tds_dstr_cstr(&connection->capabilities)); (not null terminated) */ tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "database", tds_dstr_cstr(&connection->database)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "dump_file", tds_dstr_cstr(&connection->dump_file)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %x\n", "debug_flags", connection->debug_flags); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "text_size", connection->text_size); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "emul_little_endian", connection->emul_little_endian); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "server_realm_name", tds_dstr_cstr(&connection->server_realm_name)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "server_spn", tds_dstr_cstr(&connection->server_spn)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "cafile", tds_dstr_cstr(&connection->cafile)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "crlfile", tds_dstr_cstr(&connection->crlfile)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "check_ssl_hostname", connection->check_ssl_hostname); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "db_filename", tds_dstr_cstr(&connection->db_filename)); tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "readonly_intent", connection->readonly_intent); tdsdump_close(); } /* * If a dump file has been specified, start logging */ if (!tds_dstr_isempty(&connection->dump_file) && !tdsdump_isopen()) { if (connection->debug_flags) tds_debug_flags = connection->debug_flags; tdsdump_open(tds_dstr_cstr(&connection->dump_file)); } return connection; }
/* Also used to scan ODBC.INI entries */ void tds_parse_conf_section(const char *option, const char *value, void *param) { TDSLOGIN *login = (TDSLOGIN *) param; void *s = param; tdsdump_log(TDS_DBG_INFO1, "\t%s = '%s'\n", option, value); if (!strcmp(option, TDS_STR_VERSION)) { tds_config_verstr(value, login); } else if (!strcmp(option, TDS_STR_BLKSZ)) { int val = atoi(value); if (val >= 512 && val < 65536) login->block_size = val; } else if (!strcmp(option, TDS_STR_SWAPDT)) { /* this option is deprecated, just check value for compatibility */ tds_config_boolean(option, value, login); } else if (!strcmp(option, TDS_GSSAPI_DELEGATION)) { /* gssapi flag addition */ login->gssapi_use_delegation = tds_config_boolean(option, value, login); } else if (!strcmp(option, TDS_STR_DUMPFILE)) { s = tds_dstr_copy(&login->dump_file, value); } else if (!strcmp(option, TDS_STR_DEBUGFLAGS)) { char *end; long flags; flags = strtol(value, &end, 0); if (*value != '\0' && *end == '\0' && flags != LONG_MIN && flags != LONG_MAX) login->debug_flags = flags; } else if (!strcmp(option, TDS_STR_TIMEOUT) || !strcmp(option, TDS_STR_QUERY_TIMEOUT)) { if (atoi(value)) login->query_timeout = atoi(value); } else if (!strcmp(option, TDS_STR_CONNTIMEOUT)) { if (atoi(value)) login->connect_timeout = atoi(value); } else if (!strcmp(option, TDS_STR_HOST)) { char tmp[128]; struct addrinfo *addrs; if (TDS_FAILED(tds_lookup_host_set(value, &login->ip_addrs))) { tdsdump_log(TDS_DBG_WARN, "Found host entry %s however name resolution failed. \n", value); return; } tdsdump_log(TDS_DBG_INFO1, "Found host entry %s \n", value); s = tds_dstr_copy(&login->server_host_name, value); for (addrs = login->ip_addrs; addrs != NULL; addrs = addrs->ai_next) tdsdump_log(TDS_DBG_INFO1, "IP addr is %s.\n", tds_addrinfo2str(addrs, tmp, sizeof(tmp))); } else if (!strcmp(option, TDS_STR_PORT)) { if (atoi(value)) login->port = atoi(value); } else if (!strcmp(option, TDS_STR_EMUL_LE)) { login->emul_little_endian = tds_config_boolean(option, value, login); } else if (!strcmp(option, TDS_STR_TEXTSZ)) { if (atoi(value)) login->text_size = atoi(value); } else if (!strcmp(option, TDS_STR_CHARSET)) { s = tds_dstr_copy(&login->server_charset, value); tdsdump_log(TDS_DBG_INFO1, "%s is %s.\n", option, tds_dstr_cstr(&login->server_charset)); } else if (!strcmp(option, TDS_STR_CLCHARSET)) { s = tds_dstr_copy(&login->client_charset, value); tdsdump_log(TDS_DBG_INFO1, "tds_parse_conf_section: %s is %s.\n", option, tds_dstr_cstr(&login->client_charset)); } else if (!strcmp(option, TDS_STR_USE_UTF_16)) { login->use_utf16 = tds_config_boolean(option, value, login); } else if (!strcmp(option, TDS_STR_LANGUAGE)) { s = tds_dstr_copy(&login->language, value); } else if (!strcmp(option, TDS_STR_APPENDMODE)) { tds_g_append_mode = tds_config_boolean(option, value, login); } else if (!strcmp(option, TDS_STR_INSTANCE)) { s = tds_dstr_copy(&login->instance_name, value); } else if (!strcmp(option, TDS_STR_ENCRYPTION)) { tds_config_encryption(value, login); } else if (!strcmp(option, TDS_STR_ASA_DATABASE)) { s = tds_dstr_copy(&login->server_name, value); } else if (!strcmp(option, TDS_STR_USENTLMV2)) { login->use_ntlmv2 = tds_config_boolean(option, value, login); } else if (!strcmp(option, TDS_STR_USELANMAN)) { login->use_lanman = tds_config_boolean(option, value, login); } else if (!strcmp(option, TDS_STR_REALM)) { s = tds_dstr_copy(&login->server_realm_name, value); } else if (!strcmp(option, TDS_STR_SPN)) { s = tds_dstr_copy(&login->server_spn, value); } else if (!strcmp(option, TDS_STR_CAFILE)) { s = tds_dstr_copy(&login->cafile, value); } else if (!strcmp(option, TDS_STR_CRLFILE)) { s = tds_dstr_copy(&login->crlfile, value); } else if (!strcmp(option, TDS_STR_CHECKSSLHOSTNAME)) { login->check_ssl_hostname = tds_config_boolean(option, value, login); } else if (!strcmp(option, TDS_STR_DBFILENAME)) { s = tds_dstr_copy(&login->db_filename, value); } else if (!strcmp(option, TDS_STR_DATABASE)) { s = tds_dstr_copy(&login->database, value); } else if (!strcmp(option, TDS_STR_READONLY_INTENT)) { login->readonly_intent = tds_config_boolean(option, value, login); tdsdump_log(TDS_DBG_FUNC, "Setting ReadOnly Intent to '%s'.\n", value); } else { tdsdump_log(TDS_DBG_INFO1, "UNRECOGNIZED option '%s' ... ignoring.\n", option); } if (!s) login->valid_configuration = 0; }
/** * Build a GSSAPI packet to send to server * @param tds A pointer to the TDSSOCKET structure managing a client/server operation. * @param packet GSSAPI packet build from function * @return size of packet */ TDSAUTHENTICATION * tds_gss_get_auth(TDSSOCKET * tds) { /* * TODO * There are some differences between this implementation and MS on * - MS use SPNEGO with 3 mechnisms (MS KRB5, KRB5, NTLMSSP) * - MS seems to use MUTUAL flag * - name type is "Service and Instance (2)" and not "Principal (1)" * check for memory leaks * check for errors in many functions * a bit more verbose * dinamically load library ?? */ gss_buffer_desc send_tok; OM_uint32 maj_stat, min_stat; /* same as GSS_KRB5_NT_PRINCIPAL_NAME but do not require .so library */ static gss_OID_desc nt_principal = { 10, (void*) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01" }; const char *server_name; /* Storage for getaddrinfo calls */ struct addrinfo *addrs = NULL; struct tds_gss_auth *auth = (struct tds_gss_auth *) calloc(1, sizeof(struct tds_gss_auth)); if (!auth || !tds->login) return NULL; auth->tds_auth.free = tds_gss_free; auth->tds_auth.handle_next = tds_gss_handle_next; auth->gss_context = GSS_C_NO_CONTEXT; auth->last_stat = GSS_S_COMPLETE; server_name = tds_dstr_cstr(&tds->login->server_host_name); if (strchr(server_name, '.') == NULL) { struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_V4MAPPED|AI_ADDRCONFIG|AI_CANONNAME|AI_FQDN; if (!getaddrinfo(server_name, NULL, &hints, &addrs) && addrs->ai_canonname && strchr(addrs->ai_canonname, '.') != NULL) server_name = addrs->ai_canonname; } if (!tds_dstr_isempty(&tds->login->server_spn)) { auth->sname = strdup(tds_dstr_cstr(&tds->login->server_spn)); } else if (tds_dstr_isempty(&tds->login->server_realm_name)) { if (asprintf(&auth->sname, "MSSQLSvc/%s:%d", server_name, tds->login->port) < 0) auth->sname = NULL; } else { if (asprintf(&auth->sname, "MSSQLSvc/%s:%d@%s", server_name, tds->login->port, tds_dstr_cstr(&tds->login->server_realm_name)) < 0) auth->sname = NULL; } if (addrs) freeaddrinfo(addrs); if (auth->sname == NULL) { tds_gss_free(tds->conn, (TDSAUTHENTICATION *) auth); return NULL; } tdsdump_log(TDS_DBG_NETWORK, "using kerberos name %s\n", auth->sname); /* * Import the name into target_name. Use send_tok to save * local variable space. */ send_tok.value = auth->sname; send_tok.length = strlen(auth->sname); maj_stat = gss_import_name(&min_stat, &send_tok, &nt_principal, &auth->target_name); switch (maj_stat) { case GSS_S_COMPLETE: tdsdump_log(TDS_DBG_NETWORK, "gss_import_name: GSS_S_COMPLETE: gss_import_name completed successfully.\n"); if (TDS_FAILED(tds_gss_continue(tds, auth, GSS_C_NO_BUFFER))) { tds_gss_free(tds->conn, (TDSAUTHENTICATION *) auth); return NULL; } break; case GSS_S_BAD_NAMETYPE: tdsdump_log(TDS_DBG_NETWORK, "gss_import_name: GSS_S_BAD_NAMETYPE: The input_name_type was unrecognized.\n"); break; case GSS_S_BAD_NAME: tdsdump_log(TDS_DBG_NETWORK, "gss_import_name: GSS_S_BAD_NAME: The input_name parameter could not be interpreted as a name of the specified type.\n"); break; case GSS_S_BAD_MECH: tdsdump_log(TDS_DBG_NETWORK, "gss_import_name: GSS_S_BAD_MECH: The input name-type was GSS_C_NT_EXPORT_NAME, but the mechanism contained within the input-name is not supported.\n"); break; default: tdsdump_log(TDS_DBG_NETWORK, "gss_import_name: unexpected error %d.\n", maj_stat); break; } if (GSS_ERROR(maj_stat)) { tds_gss_free(tds->conn, (TDSAUTHENTICATION *) auth); return NULL; } return (TDSAUTHENTICATION *) auth; }
static TDSRET tds5_negotiate_handle_next(TDSSOCKET * tds, TDSAUTHENTICATION * tds_auth, size_t len) { TDS5NEGOTIATE *auth = (TDS5NEGOTIATE *) tds_auth; TDSPARAMINFO *info; void *rsa, *nonce = NULL; size_t rsa_len, nonce_len = 0; void *em; size_t em_size; TDSRET rc = TDS_FAIL; /* send next data for authentication */ if (!tds->login) goto error; /* we only support RSA authentication, we should have send 2/3 parameters: * 1- integer.. unknown actually 1 TODO * 2- binary, rsa public key in PEM format * 3- binary, nonce (optional) */ /* message not supported */ if (auth->msg_type != 0x1e) goto error; info = tds->param_info; if (!info || info->num_cols < 2) goto error; if (info->columns[1]->column_type != SYBLONGBINARY) goto error; if (info->num_cols >= 3 && info->columns[2]->column_type != SYBLONGBINARY) goto error; rsa = ((TDSBLOB*) info->columns[1]->column_data)->textvalue; rsa_len = info->columns[1]->column_size; if (info->num_cols >= 3) { nonce = ((TDSBLOB*) info->columns[2]->column_data)->textvalue; nonce_len = info->columns[2]->column_size; } em = tds5_rsa_encrypt(rsa, rsa_len, nonce, nonce_len, tds_dstr_cstr(&tds->login->password), &em_size); if (!em) goto error; tds->out_flag = TDS_NORMAL; /* password */ tds5_send_msg(tds, 0x1f); tds_put_n(tds, "\xec\x0e\x00\x01\x00\x00\x00\x00\x00\x00\x00\xe1\xff\xff\xff\x7f\x00", 0x11); tds_put_byte(tds, TDS5_PARAMS_TOKEN); tds_put_int(tds, em_size); tds_put_n(tds, em, em_size); /* remote password */ tds5_send_msg(tds, 0x20); tds_put_n(tds, "\xec\x17\x00\x02\x00\x00\x00\x00\x00\x00\x00\x27\xff\x00\x00\x00\x00\x00\x00\x00\xe1\xff\xff\xff\x7f\x00", 0x1a); tds_put_byte(tds, TDS5_PARAMS_TOKEN); tds_put_byte(tds, 0); tds_put_int(tds, em_size); tds_put_n(tds, em, em_size); free(em); rc = tds_flush_packet(tds); error: tds5_negotiate_free(tds->conn, tds_auth); tds->conn->authentication = NULL; return rc; }
/* * pool_user_login * Reads clients login packet and forges a login acknowledgement sequence */ static bool pool_user_login(TDS_POOL * pool, TDS_POOL_USER * puser) { TDSSOCKET *tds; TDSLOGIN *login; tds = puser->sock.tds; while (tds->in_len <= tds->in_pos) if (tds_read_packet(tds) < 0) return false; tdsdump_log(TDS_DBG_NETWORK, "got packet type %d\n", tds->in_flag); if (tds->in_flag == TDS71_PRELOGIN) { if (!tds->conn->tds_version) tds->conn->tds_version = 0x701; tds->out_flag = TDS_REPLY; // TODO proper one !! // TODO detect TDS version here ?? tds_put_n(tds, "\x00\x00\x1a\x00\x06" /* version */ "\x01\x00\x20\x00\x01" /* encryption */ "\x02\x00\x21\x00\x01" /* instance ?? */ "\x03\x00\x22\x00\x00" /* process id ?? */ "\x04\x00\x22\x00\x01" /* MARS */ "\xff" "\x0a\x00\x06\x40\x00\x00" "\x02" "\x01" "" "\x00", 0x23); tds_flush_packet(tds); /* read another packet */ tds->in_pos = tds->in_len; while (tds->in_len <= tds->in_pos) if (tds_read_packet(tds) < 0) return false; } puser->login = login = tds_alloc_login(1); if (tds->in_flag == TDS_LOGIN) { if (!tds->conn->tds_version) tds->conn->tds_version = 0x500; tds_read_login(tds, login); } else if (tds->in_flag == TDS7_LOGIN) { if (!tds->conn->tds_version) tds->conn->tds_version = 0x700; if (!tds7_read_login(tds, login)) return false; } else { return false; } /* check we support version required */ // TODO function to check it if (!IS_TDS71_PLUS(login)) return false; tds->in_len = tds->in_pos = 0; dump_login(login); if (strcmp(tds_dstr_cstr(&login->user_name), pool->user) != 0 || strcmp(tds_dstr_cstr(&login->password), pool->password) != 0) /* TODO send nack before exiting */ return false; return true; }
static void tds_config_login(TDSLOGIN * connection, TDSLOGIN * login) { if (!tds_dstr_isempty(&login->server_name)) { if (1 || tds_dstr_isempty(&connection->server_name)) tds_dstr_dup(&connection->server_name, &login->server_name); } if (login->tds_version) connection->tds_version = login->tds_version; if (!tds_dstr_isempty(&login->language)) { tds_dstr_dup(&connection->language, &login->language); } if (!tds_dstr_isempty(&login->server_charset)) { tds_dstr_dup(&connection->server_charset, &login->server_charset); } if (!tds_dstr_isempty(&login->client_charset)) { tds_dstr_dup(&connection->client_charset, &login->client_charset); tdsdump_log(TDS_DBG_INFO1, "tds_config_login: %s is %s.\n", "client_charset", tds_dstr_cstr(&connection->client_charset)); } if (!tds_dstr_isempty(&login->database)) { tds_dstr_dup(&connection->database, &login->database); tdsdump_log(TDS_DBG_INFO1, "tds_config_login: %s is %s.\n", "database_name", tds_dstr_cstr(&connection->database)); } if (!tds_dstr_isempty(&login->client_host_name)) { tds_dstr_dup(&connection->client_host_name, &login->client_host_name); } if (!tds_dstr_isempty(&login->app_name)) { tds_dstr_dup(&connection->app_name, &login->app_name); } if (!tds_dstr_isempty(&login->user_name)) { tds_dstr_dup(&connection->user_name, &login->user_name); } if (!tds_dstr_isempty(&login->password)) { /* for security reason clear memory */ tds_dstr_zero(&connection->password); tds_dstr_dup(&connection->password, &login->password); } if (!tds_dstr_isempty(&login->library)) { tds_dstr_dup(&connection->library, &login->library); } if (login->encryption_level) { connection->encryption_level = login->encryption_level; } if (login->suppress_language) { connection->suppress_language = 1; } if (login->bulk_copy) { connection->bulk_copy = 1; } if (login->block_size) { connection->block_size = login->block_size; } if (login->port) connection->port = login->port; if (login->connect_timeout) connection->connect_timeout = login->connect_timeout; if (login->query_timeout) connection->query_timeout = login->query_timeout; /* copy other info not present in configuration file */ connection->capabilities = login->capabilities; }
bool pool_user_send_login_ack(TDS_POOL * pool, TDS_POOL_USER * puser) { char msg[256]; char block[32]; TDSSOCKET *tds = puser->sock.tds, *mtds = puser->assigned_member->sock.tds; TDSLOGIN *login = puser->login; const char *database; const char *server = mtds->conn->server ? mtds->conn->server : "JDBC"; bool dbname_mismatch, odbc_mismatch; pool->user_logins++; /* copy a bit of information, resize socket with block */ tds->conn->tds_version = mtds->conn->tds_version; tds->conn->product_version = mtds->conn->product_version; memcpy(tds->conn->collation, mtds->conn->collation, sizeof(tds->conn->collation)); tds->conn->tds71rev1 = mtds->conn->tds71rev1; free(tds->conn->product_name); tds->conn->product_name = strdup(mtds->conn->product_name); tds_realloc_socket(tds, mtds->conn->env.block_size); tds->conn->env.block_size = mtds->conn->env.block_size; tds->conn->client_spid = mtds->conn->spid; /* if database is different use USE statement */ database = pool->database; dbname_mismatch = !tds_dstr_isempty(&login->database) && strcasecmp(tds_dstr_cstr(&login->database), database) != 0; odbc_mismatch = (login->option_flag2 & TDS_ODBC_ON) == 0; if (dbname_mismatch || odbc_mismatch) { char *str; int len = 128 + tds_quote_id(mtds, NULL, tds_dstr_cstr(&login->database),-1); TDSRET ret; if ((str = (char *) malloc(len)) == NULL) return false; str[0] = 0; /* swicth to dblib options */ if (odbc_mismatch) strcat(str, "SET ANSI_DEFAULTS OFF\nSET CONCAT_NULL_YIELDS_NULL OFF\n"); if (dbname_mismatch) { strcat(str, "USE "); tds_quote_id(mtds, strchr(str, 0), tds_dstr_cstr(&login->database), -1); } ret = tds_submit_query(mtds, str); free(str); if (TDS_FAILED(ret) || TDS_FAILED(tds_process_simple_query(mtds))) return false; if (dbname_mismatch) database = tds_dstr_cstr(&login->database); else database = mtds->conn->env.database; } // 7.0 // env database // database change message (with server name correct) // env language // language change message // env 0x3 charset ("iso_1") // env 0x5 lcid ("1033") // env 0x6 ("196609" ?? 0x30001) // loginack // env 0x4 packet size // done // // 7.1/7.2/7.3 // env database // database change message (with server name correct) // env 0x7 collation // env language // language change message // loginack // env 0x4 packet size // done tds->out_flag = TDS_REPLY; tds_env_change(tds, TDS_ENV_DATABASE, "master", database); sprintf(msg, "Changed database context to '%s'.", database); tds_send_msg(tds, 5701, 2, 0, msg, server, NULL, 1); if (!login->suppress_language) { tds_env_change(tds, TDS_ENV_LANG, NULL, "us_english"); tds_send_msg(tds, 5703, 1, 0, "Changed language setting to 'us_english'.", server, NULL, 1); } if (IS_TDS71_PLUS(tds->conn)) { tds_put_byte(tds, TDS_ENVCHANGE_TOKEN); tds_put_smallint(tds, 8); tds_put_byte(tds, TDS_ENV_SQLCOLLATION); tds_put_byte(tds, 5); tds_put_n(tds, tds->conn->collation, 5); tds_put_byte(tds, 0); } tds_send_login_ack(tds, mtds->conn->product_name); sprintf(block, "%d", tds->conn->env.block_size); tds_env_change(tds, TDS_ENV_PACKSIZE, block, block); /* tds_send_capabilities_token(tds); */ tds_send_done_token(tds, 0, 0); /* send it! */ tds_flush_packet(tds); tds_free_login(login); puser->login = NULL; return true; }
int main(int argc, char **argv) { TDSLOGIN *login; TDSSOCKET *tds; int verbose = 0; int num_cols = 2; TDS_INT result_type; int rc; int i, done_flags; fprintf(stdout, "%s: Test basic submit query, results\n", __FILE__); rc = try_tds_login(&login, &tds, __FILE__, verbose); if (rc != TDS_SUCCESS) { fprintf(stderr, "try_tds_login() failed\n"); return 1; } rc = tds_submit_query(tds, "select db_name() dbname, user_name() username"); if (rc != TDS_SUCCESS) { fprintf(stderr, "tds_submit_query() failed\n"); return 1; } while ((rc = tds_process_tokens(tds, &result_type, &done_flags, TDS_TOKEN_RESULTS)) == TDS_SUCCESS) { switch (result_type) { case TDS_ROWFMT_RESULT: if (tds->res_info->num_cols != num_cols) { fprintf(stderr, "Error: num_cols != %d in %s\n", num_cols, __FILE__); return 1; } if (tds->res_info->columns[0]->column_type != SYBVARCHAR || tds->res_info->columns[1]->column_type != SYBVARCHAR) { fprintf(stderr, "Wrong column_type in %s\n", __FILE__); return 1; } if (strcmp(tds_dstr_cstr(&tds->res_info->columns[0]->column_name), "dbname") || strcmp(tds_dstr_cstr(&tds->res_info->columns[1]->column_name), "username")) { fprintf(stderr, "Wrong column_name in %s\n", __FILE__); return 1; } break; case TDS_ROW_RESULT: while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE)) == TDS_SUCCESS) { if (result_type != TDS_ROW_RESULT || result_type != TDS_COMPUTE_RESULT) break; if (verbose) { for (i = 0; i < num_cols; i++) { printf("col %i is %s\n", i, value_as_string(tds, i)); } } } if (rc != TDS_SUCCESS) { fprintf(stderr, "tds_process_tokens() unexpected return\n"); } break; case TDS_DONE_RESULT: case TDS_DONEPROC_RESULT: case TDS_DONEINPROC_RESULT: if (!(done_flags & TDS_DONE_ERROR)) break; default: fprintf(stderr, "tds_process_tokens() unexpected result_type\n"); break; } } if (rc != TDS_NO_MORE_RESULTS) { fprintf(stderr, "tds_process_tokens() unexpected return\n"); } try_tds_logout(login, tds, verbose); return 0; }
/** * Build a SSPI packet to send to server * @param tds A pointer to the TDSSOCKET structure managing a client/server operation. */ TDSAUTHENTICATION * tds_sspi_get_auth(TDSSOCKET * tds) { SecBuffer buf; SecBufferDesc desc; SECURITY_STATUS status; ULONG attrs; TimeStamp ts; SEC_WINNT_AUTH_IDENTITY identity; const char *p, *user_name, *server_name; TDSSSPIAUTH *auth; TDSCONNECTION *connection = tds->connection; /* check connection */ if (!connection) return NULL; if (!tds_init_secdll()) return NULL; /* parse username/password informations */ memset(&identity, 0, sizeof(identity)); user_name = tds_dstr_cstr(&connection->user_name); if ((p = strchr(user_name, '\\')) != NULL) { identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; identity.Password = (void *) tds_dstr_cstr(&connection->password); identity.PasswordLength = tds_dstr_len(&connection->password); identity.Domain = (void *) user_name; identity.DomainLength = p - user_name; user_name = p + 1; identity.User = (void *) user_name; identity.UserLength = strlen(user_name); } auth = (TDSSSPIAUTH *) calloc(1, sizeof(TDSSSPIAUTH)); if (!auth || !tds->connection) return NULL; auth->tds_auth.free = tds_sspi_free; auth->tds_auth.handle_next = tds_sspi_handle_next; /* using Negotiate system will use proper protocol (either NTLM or Kerberos) */ if (sec_fn->AcquireCredentialsHandleA(NULL, (char *)"Negotiate", SECPKG_CRED_OUTBOUND, NULL, identity.Domain ? &identity : NULL, NULL, NULL, &auth->cred, &ts) != SEC_E_OK) { free(auth); return NULL; } /* allocate buffer */ auth->tds_auth.packet = (TDS_UCHAR *) malloc(NTLMBUF_LEN); if (!auth->tds_auth.packet) { sec_fn->FreeCredentialsHandle(&auth->cred); free(auth); return NULL; } desc.ulVersion = SECBUFFER_VERSION; desc.cBuffers = 1; desc.pBuffers = &buf; buf.cbBuffer = NTLMBUF_LEN; buf.BufferType = SECBUFFER_TOKEN; buf.pvBuffer = auth->tds_auth.packet; /* build SPN */ server_name = tds_dstr_cstr(&connection->server_host_name); if (strchr(server_name, '.') == NULL) { struct hostent *host = gethostbyname(server_name); if (host && strchr(host->h_name, '.') != NULL) server_name = host->h_name; } if (strchr(server_name, '.') != NULL) { if (asprintf(&auth->sname, "MSSQLSvc/%s:%d", server_name, connection->port) < 0) { free(auth->tds_auth.packet); sec_fn->FreeCredentialsHandle(&auth->cred); free(auth); return NULL; } tdsdump_log(TDS_DBG_NETWORK, "kerberos name %s\n", auth->sname); } status = sec_fn->InitializeSecurityContextA(&auth->cred, NULL, auth->sname, ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION, 0, SECURITY_NETWORK_DREP, NULL, 0, &auth->cred_ctx, &desc, &attrs, &ts); if (status == SEC_I_COMPLETE_AND_CONTINUE || status == SEC_I_CONTINUE_NEEDED) { sec_fn->CompleteAuthToken(&auth->cred_ctx, &desc); } else if(status != SEC_E_OK) { free(auth->sname); free(auth->tds_auth.packet); sec_fn->FreeCredentialsHandle(&auth->cred); free(auth); return NULL; } auth->tds_auth.packet_len = buf.cbBuffer; return &auth->tds_auth; }
TDSRET tds_ssl_init(TDSSOCKET *tds) { gnutls_session_t session; gnutls_certificate_credentials_t xcred; int ret; const char *tls_msg; xcred = NULL; session = NULL; tls_msg = "initializing tls"; if (!tls_initialized) { ret = 0; tds_mutex_lock(&tls_mutex); if (!tls_initialized) { tds_gcry_init(); ret = gnutls_global_init(); if (ret == 0) tls_initialized = 1; } tds_mutex_unlock(&tls_mutex); if (ret != 0) goto cleanup; } if (tds_write_dump && tls_initialized < 2) { gnutls_global_set_log_level(11); gnutls_global_set_log_function(tds_tls_log); tls_initialized = 2; } tls_msg = "allocating credentials"; ret = gnutls_certificate_allocate_credentials(&xcred); if (ret != 0) goto cleanup; if (!tds_dstr_isempty(&tds->login->cafile)) { tls_msg = "loading CA file"; if (strcasecmp(tds_dstr_cstr(&tds->login->cafile), "system") == 0) ret = gnutls_certificate_set_x509_system_trust(xcred); else ret = gnutls_certificate_set_x509_trust_file(xcred, tds_dstr_cstr(&tds->login->cafile), GNUTLS_X509_FMT_PEM); if (ret <= 0) goto cleanup; if (!tds_dstr_isempty(&tds->login->crlfile)) { tls_msg = "loading CRL file"; ret = gnutls_certificate_set_x509_crl_file(xcred, tds_dstr_cstr(&tds->login->crlfile), GNUTLS_X509_FMT_PEM); if (ret <= 0) goto cleanup; } #ifdef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION gnutls_certificate_set_verify_function(xcred, tds_verify_certificate); #endif } /* Initialize TLS session */ tls_msg = "initializing session"; ret = gnutls_init(&session, GNUTLS_CLIENT); if (ret != 0) goto cleanup; gnutls_transport_set_ptr(session, tds); gnutls_transport_set_pull_function(session, tds_pull_func_login); gnutls_transport_set_push_function(session, tds_push_func_login); /* NOTE: there functions return int however they cannot fail */ /* use default priorities... */ gnutls_set_default_priority(session); /* ... but overwrite some */ ret = gnutls_priority_set_direct (session, "NORMAL:%COMPAT:-VERS-SSL3.0", NULL); if (ret != 0) goto cleanup; /* mssql does not like padding too much */ #ifdef HAVE_GNUTLS_RECORD_DISABLE_PADDING gnutls_record_disable_padding(session); #endif /* put the anonymous credentials to the current session */ tls_msg = "setting credential"; ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred); if (ret != 0) goto cleanup; /* Perform the TLS handshake */ tls_msg = "handshake"; ret = gnutls_handshake (session); if (ret != 0) goto cleanup; #ifndef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION ret = tds_verify_certificate(session); if (ret != 0) goto cleanup; #endif tdsdump_log(TDS_DBG_INFO1, "handshake succeeded!!\n"); gnutls_transport_set_ptr(session, tds->conn); gnutls_transport_set_pull_function(session, tds_pull_func); gnutls_transport_set_push_function(session, tds_push_func); tds->conn->tls_session = session; tds->conn->tls_credentials = xcred; return TDS_SUCCESS; cleanup: if (session) gnutls_deinit(session); if (xcred) gnutls_certificate_free_credentials(xcred); tdsdump_log(TDS_DBG_ERROR, "%s failed: %s\n", tls_msg, gnutls_strerror (ret)); return TDS_FAIL; }
int tds_ssl_init(TDSSOCKET *tds) { #define OPENSSL_CIPHERS \ "DHE-RSA-AES256-SHA DHE-DSS-AES256-SHA " \ "AES256-SHA EDH-RSA-DES-CBC3-SHA " \ "EDH-DSS-DES-CBC3-SHA DES-CBC3-SHA " \ "DES-CBC3-MD5 DHE-RSA-AES128-SHA " \ "DHE-DSS-AES128-SHA AES128-SHA RC2-CBC-MD5 RC4-SHA RC4-MD5" SSL *con; SSL_CTX *ctx; BIO *b, *b2; int ret; const char *tls_msg; con = NULL; b = NULL; b2 = NULL; ret = 1; tds_check_wildcard_test(); tds_ssl_deinit(tds->conn); tls_msg = "initializing tls"; ctx = tds_init_openssl(); if (!ctx) goto cleanup; if (!tds_dstr_isempty(&tds->login->cafile)) { tls_msg = "loading CA file"; if (strcasecmp(tds_dstr_cstr(&tds->login->cafile), "system") == 0) ret = SSL_CTX_set_default_verify_paths(ctx); else ret = SSL_CTX_load_verify_locations(ctx, tds_dstr_cstr(&tds->login->cafile), NULL); if (ret != 1) goto cleanup; if (!tds_dstr_isempty(&tds->login->crlfile)) { X509_STORE *store = SSL_CTX_get_cert_store(ctx); X509_LOOKUP *lookup; tls_msg = "loading CRL file"; if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) || (!X509_load_crl_file(lookup, tds_dstr_cstr(&tds->login->crlfile), X509_FILETYPE_PEM))) goto cleanup; X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); } SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); } /* Initialize TLS session */ tls_msg = "initializing session"; con = SSL_new(ctx); if (!con) goto cleanup; tls_msg = "creating bio"; b = BIO_new(&tds_method_login); if (!b) goto cleanup; b2 = BIO_new(&tds_method); if (!b2) goto cleanup; b->shutdown=1; b->init=1; b->num= -1; b->ptr = tds; BIO_set_conn_hostname(b, tds_dstr_cstr(&tds->login->server_host_name)); SSL_set_bio(con, b, b); b = NULL; /* use priorities... */ SSL_set_cipher_list(con, OPENSSL_CIPHERS); #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS /* this disable a security improvement but allow connection... */ SSL_set_options(con, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); #endif /* Perform the TLS handshake */ tls_msg = "handshake"; SSL_set_connect_state(con); ret = SSL_connect(con) != 1 || con->state != SSL_ST_OK; if (ret != 0) goto cleanup; /* check certificate hostname */ if (!tds_dstr_isempty(&tds->login->cafile) && tds->login->check_ssl_hostname) { X509 *cert; cert = SSL_get_peer_certificate(con); tls_msg = "checking hostname"; if (!cert || !check_hostname(cert, tds_dstr_cstr(&tds->login->server_host_name))) goto cleanup; } tdsdump_log(TDS_DBG_INFO1, "handshake succeeded!!\n"); b2->shutdown = 1; b2->init = 1; b2->num = -1; b2->ptr = tds->conn; SSL_set_bio(con, b2, b2); tds->conn->tls_session = con; tds->conn->tls_ctx = ctx; return TDS_SUCCESS; cleanup: if (b2) BIO_free(b2); if (b) BIO_free(b); if (con) { SSL_shutdown(con); SSL_free(con); } SSL_CTX_free(ctx); tdsdump_log(TDS_DBG_ERROR, "%s failed\n", tls_msg); return TDS_FAIL; }