1 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2  * Copyright (C) 2015  Frediano Ziglio
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 #include <config.h>
21 
22 #if HAVE_STDLIB_H
23 #include <stdlib.h>
24 #endif /* HAVE_STDLIB_H */
25 
26 #if HAVE_STDDEF_H
27 #include <stddef.h>
28 #endif /* HAVE_STDDEF_H */
29 
30 #include <ctype.h>
31 
32 #if HAVE_STRING_H
33 #include <string.h>
34 #endif /* HAVE_STRING_H */
35 
36 #include <freetds/time.h>
37 #include <freetds/tds.h>
38 #include <freetds/bytes.h>
39 #include <freetds/utils/string.h>
40 #include <freetds/replacements.h>
41 
42 #ifdef HAVE_GNUTLS
43 #  include "sec_negotiate_gnutls.h"
44 #elif defined(HAVE_OPENSSL)
45 #  include "sec_negotiate_openssl.h"
46 #endif
47 
48 
49 /**
50  * \ingroup libtds
51  * \defgroup auth Authentication
52  * Functions for handling authentication.
53  */
54 
55 /**
56  * \addtogroup auth
57  * @{
58  */
59 
60 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
61 
62 typedef struct tds5_negotiate
63 {
64 	TDSAUTHENTICATION tds_auth;
65 } TDS5NEGOTIATE;
66 
67 static TDSRET
tds5_negotiate_free(TDSCONNECTION * conn,TDSAUTHENTICATION * tds_auth)68 tds5_negotiate_free(TDSCONNECTION * conn, TDSAUTHENTICATION * tds_auth)
69 {
70 	TDS5NEGOTIATE *auth = (TDS5NEGOTIATE *) tds_auth;
71 
72 	free(auth->tds_auth.packet);
73 	free(auth);
74 
75 	return TDS_SUCCESS;
76 }
77 
78 static void
tds5_send_msg(TDSSOCKET * tds,uint16_t msg_type)79 tds5_send_msg(TDSSOCKET *tds, uint16_t msg_type)
80 {
81 	tds_put_tinyint(tds, TDS_MSG_TOKEN);
82 	tds_put_tinyint(tds, 3); /* length */
83 	tds_put_tinyint(tds, 1); /* status, 1=has params */
84 	tds_put_smallint(tds, msg_type);
85 }
86 
87 static TDSRET
tds5_negotiate_handle_next(TDSSOCKET * tds,TDSAUTHENTICATION * tds_auth,size_t len)88 tds5_negotiate_handle_next(TDSSOCKET * tds, TDSAUTHENTICATION * tds_auth, size_t len)
89 {
90 	TDSPARAMINFO *info;
91 	void *rsa, *nonce = NULL;
92 	size_t rsa_len, nonce_len = 0;
93 	void *em;
94 	size_t em_size;
95 	TDSRET rc = TDS_FAIL;
96 
97 	/* send next data for authentication */
98 
99 	if (!tds->login)
100 		goto error;
101 
102 	/* we only support RSA authentication, we should have send 2/3 parameters:
103 	 * 1- integer, cipher suite. 1 for RSA
104 	 * 2- binary, rsa public key in PEM format
105 	 * 3- binary, nonce (optional)
106 	 */
107 
108 	/* message not supported */
109 	if (tds_auth->msg_type != TDS5_MSG_SEC_ENCRYPT3)
110 		goto error;
111 
112 	info = tds->param_info;
113 	if (!info || info->num_cols < 2)
114 		goto error;
115 
116 	if (info->columns[1]->column_type != SYBLONGBINARY)
117 		goto error;
118 	if (info->num_cols >= 3 && info->columns[2]->column_type != SYBLONGBINARY)
119 		goto error;
120 	rsa = ((TDSBLOB*) info->columns[1]->column_data)->textvalue;
121 	rsa_len = info->columns[1]->column_size;
122 	if (info->num_cols >= 3) {
123 		nonce = ((TDSBLOB*) info->columns[2]->column_data)->textvalue;
124 		nonce_len = info->columns[2]->column_size;
125 	}
126 
127 	em = tds5_rsa_encrypt(rsa, rsa_len, nonce, nonce_len, tds_dstr_cstr(&tds->login->password), &em_size);
128 	if (!em)
129 		goto error;
130 
131 	tds->out_flag = TDS_NORMAL;
132 
133 	/* password */
134 	tds5_send_msg(tds, TDS5_MSG_SEC_LOGPWD3);
135 	tds_put_n(tds, "\xec\x0e\x00\x01\x00\x00\x00\x00\x00\x00\x00\xe1\xff\xff\xff\x7f\x00", 0x11);
136 	tds_put_byte(tds, TDS5_PARAMS_TOKEN);
137 	tds_put_int(tds, em_size);
138 	tds_put_n(tds, em, em_size);
139 
140 	/* remote password */
141 	tds5_send_msg(tds, TDS5_MSG_SEC_REMPWD3);
142 	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);
143 	tds_put_byte(tds, TDS5_PARAMS_TOKEN);
144 	tds_put_byte(tds, 0);
145 	tds_put_int(tds, em_size);
146 	tds_put_n(tds, em, em_size);
147 
148 	free(em);
149 
150 	rc = tds_flush_packet(tds);
151 
152 error:
153 	tds5_negotiate_free(tds->conn, tds_auth);
154 	tds->conn->authentication = NULL;
155 
156 	return rc;
157 }
158 
159 /**
160  * Initialize Sybase negotiate handling
161  * @param tds     A pointer to the TDSSOCKET structure managing a client/server operation.
162  * @return authentication info
163  */
164 TDSAUTHENTICATION *
tds5_negotiate_get_auth(TDSSOCKET * tds)165 tds5_negotiate_get_auth(TDSSOCKET * tds)
166 {
167 	TDS5NEGOTIATE *auth;
168 
169 	if (!tds->login)
170 		return NULL;
171 
172 	auth = tds_new0(TDS5NEGOTIATE, 1);
173 	if (!auth)
174 		return NULL;
175 
176 	auth->tds_auth.free = tds5_negotiate_free;
177 	auth->tds_auth.handle_next = tds5_negotiate_handle_next;
178 
179 	return (TDSAUTHENTICATION *) auth;
180 }
181 
182 #else /* not HAVE_GNUTLS or HAVE_OPENSSL */
183 
184 TDSAUTHENTICATION *
tds5_negotiate_get_auth(TDSSOCKET * tds)185 tds5_negotiate_get_auth(TDSSOCKET * tds)
186 {
187 	tdsdump_log(TDS_DBG_ERROR,
188 		"Sybase authentication not supported if GnuTLS or OpenSSL are not present\n");
189 
190 	return NULL;
191 }
192 
193 #endif
194 
195 /** @} */
196 
197