Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 2007-2011 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 : #include <ctype.h>
27 :
28 : #if HAVE_STRING_H
29 : #include <string.h>
30 : #endif /* HAVE_STRING_H */
31 :
32 : #if HAVE_UNISTD_H
33 : #include <unistd.h>
34 : #endif /* HAVE_UNISTD_H */
35 :
36 : #if HAVE_NETDB_H
37 : #include <netdb.h>
38 : #endif /* HAVE_NETDB_H */
39 :
40 : #if HAVE_SYS_SOCKET_H
41 : #include <sys/socket.h>
42 : #endif /* HAVE_SYS_SOCKET_H */
43 :
44 : #if HAVE_SYS_TYPES_H
45 : #include <sys/types.h>
46 : #endif /* HAVE_SYS_TYPES_H */
47 :
48 : #if HAVE_NETINET_IN_H
49 : #include <netinet/in.h>
50 : #endif /* HAVE_NETINET_IN_H */
51 :
52 : #if HAVE_ARPA_INET_H
53 : #include <arpa/inet.h>
54 : #endif /* HAVE_ARPA_INET_H */
55 :
56 : #if HAVE_COM_ERR_H
57 : #include <com_err.h>
58 : #endif /* HAVE_COM_ERR_H */
59 :
60 : #ifdef ENABLE_KRB5
61 :
62 : #ifdef __APPLE__
63 : #define KERBEROS_APPLE_DEPRECATED(x)
64 : #define GSSKRB_APPLE_DEPRECATED(x)
65 : #undef __API_DEPRECATED
66 : #define __API_DEPRECATED(x, y)
67 : #endif
68 : #include <gssapi/gssapi_krb5.h>
69 :
70 : #include <freetds/tds.h>
71 : #include <freetds/utils/string.h>
72 : #include <freetds/replacements.h>
73 :
74 : /**
75 : * \ingroup libtds
76 : * \defgroup auth Authentication
77 : * Functions for handling authentication.
78 : */
79 :
80 : /**
81 : * \addtogroup auth
82 : * @{
83 : */
84 :
85 : typedef struct tds_gss_auth
86 : {
87 : TDSAUTHENTICATION tds_auth;
88 : gss_ctx_id_t gss_context;
89 : gss_name_t target_name;
90 : char *sname;
91 : OM_uint32 last_stat;
92 : } TDSGSSAUTH;
93 :
94 : static TDSRET
95 0 : tds_gss_free(TDSCONNECTION * conn TDS_UNUSED, struct tds_authentication * tds_auth)
96 : {
97 0 : TDSGSSAUTH *auth = (TDSGSSAUTH *) tds_auth;
98 : OM_uint32 min_stat;
99 :
100 0 : if (auth->tds_auth.packet) {
101 : gss_buffer_desc send_tok;
102 :
103 0 : send_tok.value = (void *) auth->tds_auth.packet;
104 0 : send_tok.length = auth->tds_auth.packet_len;
105 0 : gss_release_buffer(&min_stat, &send_tok);
106 : }
107 :
108 0 : gss_release_name(&min_stat, &auth->target_name);
109 0 : free(auth->sname);
110 0 : if (auth->gss_context != GSS_C_NO_CONTEXT)
111 0 : gss_delete_sec_context(&min_stat, &auth->gss_context, GSS_C_NO_BUFFER);
112 0 : free(auth);
113 :
114 0 : return TDS_SUCCESS;
115 : }
116 :
117 : static TDSRET tds_gss_continue(TDSSOCKET * tds, struct tds_gss_auth *auth, gss_buffer_desc *token_ptr);
118 :
119 : static TDSRET
120 0 : tds7_gss_handle_next(TDSSOCKET * tds, struct tds_authentication * auth, size_t len)
121 : {
122 : TDSRET res;
123 : gss_buffer_desc recv_tok;
124 :
125 0 : if (((struct tds_gss_auth *) auth)->last_stat != GSS_S_CONTINUE_NEEDED)
126 : return TDS_FAIL;
127 :
128 0 : if (auth->packet) {
129 : OM_uint32 min_stat;
130 : gss_buffer_desc send_tok;
131 :
132 0 : send_tok.value = (void *) auth->packet;
133 0 : send_tok.length = auth->packet_len;
134 0 : gss_release_buffer(&min_stat, &send_tok);
135 0 : auth->packet = NULL;
136 : }
137 :
138 0 : recv_tok.length = len;
139 0 : recv_tok.value = tds_new(char, len);
140 0 : if (!recv_tok.value)
141 : return TDS_FAIL;
142 0 : tds_get_n(tds, recv_tok.value, len);
143 :
144 0 : res = tds_gss_continue(tds, (struct tds_gss_auth *) auth, &recv_tok);
145 0 : free(recv_tok.value);
146 0 : TDS_PROPAGATE(res);
147 :
148 0 : if (auth->packet_len) {
149 0 : tds->out_flag = TDS7_AUTH;
150 0 : tds_put_n(tds, auth->packet, auth->packet_len);
151 0 : return tds_flush_packet(tds);
152 : }
153 : return TDS_SUCCESS;
154 : }
155 :
156 : static TDSRET
157 0 : tds5_gss_handle_next(TDSSOCKET * tds, struct tds_authentication * auth, size_t len TDS_UNUSED)
158 : {
159 : gss_buffer_desc recv_tok;
160 : TDSPARAMINFO *info;
161 : TDSCOLUMN *col;
162 :
163 0 : if (((struct tds_gss_auth *) auth)->last_stat != GSS_S_CONTINUE_NEEDED)
164 : return TDS_FAIL;
165 :
166 0 : if (auth->packet) {
167 : OM_uint32 min_stat;
168 : gss_buffer_desc send_tok;
169 :
170 0 : send_tok.value = (void *) auth->packet;
171 0 : send_tok.length = auth->packet_len;
172 0 : gss_release_buffer(&min_stat, &send_tok);
173 0 : auth->packet = NULL;
174 : }
175 :
176 : /* parse from saved message */
177 0 : if (auth->msg_type != TDS5_MSG_SEC_OPAQUE)
178 : goto error;
179 0 : auth->msg_type = 0;
180 :
181 0 : info = tds->param_info;
182 0 : if (!info || info->num_cols < 5)
183 : goto error;
184 :
185 : /* check first column is int and TDS5_SEC_VERSION */
186 0 : col = info->columns[0];
187 0 : if (tds_get_conversion_type(col->on_server.column_type, col->on_server.column_size) != SYBINT4)
188 : goto error;
189 0 : if (*((TDS_INT *) col->column_data) != TDS5_SEC_VERSION)
190 : goto error;
191 :
192 : /* check second column is int and TDS5_SEC_SECSESS */
193 0 : col = info->columns[1];
194 0 : if (tds_get_conversion_type(col->on_server.column_type, col->on_server.column_size) != SYBINT4)
195 : goto error;
196 0 : if (*((TDS_INT *) col->column_data) != TDS5_SEC_SECSESS)
197 : goto error;
198 :
199 0 : col = info->columns[3];
200 0 : if (col->column_type != SYBLONGBINARY)
201 : goto error;
202 0 : recv_tok.value = ((TDSBLOB*) col->column_data)->textvalue;
203 0 : recv_tok.length = col->column_size;
204 :
205 0 : TDS_PROPAGATE(tds_gss_continue(tds, (struct tds_gss_auth *) auth, &recv_tok));
206 :
207 0 : tds->out_flag = TDS_NORMAL;
208 0 : TDS_PROPAGATE(tds5_gss_send(tds));
209 :
210 0 : return tds_flush_packet(tds);
211 :
212 0 : error:
213 : return TDS_FAIL;
214 : }
215 :
216 : /**
217 : * Build a GSSAPI packet to send to server
218 : * @param tds A pointer to the TDSSOCKET structure managing a client/server operation.
219 : * @return size of packet
220 : */
221 : TDSAUTHENTICATION *
222 0 : tds_gss_get_auth(TDSSOCKET * tds)
223 : {
224 : /*
225 : * TODO
226 : * There are some differences between this implementation and MS on
227 : * - MS use SPNEGO with 3 mechnisms (MS KRB5, KRB5, NTLMSSP)
228 : * - MS seems to use MUTUAL flag
229 : * - name type is "Service and Instance (2)" and not "Principal (1)"
230 : * check for memory leaks
231 : * check for errors in many functions
232 : * a bit more verbose
233 : * dinamically load library ??
234 : */
235 : gss_buffer_desc send_tok;
236 : OM_uint32 maj_stat, min_stat;
237 : #ifdef __APPLE__
238 : /* some MacOS header defines gss_OID_desc with a wrong byte alignment, use external
239 : * library definition. */
240 : # define nt_principal (*(gss_OID_desc *) GSS_KRB5_NT_PRINCIPAL_NAME)
241 : #else
242 : /* same as GSS_KRB5_NT_PRINCIPAL_NAME but do not require .so library */
243 : static gss_OID_desc nt_principal = { 10, (void*) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01" };
244 : #endif
245 : const char *server_name;
246 : /* Storage for getaddrinfo calls */
247 0 : struct addrinfo *addrs = NULL;
248 0 : int len = 0;
249 :
250 : struct tds_gss_auth *auth;
251 :
252 0 : if (!tds->login)
253 : return NULL;
254 :
255 0 : auth = tds_new0(struct tds_gss_auth, 1);
256 0 : if (!auth)
257 : return NULL;
258 :
259 0 : auth->tds_auth.free = tds_gss_free;
260 0 : auth->tds_auth.handle_next = IS_TDS50(tds->conn) ? tds5_gss_handle_next : tds7_gss_handle_next;
261 0 : auth->gss_context = GSS_C_NO_CONTEXT;
262 0 : auth->last_stat = GSS_S_COMPLETE;
263 :
264 0 : server_name = tds_dstr_cstr(&tds->login->server_host_name);
265 0 : if (IS_TDS7_PLUS(tds->conn) && strchr(server_name, '.') == NULL) {
266 : struct addrinfo hints;
267 0 : memset(&hints, 0, sizeof(hints));
268 : hints.ai_family = AF_UNSPEC;
269 0 : hints.ai_socktype = SOCK_STREAM;
270 0 : hints.ai_flags = AI_V4MAPPED|AI_ADDRCONFIG|AI_CANONNAME|AI_FQDN;
271 0 : if (!getaddrinfo(server_name, NULL, &hints, &addrs) && addrs->ai_canonname
272 0 : && strchr(addrs->ai_canonname, '.') != NULL)
273 0 : server_name = addrs->ai_canonname;
274 : }
275 :
276 0 : if (!tds_dstr_isempty(&tds->login->server_spn)) {
277 0 : auth->sname = strdup(tds_dstr_cstr(&tds->login->server_spn));
278 0 : } else if (IS_TDS7_PLUS(tds->conn)) {
279 0 : if (tds_dstr_isempty(&tds->login->server_realm_name)) {
280 0 : len = asprintf(&auth->sname, "MSSQLSvc/%s:%d", server_name, tds->login->port);
281 : } else {
282 0 : len = asprintf(&auth->sname, "MSSQLSvc/%s:%d@%s", server_name, tds->login->port,
283 0 : tds_dstr_cstr(&tds->login->server_realm_name));
284 : }
285 : } else {
286 : /* TDS 5.0, Sybase */
287 0 : server_name = tds_dstr_cstr(&tds->login->server_name);
288 0 : if (tds_dstr_isempty(&tds->login->server_realm_name)) {
289 0 : len = asprintf(&auth->sname, "%s", server_name);
290 : } else {
291 0 : len = asprintf(&auth->sname, "%s@%s", server_name,
292 0 : tds_dstr_cstr(&tds->login->server_realm_name));
293 : }
294 : }
295 0 : if (addrs)
296 0 : freeaddrinfo(addrs);
297 0 : if (len < 0 || auth->sname == NULL) {
298 0 : tds_gss_free(tds->conn, (TDSAUTHENTICATION *) auth);
299 0 : return NULL;
300 : }
301 0 : tdsdump_log(TDS_DBG_NETWORK, "using kerberos name %s\n", auth->sname);
302 :
303 : /*
304 : * Import the name into target_name. Use send_tok to save
305 : * local variable space.
306 : */
307 0 : send_tok.value = auth->sname;
308 0 : send_tok.length = strlen(auth->sname);
309 0 : maj_stat = gss_import_name(&min_stat, &send_tok, &nt_principal, &auth->target_name);
310 :
311 0 : switch (maj_stat) {
312 0 : case GSS_S_COMPLETE:
313 0 : tdsdump_log(TDS_DBG_NETWORK, "gss_import_name: GSS_S_COMPLETE: gss_import_name completed successfully.\n");
314 0 : if (TDS_FAILED(tds_gss_continue(tds, auth, GSS_C_NO_BUFFER))) {
315 0 : tds_gss_free(tds->conn, (TDSAUTHENTICATION *) auth);
316 0 : return NULL;
317 : }
318 : break;
319 0 : case GSS_S_BAD_NAMETYPE:
320 0 : tdsdump_log(TDS_DBG_NETWORK, "gss_import_name: GSS_S_BAD_NAMETYPE: The input_name_type was unrecognized.\n");
321 : break;
322 0 : case GSS_S_BAD_NAME:
323 0 : 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");
324 : break;
325 0 : case GSS_S_BAD_MECH:
326 0 : 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");
327 : break;
328 0 : default:
329 0 : tdsdump_log(TDS_DBG_NETWORK, "gss_import_name: unexpected error %d.\n", maj_stat);
330 : break;
331 : }
332 :
333 0 : if (GSS_ERROR(maj_stat)) {
334 0 : tds_gss_free(tds->conn, (TDSAUTHENTICATION *) auth);
335 0 : return NULL;
336 : }
337 :
338 : return (TDSAUTHENTICATION *) auth;
339 : }
340 :
341 : #ifndef HAVE_ERROR_MESSAGE
342 : static const char *
343 : tds_error_message(OM_uint32 e)
344 : {
345 0 : const char *m = strerror(e);
346 0 : if (m == NULL)
347 : return "";
348 : return m;
349 : }
350 : #define error_message tds_error_message
351 : #endif
352 :
353 : static TDSRET
354 0 : tds_gss_continue(TDSSOCKET * tds, struct tds_gss_auth *auth, gss_buffer_desc *token_ptr)
355 : {
356 : gss_buffer_desc send_tok;
357 0 : OM_uint32 maj_stat, min_stat = 0;
358 : OM_uint32 ret_flags;
359 : int gssapi_flags;
360 0 : const char *msg = "???";
361 0 : gss_OID pmech = GSS_C_NULL_OID;
362 :
363 0 : auth->last_stat = GSS_S_COMPLETE;
364 :
365 0 : send_tok.value = NULL;
366 0 : send_tok.length = 0;
367 :
368 : /*
369 : * Perform the context-establishement loop.
370 : *
371 : * On each pass through the loop, token_ptr points to the token
372 : * to send to the server (or GSS_C_NO_BUFFER on the first pass).
373 : * Every generated token is stored in send_tok which is then
374 : * transmitted to the server; every received token is stored in
375 : * recv_tok, which token_ptr is then set to, to be processed by
376 : * the next call to gss_init_sec_context.
377 : *
378 : * GSS-API guarantees that send_tok's length will be non-zero
379 : * if and only if the server is expecting another token from us,
380 : * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
381 : * and only if the server has another token to send us.
382 : */
383 :
384 : /*
385 : * We always want to ask for the replay, and integ flags.
386 : * We may ask for delegation based on config in the tds.conf and other conf files.
387 : */
388 0 : gssapi_flags = GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG;
389 :
390 0 : if (tds->login->gssapi_use_delegation)
391 0 : gssapi_flags |= GSS_C_DELEG_FLAG;
392 0 : if (tds->login->mutual_authentication || IS_TDS7_PLUS(tds->conn))
393 0 : gssapi_flags |= GSS_C_MUTUAL_FLAG;
394 :
395 0 : maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &auth->gss_context, auth->target_name,
396 : GSS_C_NULL_OID,
397 : gssapi_flags,
398 : 0, NULL, /* no channel bindings */
399 : token_ptr,
400 : &pmech,
401 : &send_tok, &ret_flags, NULL); /* ignore time_rec */
402 :
403 0 : tdsdump_log(TDS_DBG_NETWORK, "gss_init_sec_context: actual mechanism at %p\n", pmech);
404 0 : if (pmech && pmech->elements) {
405 0 : tdsdump_dump_buf(TDS_DBG_NETWORK, "actual mechanism", pmech->elements, pmech->length);
406 : }
407 :
408 0 : auth->last_stat = maj_stat;
409 :
410 0 : switch (maj_stat) {
411 0 : case GSS_S_COMPLETE:
412 0 : msg = "GSS_S_COMPLETE: gss_init_sec_context completed successfully.";
413 0 : break;
414 0 : case GSS_S_CONTINUE_NEEDED:
415 0 : msg = "GSS_S_CONTINUE_NEEDED: gss_init_sec_context() routine must be called again.";
416 0 : break;
417 0 : case GSS_S_FAILURE:
418 0 : msg = "GSS_S_FAILURE: The routine failed for reasons that are not defined at the GSS level.";
419 0 : tdsdump_log(TDS_DBG_NETWORK, "gss_init_sec_context: min_stat %ld \"%s\"\n",
420 : (long) min_stat, error_message(min_stat));
421 : break;
422 0 : case GSS_S_BAD_BINDINGS:
423 0 : msg = "GSS_S_BAD_BINDINGS: The channel bindings are not valid.";
424 0 : break;
425 0 : case GSS_S_BAD_MECH:
426 0 : msg = "GSS_S_BAD_MECH: The request security mechanism is not supported.";
427 0 : break;
428 0 : case GSS_S_BAD_NAME:
429 0 : msg = "GSS_S_BAD_NAME: The target_name parameter is not valid.";
430 0 : break;
431 0 : case GSS_S_BAD_SIG:
432 0 : msg = "GSS_S_BAD_SIG: The input token contains an incorrect integrity check value.";
433 0 : break;
434 0 : case GSS_S_CREDENTIALS_EXPIRED:
435 0 : msg = "GSS_S_CREDENTIALS_EXPIRED: The supplied credentials are no longer valid.";
436 0 : break;
437 0 : case GSS_S_DEFECTIVE_CREDENTIAL:
438 0 : msg = "GSS_S_DEFECTIVE_CREDENTIAL: Consistency checks performed on the credential failed.";
439 0 : break;
440 0 : case GSS_S_DEFECTIVE_TOKEN:
441 0 : msg = "GSS_S_DEFECTIVE_TOKEN: Consistency checks performed on the input token failed.";
442 0 : break;
443 0 : case GSS_S_DUPLICATE_TOKEN:
444 0 : msg = "GSS_S_DUPLICATE_TOKEN: The token is a duplicate of a token that has already been processed.";
445 0 : break;
446 0 : case GSS_S_NO_CONTEXT:
447 0 : msg = "GSS_S_NO_CONTEXT: The context handle provided by the caller does not refer to a valid security context.";
448 0 : break;
449 0 : case GSS_S_NO_CRED:
450 0 : msg = "GSS_S_NO_CRED: The supplied credential handle does not refer to a valid credential, the supplied credential is not";
451 0 : break;
452 0 : case GSS_S_OLD_TOKEN:
453 0 : msg = "GSS_S_OLD_TOKEN: The token is too old to be checked for duplication against previous tokens which have already been processed.";
454 0 : break;
455 : }
456 :
457 0 : if (GSS_ERROR(maj_stat)) {
458 0 : gss_release_buffer(&min_stat, &send_tok);
459 0 : tdsdump_log(TDS_DBG_NETWORK, "gss_init_sec_context: %s\n", msg);
460 : return TDS_FAIL;
461 : }
462 :
463 0 : auth->tds_auth.packet = (uint8_t *) send_tok.value;
464 0 : auth->tds_auth.packet_len = send_tok.length;
465 :
466 0 : return TDS_SUCCESS;
467 : }
468 :
469 : static void
470 0 : tds5_send_msg(TDSSOCKET *tds, uint16_t msg_type)
471 : {
472 0 : tds_put_tinyint(tds, TDS_MSG_TOKEN);
473 0 : tds_put_tinyint(tds, 3); /* length */
474 0 : tds_put_tinyint(tds, 1); /* status, 1=has params */
475 0 : tds_put_smallint(tds, msg_type);
476 0 : }
477 :
478 : TDSRET
479 0 : tds5_gss_send(TDSSOCKET *tds)
480 : {
481 0 : uint32_t flags = TDS5_SEC_NETWORK_AUTHENTICATION;
482 :
483 0 : if (!tds->conn->authentication)
484 : return TDS_FAIL;
485 :
486 0 : if (tds->login) {
487 0 : if (tds->login->gssapi_use_delegation)
488 0 : flags |= TDS5_SEC_DELEGATION;
489 0 : if (tds->login->mutual_authentication)
490 0 : flags |= TDS5_SEC_MUTUAL_AUTHENTICATION;
491 : }
492 :
493 0 : tds5_send_msg(tds, TDS5_MSG_SEC_OPAQUE);
494 :
495 0 : tds_put_byte(tds, TDS5_PARAMFMT_TOKEN);
496 0 : TDS_START_LEN_USMALLINT(tds) {
497 0 : tds_put_smallint(tds, 5); /* # parameters */
498 :
499 0 : tds_put_n(tds, NULL, 6); /* name len + output + usertype */
500 0 : tds_put_tinyint(tds, SYBINTN);
501 0 : tds_put_tinyint(tds, 4);
502 0 : tds_put_tinyint(tds, 0); /* locale len */
503 :
504 0 : tds_put_n(tds, NULL, 6); /* name len + output + usertype */
505 0 : tds_put_tinyint(tds, SYBINTN);
506 0 : tds_put_tinyint(tds, 4);
507 0 : tds_put_tinyint(tds, 0); /* locale len */
508 :
509 0 : tds_put_n(tds, NULL, 6); /* name len + output + usertype */
510 0 : tds_put_tinyint(tds, SYBVARBINARY);
511 0 : tds_put_tinyint(tds, 255);
512 0 : tds_put_tinyint(tds, 0); /* locale len */
513 :
514 0 : tds_put_n(tds, NULL, 6); /* name len + output + usertype */
515 0 : tds_put_tinyint(tds, SYBLONGBINARY);
516 0 : tds_put_int(tds, 0x7fffffff);
517 0 : tds_put_tinyint(tds, 0); /* locale len */
518 :
519 0 : tds_put_n(tds, NULL, 6); /* name len + output + usertype */
520 0 : tds_put_tinyint(tds, SYBINTN);
521 0 : tds_put_tinyint(tds, 4);
522 0 : tds_put_tinyint(tds, 0); /* locale len */
523 0 : } TDS_END_LEN
524 :
525 0 : tds_put_byte(tds, TDS5_PARAMS_TOKEN);
526 :
527 0 : tds_put_tinyint(tds, 4);
528 0 : tds_put_int(tds, TDS5_SEC_VERSION);
529 :
530 0 : tds_put_tinyint(tds, 4);
531 0 : tds_put_int(tds, TDS5_SEC_SECSESS);
532 :
533 0 : tds_put_tinyint(tds, 12);
534 0 : tds_put_n(tds, "\x06\x0a\x2b\x06\x01\x04\x01\x87\x01\x04\x06\x06", 12); /* KRB5 Sybase OID */
535 :
536 0 : tds_put_int(tds, tds->conn->authentication->packet_len);
537 0 : tds_put_n(tds, tds->conn->authentication->packet, tds->conn->authentication->packet_len);
538 :
539 0 : tds_put_tinyint(tds, 4);
540 0 : tds_put_int(tds, flags);
541 :
542 0 : return TDS_SUCCESS;
543 : }
544 :
545 : /** @} */
546 :
547 : #endif
|