login.c

来自「在Linux/Unix下面访问WINDOWS SQLSERVER 的ODBC驱动」· C语言 代码 · 共 1,031 行 · 第 1/2 页

C
1,031
字号
	 */	/*	 * This is the original capabilities packet we were working with (sqsh)	 * unsigned char capabilities[]= {0x01,0x07,0x03,109,127,0xFF,0xFF,0xFF,0xFE,0x02,0x07,0x00,0x00,0x0A,104,0x00,0x00,0x00};	 * original with 4.x messages	 * unsigned char capabilities[]= {0x01,0x07,0x03,109,127,0xFF,0xFF,0xFF,0xFE,0x02,0x07,0x00,0x00,0x00,120,192,0x00,0x0D};	 * This is isql 11.0.3	 * unsigned char capabilities[]= {0x01,0x07,0x00,96, 129,207, 0xFF,0xFE,62,  0x02,0x07,0x00,0x00,0x00,120,192,0x00,0x0D};	 * like isql but with 5.0 messages	 * unsigned char capabilities[]= {0x01,0x07,0x00,96, 129,207, 0xFF,0xFE,62,  0x02,0x07,0x00,0x00,0x00,120,192,0x00,0x00};	 */	unsigned char protocol_version[4];	unsigned char program_version[4];	const char *server_charset;	int len;	char blockstr[16];	if (strchr(tds_dstr_cstr(&connection->user_name), '\\') != NULL) {		tdsdump_log(TDS_DBG_ERROR, "NT login not support using TDS 4.x or 5.0\n");		return TDS_FAIL;	}	if (IS_TDS42(tds)) {		memcpy(protocol_version, "\004\002\000\000", 4);		memcpy(program_version, "\004\002\000\000", 4);	} else if (IS_TDS46(tds)) {		memcpy(protocol_version, "\004\006\000\000", 4);		memcpy(program_version, "\004\002\000\000", 4);	} else if (IS_TDS50(tds)) {		memcpy(protocol_version, "\005\000\000\000", 4);		memcpy(program_version, "\005\000\000\000", 4);	} else {		tdsdump_log(TDS_DBG_SEVERE, "Unknown protocol version!\n");		exit(1);	}	/*	 * the following code is adapted from  Arno Pedusaar's 	 * (psaar@fenar.ee) MS-SQL Client. His was a much better way to	 * do this, (well...mine was a kludge actually) so here's mostly his	 */	tds_put_login_string(tds, tds_dstr_cstr(&connection->client_host_name), TDS_MAX_LOGIN_STR_SZ);	/* client host name */	tds_put_login_string(tds, tds_dstr_cstr(&connection->user_name), TDS_MAX_LOGIN_STR_SZ);	/* account name */	tds_put_login_string(tds, tds_dstr_cstr(&connection->password), TDS_MAX_LOGIN_STR_SZ);	/* account password */	tds_put_login_string(tds, "37876", TDS_MAX_LOGIN_STR_SZ);	/* host process */#ifdef WORDS_BIGENDIAN	if (tds->emul_little_endian) {		tds_put_n(tds, le1, 6);	} else {		tds_put_n(tds, be1, 6);	}#else	tds_put_n(tds, le1, 6);#endif	tds_put_byte(tds, connection->bulk_copy);	tds_put_n(tds, magic2, 2);	if (IS_TDS42(tds)) {		tds_put_int(tds, 512);	} else {		tds_put_int(tds, 0);	}	tds_put_n(tds, magic3, 3);	tds_put_login_string(tds, tds_dstr_cstr(&connection->app_name), TDS_MAX_LOGIN_STR_SZ);	tds_put_login_string(tds, tds_dstr_cstr(&connection->server_name), TDS_MAX_LOGIN_STR_SZ);	if (IS_TDS42(tds)) {		tds_put_login_string(tds, tds_dstr_cstr(&connection->password), 255);	} else {		len = tds_dstr_len(&connection->password);		if (len > 253)			len = 0;		tds_put_byte(tds, 0);		tds_put_byte(tds, len);		tds_put_n(tds, tds_dstr_cstr(&connection->password), len);		tds_put_n(tds, NULL, 253 - len);		tds_put_byte(tds, len + 2);	}	tds_put_n(tds, protocol_version, 4);	/* TDS version; { 0x04,0x02,0x00,0x00 } */	tds_put_login_string(tds, tds_dstr_cstr(&connection->library), 10);	/* client program name */	if (IS_TDS42(tds)) {		tds_put_int(tds, 0);	} else {		tds_put_n(tds, program_version, 4);	/* program version ? */	}#ifdef WORDS_BIGENDIAN	if (tds->emul_little_endian) {		tds_put_n(tds, le2, 3);	} else {		tds_put_n(tds, be2, 3);	}#else	tds_put_n(tds, le2, 3);#endif	tds_put_login_string(tds, tds_dstr_cstr(&connection->language), TDS_MAX_LOGIN_STR_SZ);	/* language */	tds_put_byte(tds, connection->suppress_language);	tds_put_n(tds, magic5, 2);	tds_put_byte(tds, connection->encryption_level ? 1 : 0);	tds_put_n(tds, magic6, 10);	/* use charset nearest to client or nothing */	server_charset = NULL;	if (!tds_dstr_isempty(&connection->server_charset))		server_charset = tds_dstr_cstr(&connection->server_charset);	else		server_charset = tds_sybase_charset_name(tds_dstr_cstr(&connection->client_charset));	if (!server_charset)		server_charset = "";	tds_put_login_string(tds, server_charset, TDS_MAX_LOGIN_STR_SZ);	/* charset */	/* this is a flag, mean that server should use character set provided by client */	/* TODO notify charset change ?? what's correct meaning ?? -- freddy77 */	tds_put_byte(tds, 1);	/* network packet size */	if (connection->block_size < 1000000 && connection->block_size)		sprintf(blockstr, "%d", connection->block_size);	else		strcpy(blockstr, "512");	tds_put_login_string(tds, blockstr, 6);	if (IS_TDS42(tds)) {		tds_put_n(tds, magic42, 8);	} else if (IS_TDS46(tds)) {		tds_put_n(tds, magic42, 4);	} else if (IS_TDS50(tds)) {		tds_put_n(tds, magic50, 4);		tds_put_byte(tds, TDS_CAPABILITY_TOKEN);		tds_put_smallint(tds, TDS_MAX_CAPABILITY);		tds_put_n(tds, tds->capabilities, TDS_MAX_CAPABILITY);	}	return tds_flush_packet(tds);}/** * tds7_send_login() -- Send a TDS 7.0 login packet * TDS 7.0 login packet is vastly different and so gets its own function * \returns the return value is ignored by the caller. :-/ */static inttds7_send_login(TDSSOCKET * tds, TDSCONNECTION * connection){	int rc;	static const unsigned char client_progver[] = { 6, 0x83, 0xf2, 0xf8 };	static const unsigned char tds7Version[] = { 0x00, 0x00, 0x00, 0x70 };	static const unsigned char tds8Version[] = { 0x01, 0x00, 0x00, 0x71 };	static const unsigned char tds9Version[] = { 0x02, 0x00, 0x09, 0x72 };	static const unsigned char connection_id[] = { 0x00, 0x00, 0x00, 0x00 };	unsigned char option_flag1 = 0x00;	unsigned char option_flag2 = tds->option_flag2;	static const unsigned char sql_type_flag = 0x00;	static const unsigned char reserved_flag = 0x00;	static const unsigned char time_zone[] = { 0x88, 0xff, 0xff, 0xff };	static const unsigned char collation[] = { 0x36, 0x04, 0x00, 0x00 };	unsigned char hwaddr[6];	/* 0xb4,0x00,0x30,0x00,0xe4,0x00,0x00,0x00; */	char unicode_string[256];	char *punicode;	size_t unicode_left;	int packet_size;	int block_size;	int current_pos;	const char *user_name = tds_dstr_cstr(&connection->user_name);	int user_name_len = strlen(user_name);	int host_name_len = tds_dstr_len(&connection->client_host_name);	int app_name_len = tds_dstr_len(&connection->app_name);	size_t password_len = tds_dstr_len(&connection->password);	int server_name_len = tds_dstr_len(&connection->server_name);	int library_len = tds_dstr_len(&connection->library);	int language_len = tds_dstr_len(&connection->language);	int database_len = tds_dstr_len(&connection->database);	int auth_len = 0;	tds->out_flag = TDS7_LOGIN;	/* discard possible previous authentication */	if (tds->authentication) {		tds->authentication->free(tds, tds->authentication);		tds->authentication = NULL;	}	/* avoid overflow limiting password */	if (password_len > 128)		password_len = 128;	current_pos = IS_TDS90(tds) ? 86 + 8 : 86;	/* ? */	packet_size = current_pos + (host_name_len + app_name_len + server_name_len + library_len + language_len + database_len) * 2;	/* check ntlm */	if (strchr(user_name, '\\') != NULL) {		tds->authentication = tds_ntlm_get_auth(tds);		if (!tds->authentication)			return TDS_FAIL;		auth_len = tds->authentication->packet_len;		packet_size += auth_len;	} else if (user_name_len == 0) {#ifdef ENABLE_KRB5		/* try kerberos */		tds->authentication = tds_gss_get_auth(tds);		if (!tds->authentication)			return TDS_FAIL;		auth_len = tds->authentication->packet_len;		packet_size += auth_len;#else		return TDS_FAIL;#endif	} else		packet_size += (user_name_len + password_len) * 2;	tdsdump_log(TDS_DBG_INFO2, "quietly sending TDS 7+ login packet\n");	tdsdump_off();	tds_put_int(tds, packet_size);	if (IS_TDS90(tds)) {		tds_put_n(tds, tds9Version, 4);	} else if (IS_TDS8_PLUS(tds)) {		tds_put_n(tds, tds8Version, 4);	} else {		tds_put_n(tds, tds7Version, 4);	}	if (connection->block_size < 1000000 && connection->block_size >= 512)		block_size = connection->block_size;	else		block_size = 4096;	/* SQL server default */	tds_put_int(tds, block_size);	/* desired packet size being requested by client */	if (block_size > tds->env.block_size)		tds_realloc_socket(tds, block_size);	tds_put_n(tds, client_progver, 4);	/* client program version ? */	tds_put_int(tds, getpid());	/* process id of this process */	tds_put_n(tds, connection_id, 4);	option_flag1 |= 0x80;	/* enable warning messages if SET LANGUAGE issued   */	option_flag1 |= 0x40;	/* change to initial database must succeed          */	option_flag1 |= 0x20;	/* enable warning messages if USE <database> issued */	tds_put_byte(tds, option_flag1);	if (tds->authentication)		option_flag2 |= 0x80;	/* enable domain login security                     */	tds_put_byte(tds, option_flag2);	tds_put_byte(tds, sql_type_flag);	tds_put_byte(tds, reserved_flag);	tds_put_n(tds, time_zone, 4);	tds_put_n(tds, collation, 4);	/* host name */	tds_put_smallint(tds, current_pos);	tds_put_smallint(tds, host_name_len);	current_pos += host_name_len * 2;	if (tds->authentication) {		tds_put_smallint(tds, 0);		tds_put_smallint(tds, 0);		tds_put_smallint(tds, 0);		tds_put_smallint(tds, 0);	} else {		/* username */		tds_put_smallint(tds, current_pos);		tds_put_smallint(tds, user_name_len);		current_pos += user_name_len * 2;		/* password */		tds_put_smallint(tds, current_pos);		tds_put_smallint(tds, password_len);		current_pos += password_len * 2;	}	/* app name */	tds_put_smallint(tds, current_pos);	tds_put_smallint(tds, app_name_len);	current_pos += app_name_len * 2;	/* server name */	tds_put_smallint(tds, current_pos);	tds_put_smallint(tds, server_name_len);	current_pos += server_name_len * 2;	/* unknown */	tds_put_smallint(tds, 0);	tds_put_smallint(tds, 0);	/* library name */	tds_put_smallint(tds, current_pos);	tds_put_smallint(tds, library_len);	current_pos += library_len * 2;	/* language  - kostya@warmcat.excom.spb.su */	tds_put_smallint(tds, current_pos);	tds_put_smallint(tds, language_len);	current_pos += language_len * 2;	/* database name */	tds_put_smallint(tds, current_pos);	tds_put_smallint(tds, database_len);	current_pos += database_len * 2;	/* MAC address */	tds_getmac(tds->s, hwaddr);	tds_put_n(tds, hwaddr, 6);	/* authentication stuff */	tds_put_smallint(tds, current_pos);	tds_put_smallint(tds, auth_len);	/* this matches numbers at end of packet */	current_pos += auth_len;	/* unknown */	tds_put_smallint(tds, current_pos);	tds_put_smallint(tds, 0);	if (IS_TDS90(tds)) {		tds_put_smallint(tds, current_pos);		tds_put_smallint(tds, 0);		tds_put_int(tds, 0);	}	/* FIXME here we assume single byte, do not use *2 to compute bytes, convert before !!! */	tds_put_string(tds, tds_dstr_cstr(&connection->client_host_name), host_name_len);	if (!tds->authentication) {		const char *p;		TDSICONV *char_conv = tds->char_convs[client2ucs2];		tds_put_string(tds, tds_dstr_cstr(&connection->user_name), user_name_len);		p = tds_dstr_cstr(&connection->password);		punicode = unicode_string;		unicode_left = sizeof(unicode_string);		memset(&char_conv->suppress, 0, sizeof(char_conv->suppress));		if (tds_iconv(tds, tds->char_convs[client2ucs2], to_server, &p, &password_len, &punicode, &unicode_left) ==		    (size_t) - 1) {			tdsdump_log(TDS_DBG_INFO1, "password \"%s\" could not be converted to UCS-2\n", p);			assert(0);		}		password_len = punicode - unicode_string;		tds7_crypt_pass((unsigned char *) unicode_string, password_len, (unsigned char *) unicode_string);		tds_put_n(tds, unicode_string, password_len);	}	tds_put_string(tds, tds_dstr_cstr(&connection->app_name), app_name_len);	tds_put_string(tds, tds_dstr_cstr(&connection->server_name), server_name_len);	tds_put_string(tds, tds_dstr_cstr(&connection->library), library_len);	tds_put_string(tds, tds_dstr_cstr(&connection->language), language_len);	tds_put_string(tds, tds_dstr_cstr(&connection->database), database_len);	if (tds->authentication)		tds_put_n(tds, tds->authentication->packet, auth_len);	rc = tds_flush_packet(tds);	tdsdump_on();	return rc;}/** * tds7_crypt_pass() -- 'encrypt' TDS 7.0 style passwords. * the calling function is responsible for ensuring crypt_pass is at least  * 'len' characters */unsigned char *tds7_crypt_pass(const unsigned char *clear_pass, int len, unsigned char *crypt_pass){	int i;	for (i = 0; i < len; i++)		crypt_pass[i] = ((clear_pass[i] << 4) | (clear_pass[i] >> 4)) ^ 0xA5;	return crypt_pass;}static inttds8_do_login(TDSSOCKET * tds, TDSCONNECTION * connection){#if !defined(HAVE_GNUTLS) && !defined(HAVE_OPENSSL)	/*	 * In case we do not have SSL enabled do not send pre-login so	 * if server has certificate but no force encryption login success	 */	return tds7_send_login(tds, connection);#else	int i, len, ret;	const char *instance_name = tds_dstr_isempty(&connection->instance_name) ? "MSSQLServer" : tds_dstr_cstr(&connection->instance_name);	int instance_name_len = strlen(instance_name) + 1;	TDS_CHAR crypt_flag;	unsigned int start_pos = 21;#define START_POS 21#define UI16BE(n) ((n) >> 8), ((n) & 0xffu)#define SET_UI16BE(i,n) do { buf[i] = ((n) >> 8); buf[i+1] = ((n) & 0xffu); } while(0)	TDS_UCHAR buf[] = {		/* netlib version */		0, UI16BE(START_POS), UI16BE(6),		/* encryption */		1, UI16BE(START_POS + 6), UI16BE(1),		/* instance */		2, UI16BE(START_POS + 6 + 1), UI16BE(0),		/* process id */		3, UI16BE(0), UI16BE(4),		/* ???? unknown ??? */		4, UI16BE(0), UI16BE(1),		/* end */		0xff	};	static const TDS_UCHAR netlib8[] = { 8, 0, 1, 0x55, 0, 0 };	static const TDS_UCHAR netlib9[] = { 9, 0, 0,    0, 0, 0 };	TDS_UCHAR *p;	SET_UI16BE(13, instance_name_len);	if (!IS_TDS90(tds)) {		SET_UI16BE(16, START_POS + 6 + 1 + instance_name_len);		buf[20] = 0xff;	} else {		start_pos += 5;#undef  START_POS#define START_POS 26		SET_UI16BE(1, START_POS);		SET_UI16BE(6, START_POS + 6);		SET_UI16BE(11, START_POS + 6 + 1);		SET_UI16BE(16, START_POS + 6 + 1 + instance_name_len);		SET_UI16BE(21, START_POS + 6 + 1 + instance_name_len + 4);	}	assert(start_pos >= 21 && start_pos <= sizeof(buf));	assert(buf[start_pos-1] == 0xff);	/* do prelogin */	tds->out_flag = TDS8_PRELOGIN;	tds_put_n(tds, buf, start_pos);	/* netlib version */	tds_put_n(tds, IS_TDS90(tds) ? netlib9 : netlib8, 6);	/* encryption */	tds_put_byte(tds, connection->encryption_level ? 1 : 0);	/* instance */	tds_put_n(tds, instance_name, instance_name_len);	/* pid */	tds_put_int(tds, getpid());	/* ???? unknown ??? */	if (IS_TDS90(tds))		tds_put_byte(tds, 0);	if (tds_flush_packet(tds) == TDS_FAIL)		return TDS_FAIL;	/* now process reply from server */	len = tds_read_packet(tds);	if (len <= 0 || tds->in_flag != 4)		return TDS_FAIL;	len = tds->in_len - tds->in_pos;	/* the only thing we care is flag */	p = tds->in_buf + tds->in_pos;	/* default 2, no certificate, no encryption */	crypt_flag = 2;	for (i = 0;; i += 5) {		TDS_UCHAR type;		int off, l;		if (i >= len)			return TDS_FAIL;		type = p[i];		if (type == 0xff)			break;		/* check packet */		if (i+4 >= len)			return TDS_FAIL;		off = (((int) p[i+1]) << 8) | p[i+2];		l = (((int) p[i+3]) << 8) | p[i+4];		if (off > len || (off+l) > len)			return TDS_FAIL;		if (type == 1 && l >= 1) {			crypt_flag = p[off];		}	}	/* we readed all packet */	tds->in_pos += len;	/* TODO some mssql version do not set last packet, update tds according */	tdsdump_log(TDS_DBG_INFO1, "detected flag %d\n", crypt_flag);	/* if server do not has certificate do normal login */	if (crypt_flag == 2) {		/* unless we wanted encryption and got none, then fail */		if (connection->encryption_level >= TDS_ENCRYPTION_REQUIRE)			return TDS_FAIL;		return tds7_send_login(tds, connection);	}	/*	 * if server has a certificate it require at least a crypted login	 * (even if data is not encrypted)	 */	/* here we have to do encryption ... */	if (tds_ssl_init(tds) != TDS_SUCCEED)		return TDS_FAIL;	ret = tds7_send_login(tds, connection);	/* if flag is 0 it means that after login server continue not encrypted */	if (crypt_flag == 0 || ret != TDS_SUCCEED)		tds_ssl_deinit(tds);	return ret;#endif}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?