1 /*
2 * cmd_connect.c - User command to connect to the database
3 *
4 * Copyright (C) 1995, 1996 by Scott C. Gray
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, write to the Free Software
18 * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * You may contact the author :
21 * e-mail: gray@voicenet.com
22 * grays@xtend-tech.com
23 * gray@xenotropic.com
24 */
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <setjmp.h>
28 #include <ctpublic.h>
29 #include "sqsh_config.h"
30 #include "sqsh_debug.h"
31 #include "sqsh_error.h"
32 #include "sqsh_global.h"
33 #include "sqsh_getopt.h"
34 #include "sqsh_env.h"
35 #include "sqsh_cmd.h"
36 #include "sqsh_job.h"
37 #include "sqsh_init.h"
38 #include "sqsh_sig.h"
39 #include "sqsh_stdin.h"
40 #include "cmd.h"
41 #include "sqsh_expand.h" /* sqsh-2.1.6 */
42
43 /*-- Current Version --*/
44 #if !defined(lint) && !defined(__LINT__)
45 static char RCS_Id[] = "$Id: cmd_connect.c,v 1.40 2014/04/04 08:22:38 mwesdorp Exp $";
46 USE(RCS_Id)
47 #endif /* !defined(lint) */
48
49 /*
50 * sqsh-2.1.6 - Structure for Network Security Options
51 */
52 typedef struct _NetSecService {
53 CS_INT service;
54 CS_CHAR optchar;
55 CS_CHAR *name;
56 } NET_SEC_SERVICE;
57
58 /*
59 * sg_login: The following variable is set to True when we are in the
60 * middle of the login process. It prevents the error handler
61 * from exiting due to a dead or NULL CS_CONTEXT structure.
62 */
63 static int sg_login = False;
64 static int sg_login_failed = False;
65
66 /*
67 * sqsh-2.1.6 feature - Keep track of the number of timeouts
68 * i.e. occurrences of CS_TIMEOUT for a connection.
69 */
70 static int timeouts;
71
72 /*-- Local Prototypes --*/
73 static CS_RETCODE syb_server_cb
74 _ANSI_ARGS(( CS_CONTEXT*, CS_CONNECTION*, CS_SERVERMSG* ))
75 #if defined(__CYGWIN__)
76 __attribute__ ((stdcall))
77 #endif /* __CYGWIN__ */
78 ;
79
80 static CS_RETCODE syb_client_cb
81 _ANSI_ARGS(( CS_CONTEXT*, CS_CONNECTION*, CS_CLIENTMSG* ))
82 #if defined(__CYGWIN__)
83 __attribute__ ((stdcall))
84 #endif /* __CYGWIN__ */
85 ;
86
87 static CS_RETCODE syb_cs_cb
88 _ANSI_ARGS(( CS_CONTEXT*, CS_CLIENTMSG* ))
89 #if defined(__CYGWIN__)
90 __attribute__ ((stdcall))
91 #endif /* __CYGWIN__ */
92 ;
93
94 #if defined(CS_SSLVALIDATE_CB)
95 static CS_RETCODE validate_srvname_cb
96 _ANSI_ARGS(( CS_VOID*, CS_SSLCERT*, CS_INT, CS_INT ))
97 #if defined(__CYGWIN__)
98 __attribute__ ((stdcall))
99 #endif /* __CYGWIN__ */
100 ;
101 #endif
102
103 static int wrap_print _ANSI_ARGS(( FILE*, char* )) ;
104 static int check_opt_capability _ANSI_ARGS(( CS_CONNECTION * ));
105
106 /* sqsh-2.1.6 - New function SetNetAuth */
107 static CS_RETCODE SetNetAuth _ANSI_ARGS(( CS_CONNECTION *,
108 CS_CHAR *, CS_CHAR *, CS_CHAR *, CS_CHAR *));
109
110 /* sqsh-2.1.7 - New function ShowNetAuthCredExp */
111 static CS_RETCODE ShowNetAuthCredExp _ANSI_ARGS((CS_CONNECTION *,
112 CS_CHAR *));
113
114 /* sqsh-2.2.0 - Signal handler to respond to SIGINT during cmd_connect */
115 static JMP_BUF sg_jmp_buf;
116 static int sg_interrupted;
117 static void connect_run_sigint ( int, void *);
118
119 /*
120 * cmd_connect:
121 *
122 * This mostly internal command is used to connect to the database
123 * if a connection hasn't already been established. If one has been
124 * established CMD_LEAVEBUF is returned.
125 *
126 * Variables Used:
127 * ---------------
128 * username - Sybase username (overridden by -U)
129 * password - Sybase password (overridden by -P)
130 * server - Sybase server (overridden by -S)
131 * database - Sybase database (overridden by -D)
132 * interfaces - Location of sybase interfaces file (overridden by -I)
133 * chained - transaction mode chained or unchained (-n on|off)
134 * preserve_context - Do not preserve database context when reconnecting
135 * to a server but use the default database of the
136 * specified login (-c)
137 *
138 * appname -N
139 * keytab_file -K
140 * principal -R
141 * secure_options -V
142 * secmech -Z
143 * query_timeout -Q
144 * login_timeout -T
145 */
cmd_connect(argc,argv)146 int cmd_connect( argc, argv )
147 int argc ;
148 char *argv[] ;
149 {
150 char *database ;
151 char *username ;
152 char *password ;
153 char *server ;
154 char *interfaces ;
155 char *packet_size ;
156 char *autouse ;
157 char *session ;
158 char *charset ;
159 char *language ;
160 char *encryption ;
161 char *hostname ;
162 char *password_retry;
163 char *tds_version;
164 char *chained;
165 char *appname;
166 char *cp;
167 extern char *sqsh_optarg ;
168 extern int sqsh_optind ;
169 char use_database[128] ;
170 char *usedbcheck ;
171 int c ;
172 int have_error = False ;
173 int preserve_context = True ;
174 char orig_password[SQSH_PASSLEN+1];
175 int password_changed = False;
176 char sqlbuf[64];
177 char passbuf[64];
178 int len;
179 int exit_status;
180 int i;
181 int return_code;
182 CS_INT version;
183 /* sqsh-2.1.6 - New variables */
184 char *keytab_file;
185 char *principal;
186 char *secmech;
187 char *secure_options;
188 char *login_timeout;
189 char *query_timeout;
190 CS_INT SybTimeOut;
191 CS_BOOL NetAuthRequired;
192 varbuf_t *exp_buf = NULL;
193
194 /* sqsh-2.2.0 - New variables for TDS debugging with ct_debug() */
195 #if defined(DEBUG) && defined(CS_SET_DBG_FILE) && defined(CS_SET_PROTOCOL_FILE)
196 char *debug_tds_logdata;
197 char *debug_tds_capture;
198 #endif
199
200 #if defined(CTLIB_SIGPOLL_BUG) && defined(F_SETOWN)
201 int ctlib_fd;
202 #endif
203
204 CS_RETCODE result_type;
205 CS_LOCALE *locale = NULL;
206 CS_COMMAND *cmd;
207 #if !defined(_WINDOZE_)
208 CS_INT netio_type;
209 #endif
210 CS_INT con_status;
211 CS_INT retcode;
212
213
214 /*
215 * If we are already connected to the database, then don't
216 * bother to do anything.
217 */
218 if (g_connection != NULL)
219 {
220 return CMD_LEAVEBUF ;
221 }
222
223 /*
224 * First, we want to establish an environment "transaction". This
225 * will allow us to change whichever environment variables we
226 * would like, then roll things back if we want to fail.
227 */
228 env_tran( g_env );
229
230 /*
231 * Parse the command line options.
232 * sqsh-2.1.6 - New options added and case evaluation neatly ordered.
233 * sqsh-2.2.0 - -J option added. -I requires an optarg.
234 */
235 while ((c = sqsh_getopt( argc, argv, "A:cD:G:I:J:K:n:N:P;Q:R:S:T:U:V;XZ;z:" )) != EOF)
236 {
237 switch( c )
238 {
239 case 'A' : /* sqsh-2.2.0 - Option added */
240 if (env_put( g_env, "packet_size", sqsh_optarg, ENV_F_TRAN ) == False)
241 {
242 fprintf( stderr, "\\connect: -A: %s\n", sqsh_get_errstr() );
243 have_error = True;
244 }
245 break;
246 case 'c' :
247 preserve_context = False ;
248 break ;
249 case 'D' :
250 if (env_put( g_env, "database", sqsh_optarg, ENV_F_TRAN ) == False)
251 {
252 fprintf( stderr, "\\connect: -D: %s\n", sqsh_get_errstr() );
253 have_error = True;
254 }
255 break;
256 case 'G' : /* sqsh-2.2.0 - Option added */
257 if (env_put( g_env, "tds_version", sqsh_optarg, ENV_F_TRAN ) == False)
258 {
259 fprintf( stderr, "\\connect: -G: %s\n", sqsh_get_errstr() );
260 have_error = True;
261 }
262 break;
263 case 'I' :
264 if (env_put( g_env, "interfaces", sqsh_optarg, ENV_F_TRAN ) == False)
265 {
266 fprintf( stderr, "\\connect: -I: %s\n", sqsh_get_errstr() );
267 have_error = True;
268 }
269 break ;
270 case 'J' : /* sqsh-2.2.0 - Option added */
271 if (env_put( g_env, "charset", sqsh_optarg, ENV_F_TRAN ) == False)
272 {
273 fprintf( stderr, "\\connect: -J: %s\n", sqsh_get_errstr() );
274 have_error = True;
275 }
276 break ;
277 case 'K' : /* sqsh-2.1.6 */
278 if (env_put( g_env, "keytab_file", sqsh_optarg, ENV_F_TRAN ) == False)
279 {
280 fprintf( stderr, "\\connect: -K: %s\n", sqsh_get_errstr() );
281 have_error = True;
282 }
283 break ;
284 case 'n' :
285 if (env_put( g_env, "chained", sqsh_optarg, ENV_F_TRAN ) == False)
286 {
287 fprintf( stderr, "\\connect: -n: %s\n", sqsh_get_errstr() );
288 have_error = True;
289 }
290 break ;
291 case 'N' : /* sqsh-2.1.5 */
292 if (env_put( g_env, "appname", sqsh_optarg, ENV_F_TRAN ) == False)
293 {
294 fprintf( stderr, "\\connect: -N: %s\n", sqsh_get_errstr() );
295 have_error = True;
296 }
297 break;
298 case 'P' :
299 if (g_password_set == True && g_password != NULL)
300 strcpy( orig_password, g_password );
301 password_changed = True;
302
303 if (env_put( g_env, "password", sqsh_optarg, ENV_F_TRAN ) == False)
304 {
305 fprintf( stderr, "\\connect: -P: %s\n", sqsh_get_errstr() );
306 have_error = True;
307 }
308 break;
309 case 'Q' : /* sqsh-2.1.6 */
310 if (env_put( g_env, "query_timeout", sqsh_optarg, ENV_F_TRAN ) == False)
311 {
312 fprintf( stderr, "\\connect: -Q: %s\n", sqsh_get_errstr() );
313 have_error = True;
314 }
315 break ;
316 case 'R' : /* sqsh-2.1.6 */
317 if (env_put( g_env, "principal", sqsh_optarg, ENV_F_TRAN ) == False)
318 {
319 fprintf( stderr, "\\connect: -R: %s\n", sqsh_get_errstr() );
320 have_error = True;
321 }
322 break ;
323 case 'S' :
324 if (env_put( g_env, "DSQUERY", sqsh_optarg, ENV_F_TRAN ) == False)
325 {
326 fprintf( stderr, "\\connect: -S: %s\n", sqsh_get_errstr() );
327 have_error = True;
328 }
329 break ;
330 case 'T' : /* sqsh-2.1.6 */
331 if (env_put( g_env, "login_timeout", sqsh_optarg, ENV_F_TRAN ) == False)
332 {
333 fprintf( stderr, "\\connect: -T: %s\n", sqsh_get_errstr() );
334 have_error = True;
335 }
336 break ;
337 case 'U' :
338 if (env_put( g_env, "username", sqsh_optarg, ENV_F_TRAN ) == False)
339 {
340 fprintf( stderr, "\\connect: -U: %s\n", sqsh_get_errstr() );
341 have_error = True;
342 }
343 break ;
344 case 'V' : /* sqsh-2.1.6 */
345 if (sqsh_optarg == NULL || *sqsh_optarg == '\0')
346 return_code = env_put( g_env, "secure_options", "u", ENV_F_TRAN);
347 else
348 return_code = env_put( g_env, "secure_options", sqsh_optarg, ENV_F_TRAN);
349
350 if (return_code == False)
351 {
352 fprintf( stderr, "\\connect: -V: %s\n", sqsh_get_errstr() );
353 have_error = True;
354 }
355 break ;
356 case 'X' : /* sqsh-2.1.9 */
357 if (env_put( g_env, "encryption", "1", ENV_F_TRAN ) == False)
358 {
359 fprintf( stderr, "\\connect: -X: %s\n", sqsh_get_errstr() );
360 have_error = True;
361 }
362 break ;
363 case 'z' : /* sqsh-2.2.0 - Option added */
364 if (env_put( g_env, "language", sqsh_optarg, ENV_F_TRAN ) == False)
365 {
366 fprintf( stderr, "\\connect: -z: %s\n", sqsh_get_errstr() );
367 have_error = True;
368 }
369 break ;
370 case 'Z' : /* sqsh-2.1.6 */
371 if (sqsh_optarg == NULL || *sqsh_optarg == '\0')
372 return_code = env_put( g_env, "secmech", "default", ENV_F_TRAN);
373 else
374 return_code = env_put( g_env, "secmech", sqsh_optarg, ENV_F_TRAN);
375 if (return_code == False)
376 {
377 fprintf( stderr, "\\connect: -Z: %s\n", sqsh_get_errstr() );
378 have_error = True;
379 }
380 break ;
381
382 default :
383 fprintf( stderr, "\\connect: %s\n", sqsh_get_errstr() ) ;
384 have_error = True ;
385 break ;
386 }
387 }
388
389 /*
390 * If there are any options left on the end of the line, then we have an error.
391 * sqsh-2.1.6 - New options added to the list.
392 * sqsh-2.2.0 - -J charset added to the list.
393 */
394 if( have_error || sqsh_optind != argc )
395 {
396 fprintf( stderr,
397 "Use: \\connect [-A packet size] [-c] [-I interfaces] [-U username] [-P pwd] [-S server]\n"
398 " [-D database ] [-G tds version] [-J charset] [-N appname] [-n {on|off}]\n"
399 " [-Q query_timeout] [-T login_timeout] [-K keytab_file] [-R principal]\n"
400 " [-V [bcdimoqru]] [-X] [-z language] [-Z [secmech|default|none]]\n"
401 ) ;
402
403 env_rollback( g_env );
404 return CMD_FAIL;
405 }
406
407 /*
408 * Retrieve the memory addresses of the various environment variables that
409 * may be used to connect. These may be overridden with flags to connect.
410 * So basically our environment variables point to memory addresses. The
411 * contents can still be modified by the execution of the $session file.
412 */
413 env_get( g_env, "username", &username ) ;
414 env_get( g_env, "DSQUERY", &server ) ;
415 env_get( g_env, "interfaces", &interfaces ) ;
416 env_get( g_env, "packet_size", &packet_size ) ;
417 env_get( g_env, "autouse", &autouse ) ;
418 env_get( g_env, "database", &database ) ;
419 env_get( g_env, "charset", &charset ) ;
420 env_get( g_env, "language", &language ) ;
421 env_get( g_env, "encryption", &encryption ) ;
422 env_get( g_env, "hostname", &hostname ) ;
423 env_get( g_env, "password_retry", &password_retry ) ;
424 env_get( g_env, "tds_version", &tds_version ) ;
425 env_get( g_env, "chained", &chained ) ;
426 env_get( g_env, "appname", &appname);
427 /* sqsh-2.1.6 - New variables */
428 env_get( g_env, "keytab_file", &keytab_file);
429 env_get( g_env, "login_timeout", &login_timeout);
430 env_get( g_env, "principal", &principal);
431 env_get( g_env, "query_timeout", &query_timeout);
432 env_get( g_env, "secmech", &secmech);
433 env_get( g_env, "secure_options", &secure_options);
434
435 password = g_password;
436
437 /*
438 * Allocate an expansion buffer for general use.
439 */
440 if ((exp_buf = varbuf_create( 512 )) == NULL)
441 {
442 fprintf( stderr, "sqsh: %s\n", sqsh_get_errstr() );
443 sqsh_exit( 255 );
444 }
445
446 /*
447 * sqsh-2.2.0 - Install a signal handler to catch SIGINT during cmd_connect.
448 * The current signals are saved first and restored at the end of cmd_connect.
449 */
450 sig_save();
451 sig_install( SIGINT, connect_run_sigint, (void *) NULL, 0);
452 sg_interrupted = False;
453 if (SETJMP( sg_jmp_buf ) != 0)
454 goto connect_fail;
455
456 /*
457 * If the $session variable is set and the path that it contains
458 * is a valid path name, then we want to execute the contents of
459 * this file immediately, and prior to connecting to the database.
460 */
461 env_get( g_env, "session", &session );
462 if ( session != NULL && *session != '\0' )
463 {
464 if (sqsh_expand( session, exp_buf, 0 ) != False)
465 {
466 session = varbuf_getstr( exp_buf );
467 if (session != NULL && access( session, R_OK ) != -1)
468 {
469 DBG(sqsh_debug(DEBUG_ENV, "cmd_connect: session file is %s.\n",
470 session);)
471 cp = malloc (strlen(session) + 12) ;
472 sprintf (cp, "\\loop -n %s", session);
473 if ((jobset_run( g_jobset, cp, &exit_status )) == -1 ||
474 exit_status == CMD_FAIL)
475 {
476 fprintf( stderr, "%s\n", sqsh_get_errstr() );
477 free (cp);
478 goto connect_fail;
479 }
480 free (cp);
481 }
482 }
483 else
484 {
485 fprintf( stderr, "sqsh: Error expanding $session: %s\n",
486 sqsh_get_errstr() );
487 goto connect_fail;
488 }
489 }
490
491 /*
492 * sqsh-2.1.6 feature - Network Authentication
493 *
494 * If network authentication is requested by passing on options
495 * -V and/or -Z, then we do not need to set the password.
496 *
497 * Note: Options -K and/or -R do not determine to use network
498 * authentication, they are just helper variables to netauth.
499 * Only the parameters -V and/or -Z will request network authentication.
500 * This code relies on the fact that sqsh will fill in default values for
501 * the parameters -V and -Z if the user does not supply a parameter value.
502 * (Parameter values for -V and -Z are optional)
503 * If a value of "none" is passed on to -Z, then we ignore all Network auth
504 * settings and will login using a password altogether.
505 */
506 if (secmech != NULL && strcasecmp(secmech, "none") == 0)
507 {
508 env_put (g_env, secmech, NULL, ENV_F_TRAN);
509 env_put (g_env, secure_options, NULL, ENV_F_TRAN);
510 secmech = NULL;
511 secure_options = NULL;
512 }
513 if ((secmech != NULL && *secmech != '\0') ||
514 (secure_options != NULL && *secure_options != '\0'))
515 {
516 NetAuthRequired = CS_TRUE;
517 DBG(sqsh_debug(DEBUG_ERROR, "cmd_connect: Network user authentication required.\n");)
518 }
519 else
520 {
521 NetAuthRequired = CS_FALSE;
522 DBG(sqsh_debug(DEBUG_ERROR, "cmd_connect: Regular user authentication using password.\n");)
523 if (password != NULL && strcmp( password, "-" ) == 0)
524 {
525 if (sqsh_stdin_fgets( passbuf, sizeof(passbuf) ) != NULL)
526 {
527 cp = strchr( passbuf, (int)'\n' );
528 if (cp != NULL)
529 {
530 *cp = '\0';
531 }
532 env_put( g_env, "password", passbuf, ENV_F_TRAN );
533 password = g_password;
534 }
535 }
536
537 /*
538 * If we don't have a password to use (i.e. the $password isn't set
539 * or -P was not supplied), then ask the user for one.
540 */
541 else if (g_password_set == False)
542 {
543 len = sqsh_getinput( "Password: ", passbuf, sizeof(passbuf), 0);
544
545 if (len < 0)
546 {
547 if (len == -1)
548 {
549 fprintf( stderr, "\\connect: %s\n", sqsh_get_errstr() );
550 }
551 goto connect_fail;
552 }
553
554 if (passbuf[len] == '\n')
555 passbuf[len] = '\0';
556
557 env_put( g_env, "password", passbuf, ENV_F_TRAN );
558 }
559 }
560
561 /*
562 * If the $database variable is set *prior* to connecting to the
563 * database, then that indicates that we are to automatically
564 * connect to $database prior to returning.
565 */
566 if( preserve_context && database != NULL && *database != '\0' )
567 {
568 strncpy( use_database, database, sizeof(use_database)-1 ) ;
569 use_database[sizeof(use_database)-1] = '\0';
570 autouse = use_database ;
571 }
572
573 /*
574 * In order to keep the error handler from exiting if something
575 * goes wrong here, we need to mark ourselves as being in the
576 * middle of logging in.
577 */
578 sg_login = True ;
579
580 /*
581 * The context for the application need only be created once, we'll
582 * re-use it as needed.
583 */
584 if (g_context == NULL)
585 {
586 /*-- Allocate a new context structure --*/
587 /*-- mpeppler 4/9/2004
588 we loop through the CS_VERSION_xxx values to try
589 to use the highest one we find */
590
591 retcode = CS_FAIL;
592
593 #if defined(CS_CURRENT_VERSION)
594 if(retcode != CS_SUCCEED) {
595 g_cs_ver = CS_CURRENT_VERSION;
596 retcode = cs_ctx_alloc(g_cs_ver, &g_context);
597 }
598 #endif
599 #if defined(CS_VERSION_160)
600 if(retcode != CS_SUCCEED) {
601 g_cs_ver = CS_VERSION_160;
602 retcode = cs_ctx_alloc(g_cs_ver, &g_context);
603 }
604 #endif
605 #if defined(CS_VERSION_157)
606 if(retcode != CS_SUCCEED) {
607 g_cs_ver = CS_VERSION_157;
608 retcode = cs_ctx_alloc(g_cs_ver, &g_context);
609 }
610 #endif
611 #if defined(CS_VERSION_155)
612 if(retcode != CS_SUCCEED) {
613 g_cs_ver = CS_VERSION_155;
614 retcode = cs_ctx_alloc(g_cs_ver, &g_context);
615 }
616 #endif
617 #if defined(CS_VERSION_150)
618 if(retcode != CS_SUCCEED) {
619 g_cs_ver = CS_VERSION_150;
620 retcode = cs_ctx_alloc(g_cs_ver, &g_context);
621 }
622 #endif
623 #if defined(CS_VERSION_125)
624 if(retcode != CS_SUCCEED) {
625 g_cs_ver = CS_VERSION_125;
626 retcode = cs_ctx_alloc(g_cs_ver, &g_context);
627 }
628 #endif
629 #if defined(CS_VERSION_120)
630 if(retcode != CS_SUCCEED) {
631 g_cs_ver = CS_VERSION_120;
632 retcode = cs_ctx_alloc(g_cs_ver, &g_context);
633 }
634 #endif
635 #if defined(CS_VERSION_110)
636 if(retcode != CS_SUCCEED) {
637 g_cs_ver = CS_VERSION_110;
638 retcode = cs_ctx_alloc(g_cs_ver, &g_context);
639 }
640 #endif
641
642 if(retcode != CS_SUCCEED) {
643 g_cs_ver = CS_VERSION_100;
644 retcode = cs_ctx_alloc(g_cs_ver, &g_context);
645 }
646 DBG(sqsh_debug(DEBUG_TDS, "cmd_connect: g_cs_ver (CS_VERSION) set to: %d\n", g_cs_ver);)
647
648 if (retcode != CS_SUCCEED) /* nothing worked... */
649 goto connect_fail;
650
651 /*-- Install callbacks into context --*/
652 if (cs_config( g_context, /* Context */
653 CS_SET, /* Action */
654 CS_MESSAGE_CB, /* Property */
655 (CS_VOID*)syb_cs_cb, /* Buffer */
656 CS_UNUSED, /* Buffer length */
657 (CS_INT*)NULL /* Output length */
658 ) != CS_SUCCEED)
659 {
660 fprintf( stderr, "\\connect: Unable to install message callback\n" );
661 goto connect_fail;
662 }
663
664 /*-- Initialize the context --*/
665 if (ct_init( g_context, g_cs_ver ) != CS_SUCCEED)
666 goto connect_fail;
667
668 if (ct_callback( g_context, /* Context */
669 (CS_CONNECTION*)NULL, /* Connection */
670 CS_SET, /* Action */
671 CS_CLIENTMSG_CB, /* Type */
672 (CS_VOID*)syb_client_cb /* Callback Pointer */
673 ) != CS_SUCCEED)
674 goto connect_fail;
675
676 if (ct_callback( g_context, /* Context */
677 (CS_CONNECTION*)NULL, /* Connection */
678 CS_SET, /* Action */
679 CS_SERVERMSG_CB, /* Type */
680 (CS_VOID*)syb_server_cb /* Callback Pointer */
681 ) != CS_SUCCEED)
682 goto connect_fail;
683
684 /*
685 * Set the I/O type to syncronous (things would really freak out
686 * in an async environment).
687 */
688 #if !defined(_WINDOZE_)
689 netio_type = CS_SYNC_IO;
690 if (ct_config( g_context, /* Context */
691 CS_SET, /* Action */
692 CS_NETIO, /* Property */
693 (CS_VOID*)&netio_type, /* Buffer */
694 CS_UNUSED, /* Buffer Length */
695 NULL /* Output Length */
696 ) != CS_SUCCEED)
697 goto connect_fail;
698 #endif
699
700 /*
701 * sqsh-2.1.6 feature - Set CS_TIMEOUT and CS_LOGIN_TIMEOUT
702 */
703 if (query_timeout != NULL && *query_timeout != '\0' && atoi (query_timeout) > 0)
704 {
705 SybTimeOut = atoi (query_timeout);
706 if (ct_config( g_context, /* Context */
707 CS_SET, /* Action */
708 CS_TIMEOUT, /* Property */
709 (CS_VOID*)&SybTimeOut, /* Buffer */
710 CS_UNUSED, /* Buffer Length */
711 NULL /* Output Length */
712 ) != CS_SUCCEED)
713 {
714 DBG(sqsh_debug(DEBUG_ERROR, "ct_config: Failed to set CS_TIMEOUT to %d seconds.\n", SybTimeOut);)
715 goto connect_fail;
716 }
717 DBG(sqsh_debug(DEBUG_ERROR, "ct_config: CS_TIMEOUT set to %d seconds.\n", SybTimeOut);)
718 }
719
720 if (login_timeout != NULL && *login_timeout != '\0' && atoi (login_timeout) > 0)
721 {
722 SybTimeOut = atoi (login_timeout);
723 if (ct_config( g_context, /* Context */
724 CS_SET, /* Action */
725 CS_LOGIN_TIMEOUT, /* Property */
726 (CS_VOID*)&SybTimeOut, /* Buffer */
727 CS_UNUSED, /* Buffer Length */
728 NULL /* Output Length */
729 ) != CS_SUCCEED)
730 {
731 DBG(sqsh_debug(DEBUG_ERROR, "ct_config: Failed to set CS_LOGIN_TIMEOUT to %d seconds.\n", SybTimeOut);)
732 goto connect_fail;
733 }
734 DBG(sqsh_debug(DEBUG_ERROR, "ct_config: CS_LOGIN_TIMEOUT set to %d seconds.\n", SybTimeOut);)
735 }
736
737 /*
738 * sqsh-2.1.6/2.1.7 - If an interfaces file is specified, make sure
739 * environment variables get expanded correctly.
740 */
741 if ( interfaces != NULL && *interfaces != '\0' )
742 {
743 if (sqsh_expand( interfaces, exp_buf, 0 ) != False)
744 {
745 cp = varbuf_getstr( exp_buf );
746 DBG(sqsh_debug(DEBUG_ENV, "cmd_connect: Interfaces file is %s.\n", cp);)
747 /*
748 * If the user overrode the default interfaces file location,
749 * then configure the interfaces file as such.
750 */
751 if (cp == NULL || ct_config( g_context, /* Context */
752 CS_SET, /* Action */
753 CS_IFILE, /* Property */
754 (CS_VOID*)cp, /* Buffer */
755 CS_NULLTERM, /* Buffer Length */
756 NULL /* Output Length */
757 ) != CS_SUCCEED)
758 goto connect_fail;
759 }
760 else
761 {
762 fprintf( stderr, "sqsh: Error expanding $interfaces: %s\n",
763 sqsh_get_errstr() );
764 goto connect_fail;
765 }
766 }
767
768 } /* if (g_context == NULL) */
769
770 /*
771 * Allocate a new connection and initialize the username and
772 * password for the connection.
773 */
774 if (ct_con_alloc( g_context, &g_connection ) != CS_SUCCEED)
775 goto connect_fail;
776
777 /*-- Set username --*/
778 if (username != NULL && *username != '\0') /* sqsh-2.1.6 sanity check */
779 {
780 if (ct_con_props( g_connection, /* Connection */
781 CS_SET, /* Action */
782 CS_USERNAME, /* Property */
783 (CS_VOID*)username, /* Buffer */
784 CS_NULLTERM, /* Buffer Length */
785 (CS_INT*)NULL /* Output Length */
786 ) != CS_SUCCEED)
787 goto connect_fail;
788 }
789
790 /*
791 * sqsh-2.1.6 - If a keytab_file is specified, make sure environment
792 * variables get expanded correctly.
793 */
794 if ( keytab_file != NULL && *keytab_file != '\0' )
795 {
796 if (sqsh_expand( keytab_file, exp_buf, 0 ) != False)
797 {
798 cp = varbuf_getstr( exp_buf );
799 DBG(sqsh_debug(DEBUG_ENV, "cmd_connect: Keytab_file is %s.\n", cp);)
800 }
801 else
802 {
803 fprintf( stderr, "sqsh: Error expanding $keytab_file: %s\n",
804 sqsh_get_errstr() );
805 cp = NULL;
806 }
807 }
808 else
809 cp = NULL;
810
811 /*-- sqsh-2.1.6 feature - Use network authentication or set password --*/
812 if (NetAuthRequired == CS_TRUE)
813 {
814 if (SetNetAuth ( g_connection, /* Connection */
815 principal, /* Server principal */
816 cp, /* Expanded DCE keytab file */
817 secmech, /* Security mechanism */
818 secure_options /* Security options */
819 ) != CS_SUCCEED)
820 goto connect_fail;
821 }
822 else
823 {
824 if (ct_con_props( g_connection, /* Connection */
825 CS_SET, /* Action */
826 CS_PASSWORD, /* Property */
827 (CS_VOID*)g_password, /* Buffer */
828 (g_password == NULL) ? CS_UNUSED : CS_NULLTERM,
829 (CS_INT*)NULL /* Output Length */
830 ) != CS_SUCCEED)
831 goto connect_fail;
832 }
833
834 /*-- Set application name --*/
835 if (appname != NULL && *appname != '\0') /* sqsh-2.1.5 */
836 {
837 if (ct_con_props( g_connection, /* Connection */
838 CS_SET, /* Action */
839 CS_APPNAME, /* Property */
840 (CS_VOID*)appname, /* Buffer */
841 CS_NULLTERM, /* Buffer Length */
842 (CS_INT*)NULL /* Output Length */
843 ) != CS_SUCCEED)
844 goto connect_fail;
845 }
846
847 if (tds_version != NULL && *tds_version != '\0') /* sqsh-2.1.6 fix on *tds_version */
848 {
849 if (strcmp(tds_version, "4.0") == 0)
850 version = CS_TDS_40;
851 else if (strcmp(tds_version, "4.2") == 0)
852 version = CS_TDS_42;
853 else if (strcmp(tds_version, "4.6") == 0)
854 version = CS_TDS_46;
855 else if (strcmp(tds_version, "4.9.5") == 0)
856 version = CS_TDS_495;
857 else if (strcmp(tds_version, "5.0") == 0)
858 version = CS_TDS_50;
859 #if !defined(CS_TDS_50)
860 /* Then we use freetds which uses enum instead of defines */
861 else if (strcmp(tds_version, "7.0") == 0)
862 version = CS_TDS_70;
863 else if (strcmp(tds_version, "7.1") == 0)
864 version = CS_TDS_71;
865 else if (strcmp(tds_version, "7.2") == 0)
866 version = CS_TDS_72;
867 else if (strcmp(tds_version, "7.3") == 0)
868 version = CS_TDS_73;
869 else if (strcmp(tds_version, "7.4") == 0)
870 version = CS_TDS_74;
871 else if (strcmp(tds_version, "8.0") == 0)
872 version = CS_TDS_71;
873 #endif
874 else version = CS_TDS_50; /* default version */
875
876
877 if (ct_con_props(g_connection, CS_SET, CS_TDS_VERSION,
878 (CS_VOID*)&version, CS_UNUSED, (CS_INT*)NULL) != CS_SUCCEED)
879 goto connect_fail;
880 }
881
882 /*-- Hostname --*/
883 if (hostname != NULL && *hostname != '\0')
884 {
885 if (ct_con_props( g_connection, /* Connection */
886 CS_SET, /* Action */
887 CS_HOSTNAME, /* Property */
888 (CS_VOID*)hostname, /* Buffer */
889 CS_NULLTERM, /* Buffer Length */
890 (CS_INT*)NULL /* Output Length */
891 ) != CS_SUCCEED)
892 goto connect_fail;
893 }
894
895 /*-- Packet Size --*/
896 if (packet_size != NULL && *packet_size != '\0') /* sqsh-2.1.6 sanity check */
897 {
898 i = atoi(packet_size);
899 if (ct_con_props( g_connection, /* Connection */
900 CS_SET, /* Action */
901 CS_PACKETSIZE, /* Property */
902 (CS_VOID*)&i, /* Buffer */
903 CS_UNUSED, /* Buffer Length */
904 (CS_INT*)NULL /* Output Length */
905 ) != CS_SUCCEED)
906 goto connect_fail;
907 }
908
909 /*-- Encryption --*/
910 if (encryption != NULL && *encryption == '1')
911 {
912 i = CS_TRUE;
913 if (ct_con_props( g_connection, /* Connection */
914 CS_SET, /* Action */
915 CS_SEC_ENCRYPTION, /* Property */
916 (CS_VOID*)&i, /* Buffer */
917 CS_UNUSED, /* Buffer Length */
918 (CS_INT*)NULL /* Output Length */
919 ) != CS_SUCCEED)
920 goto connect_fail;
921
922 #if defined(CS_SEC_EXTENDED_ENCRYPTION)
923 /*
924 * sqsh-2.1.9: Enable extended password encryption to be able to
925 * connect to ASE servers with 'net password encryption reqd'
926 * configured to 2 (RSA).
927 */
928 if (ct_con_props( g_connection, /* Connection */
929 CS_SET, /* Action */
930 CS_SEC_EXTENDED_ENCRYPTION, /* Property */
931 (CS_VOID*)&i, /* Buffer */
932 CS_UNUSED, /* Buffer Length */
933 (CS_INT*)NULL /* Output Length */
934 ) != CS_SUCCEED)
935 goto connect_fail;
936 #endif
937
938 }
939
940 /*
941 * The following section initializes all locale type information.
942 * First, we need to create a locale structure and initialize it
943 * with information.
944 */
945 if (cs_loc_alloc( g_context, &locale ) != CS_SUCCEED)
946 goto connect_fail;
947
948 /*-- Initialize --*/
949 if (cs_locale( g_context, /* Context */
950 CS_SET, /* Action */
951 locale, /* Locale Structure */
952 CS_LC_ALL, /* Property */
953 (CS_CHAR*)NULL, /* Buffer */
954 CS_UNUSED, /* Buffer Length */
955 (CS_INT*)NULL /* Output Length */
956 ) != CS_SUCCEED)
957 goto connect_fail;
958
959 /*-- Language --*/
960 if( language != NULL && *language != '\0' )
961 {
962 if (cs_locale( g_context, /* Context */
963 CS_SET, /* Action */
964 locale, /* Locale Structure */
965 CS_SYB_LANG, /* Property */
966 (CS_CHAR*)language, /* Buffer */
967 CS_NULLTERM, /* Buffer Length */
968 (CS_INT*)NULL /* Output Length */
969 ) != CS_SUCCEED)
970 goto connect_fail;
971 }
972
973 /*-- Character Set --*/
974 if (charset != NULL && *charset != '\0') /* sqsh-2.1.6 sanity check */
975 {
976 if (cs_locale( g_context, /* Context */
977 CS_SET, /* Action */
978 locale, /* Locale Structure */
979 CS_SYB_CHARSET, /* Property */
980 (CS_CHAR*)charset, /* Buffer */
981 CS_NULLTERM, /* Buffer Length */
982 (CS_INT*)NULL /* Output Length */
983 ) != CS_SUCCEED)
984 goto connect_fail;
985 }
986
987 /*-- Locale Property --*/
988 if (ct_con_props( g_connection, /* Connection */
989 CS_SET, /* Action */
990 CS_LOC_PROP, /* Property */
991 (CS_VOID*)locale, /* Buffer */
992 CS_UNUSED, /* Buffer Length */
993 (CS_INT*)NULL /* Output Length */
994 ) != CS_SUCCEED)
995 goto connect_fail;
996
997 /* Handle case where server is defined as host:port */
998 /*
999 * sqsh-2.5 - Work around a bug in FreeTDS and do not call ct_con_props
1000 * for server:port connections but let ct_connect handle it.
1001 * OpenClient supports an optional filter that can be specified as third
1002 * parameter like host:port:filter. Filters are defined in libtcl.cfg.
1003 */
1004 #if defined(CS_SERVERADDR) && defined(CS_TDS_50)
1005 if ( server != NULL && (cp = strchr(server, ':')) != NULL )
1006 {
1007 char *cp2;
1008
1009 *cp = ' ';
1010 if ( (cp2 = strchr(cp+1, ':')) != NULL) /* Optional filter specified? */
1011 *cp2 = ' ';
1012
1013 if (ct_con_props( g_connection,
1014 CS_SET,
1015 CS_SERVERADDR,
1016 (CS_VOID*)server,
1017 CS_NULLTERM,
1018 (CS_INT*)NULL
1019 ) != CS_SUCCEED)
1020 goto connect_fail;
1021
1022 if (cp2 != NULL)
1023 *cp2 = '\0'; /* Remove optional filter from the servername */
1024 *cp = ':'; /* Put a ':' back into the display servername */
1025
1026 #if defined(CS_SSLVALIDATE_CB)
1027 if (ct_callback( g_context, /* Context */
1028 (CS_CONNECTION*)NULL, /* Connection */
1029 CS_SET, /* Action */
1030 CS_SSLVALIDATE_CB, /* Type */
1031 (CS_VOID*)validate_srvname_cb /* Callback Pointer */
1032 ) != CS_SUCCEED)
1033 goto connect_fail;
1034 #endif
1035 }
1036 #endif
1037
1038 #if defined(DEBUG) && defined(CS_SET_DBG_FILE) && defined(CS_SET_PROTOCOL_FILE)
1039 /*
1040 * sqsh-2.2.0 - Setup TDS debugging using a logdata file and or a capture file
1041 * for tracing TDS packets.
1042 */
1043 if ( sqsh_debug_show (DEBUG_TDS) )
1044 {
1045 /*
1046 * Note, this requires the CT-lib development libraries to be linked/loaded with sqsh.
1047 */
1048 env_get (g_env, "debug_tds_logdata", &debug_tds_logdata);
1049 if (debug_tds_logdata != NULL && *debug_tds_logdata != '\0')
1050 {
1051 if (sqsh_expand( debug_tds_logdata, exp_buf, 0 ) != False)
1052 {
1053 cp = varbuf_getstr( exp_buf );
1054 if (ct_debug ( g_context, /* Context */
1055 NULL, /* Connection */
1056 CS_SET_DBG_FILE, /* Action */
1057 CS_UNUSED, /* Flag */
1058 cp, /* Buffer value */
1059 CS_NULLTERM /* String '\0' terminated */
1060 ) != CS_SUCCEED)
1061 fprintf (stderr, "\\connect: ct_debug - Unable to set CS_SET_DBG_FILE to %s\n", cp);
1062 else
1063 {
1064 fprintf (stdout, "\\connect: ct_debug - Successfully set CS_SET_DBG_FILE to %s\n", cp);
1065
1066 if (ct_debug ( g_context, /* Context */
1067 g_connection, /* Connection */
1068 CS_SET_FLAG, /* Action */
1069 CS_DBG_ALL, /* Flag */
1070 NULL, /* Buffer value */
1071 CS_UNUSED /* Buffer length */
1072 ) != CS_SUCCEED)
1073 fprintf (stderr, "\\connect: ct_debug - Unable to set flag CS_DBG_ALL\n");
1074 else
1075 fprintf (stdout, "\\connect: ct_debug - Flag CS_DBG_ALL successfully set\n");
1076 }
1077 }
1078 else
1079 {
1080 fprintf( stderr, "sqsh: Error expanding $debug_tds_logdata: %s\n", sqsh_get_errstr() );
1081 }
1082 }
1083
1084 /*
1085 * For protocol tracing regular CT-lib libraries will do.
1086 * The created trace file can be decoded using Ribo.
1087 */
1088 env_get (g_env, "debug_tds_capture", &debug_tds_capture);
1089 if (debug_tds_capture != NULL && *debug_tds_capture != '\0')
1090 {
1091 if (sqsh_expand( debug_tds_capture, exp_buf, 0 ) != False)
1092 {
1093 cp = varbuf_getstr( exp_buf );
1094 if (ct_debug ( NULL, /* Context */
1095 g_connection, /* Connection */
1096 CS_SET_PROTOCOL_FILE, /* Action */
1097 CS_UNUSED, /* Flag */
1098 cp, /* Buffer value */
1099 CS_NULLTERM /* String '\0' terminated */
1100 ) != CS_SUCCEED)
1101 fprintf (stderr, "\\connect: ct_debug - Unable to set CS_SET_PROTOCOL_FILE to %s\n", cp);
1102 else
1103 {
1104 fprintf (stdout, "\\connect: ct_debug - Successfully set CS_SET_PROTOCOL_FILE to %s\n", cp);
1105 if (ct_debug ( NULL, /* Context */
1106 g_connection, /* Connection */
1107 CS_SET_FLAG, /* Action */
1108 CS_DBG_PROTOCOL, /* Flag */
1109 NULL, /* Buffer value */
1110 CS_UNUSED /* Buffer length */
1111 ) != CS_SUCCEED)
1112 fprintf (stderr, "\\connect: ct_debug - Unable to set falg CS_DBG_PROTOCOL\n");
1113 else
1114 fprintf (stdout, "\\connect: ct_debug - Flag CS_DBG_PROTOCOL successfully set\n");
1115 }
1116 }
1117 else
1118 {
1119 fprintf( stderr, "sqsh: Error expanding $debug_tds_capture: %s\n", sqsh_get_errstr() );
1120 }
1121 }
1122 }
1123 #elif defined(DEBUG)
1124 if ( sqsh_debug_show (DEBUG_TDS) )
1125 {
1126 fprintf( stderr, "\\connect: ct_debug - TDS debugging is not supported in this version of CT-lib\n" );
1127 }
1128 #endif
1129
1130 /*
1131 * We sit in a loop and attempt to connect while we are getting
1132 * "Login failed" messages.
1133 */
1134 do
1135 {
1136 /*
1137 * Reset the failed login indicater. This will be set by
1138 * the server message handler if the login failed.
1139 */
1140 sg_login_failed = False;
1141
1142 /*
1143 * Attempt to establish the connection to the database. If this
1144 * fails the error messages will be displayed by the message and/or
1145 * error handlers.
1146 */
1147 if (ct_connect( g_connection, server,
1148 (server == NULL) ? CS_UNUSED : CS_NULLTERM ) != CS_SUCCEED)
1149 {
1150 /*
1151 * sqsh-2.1.9: Check for g_interactive instead of sqsh_stdin_isatty()
1152 * because that may not be determined at this point. Do not prompt
1153 * for a password if Network authentication is used but just failed.
1154 */
1155 if (*password_retry != '1' || g_interactive == False ||
1156 sg_login_failed != True || NetAuthRequired == True)
1157 {
1158 goto connect_fail;
1159 }
1160
1161 /*
1162 * We failed due to a bad password, so lets prompt again.
1163 */
1164 len = sqsh_getinput( "Password: ", passbuf, sizeof(passbuf), 0 );
1165
1166 if (len < 0)
1167 {
1168 if (len == -1)
1169 {
1170 fprintf( stderr, "\\connect: %s\n", sqsh_get_errstr() );
1171 }
1172 goto connect_fail;
1173 }
1174
1175 if (passbuf[len] == '\n')
1176 passbuf[len] = '\0';
1177
1178 env_put( g_env, "password", passbuf, ENV_F_TRAN );
1179 password = g_password;
1180
1181 ct_con_props( g_connection, CS_SET, CS_PASSWORD, (CS_VOID*)g_password,
1182 (g_password == NULL) ? CS_UNUSED : CS_NULLTERM, (CS_INT*)NULL );
1183 }
1184 }
1185 while (sg_login_failed == True);
1186 sg_login = False;
1187
1188 /* XXX */
1189 /* ct_cancel(g_connection, NULL, CS_CANCEL_ALL); */
1190
1191 #if defined(CTLIB_SIGPOLL_BUG) && defined(F_SETOWN)
1192 /*
1193 * On certain platforms (older versions of CT-Lib on AIX and even
1194 * recent versions on SGI) there exists a bug having to do with
1195 * fork() in conjunction with the way that CT-Lib sets things up
1196 * for async I/O. The following hack works around this bug (hopefully)
1197 * but should only be used as a last resort as it relies on knowing
1198 * that CT-Lib uses a file descriptor as its communication mechanism.
1199 */
1200 if (ct_con_props( g_connection, /* Connection */
1201 CS_GET, /* Action */
1202 CS_ENDPOINT, /* Property */
1203 (CS_VOID*)&ctlib_fd, /* Buffer */
1204 CS_UNUSED, /* Buffer Length */
1205 (CS_INT*)NULL /* Output Length */
1206 ) != CS_SUCCEED)
1207 {
1208 fprintf( stderr, "\\connect: WARNING: Unable to fetch CT-Lib file\n" );
1209 fprintf( stderr, "\\connect: descriptor to work around SIGPOLL bug.\n" );
1210 }
1211 else
1212 {
1213 /*
1214 * Here is the cruxt of the situation. Async I/O works by delivering
1215 * the signal SIGPOLL or SIGIO every time a socket is ready to do
1216 * some work (either send or receive data). Normally, this signal
1217 * will only be delivered to the process that requested the
1218 * facility. Unfortunately, on some platforms, CT-Lib is explicitly
1219 * requesting that the signal be sent to every process in the same
1220 * process group! This means that when sqsh spawns a child process
1221 * during a pipe (e.g. "go | more") the child process will receive
1222 * the SIGPOLL signal as well as sqsh...and, since 99% of the
1223 * programs out there don't know how to deal with SIGPOLL, they
1224 * will simply exit. Not a good situation.
1225 */
1226 if (fcntl( ctlib_fd, F_SETOWN, getpid() ) == -1)
1227 {
1228 fprintf( stderr,
1229 "\\connect: WARNING: Cannot work around CT-Lib SIGPOLL bug: %s\n",
1230 strerror(errno) );
1231 }
1232 }
1233 #endif /* CTLIB_SIGPOLL_BUG */
1234
1235 /*
1236 * sqsh-2.1.7 - Determine the real tds_version being used if the user
1237 * requested to set a specific tds_version.
1238 */
1239 if (tds_version != NULL && *tds_version != '\0')
1240 {
1241 if (ct_con_props( g_connection, /* Connection */
1242 CS_GET, /* Action */
1243 CS_TDS_VERSION, /* Property */
1244 (CS_VOID*)&version, /* Buffer */
1245 CS_UNUSED, /* Buffer Length */
1246 (CS_INT*)NULL
1247 ) == CS_SUCCEED)
1248 {
1249 switch (version) {
1250 case CS_TDS_40:
1251 env_set( g_env, "tds_version", "4.0" );
1252 break;
1253 case CS_TDS_42:
1254 env_set( g_env, "tds_version", "4.2" );
1255 break;
1256 case CS_TDS_46:
1257 env_set( g_env, "tds_version", "4.6" );
1258 break;
1259 case CS_TDS_495:
1260 env_set( g_env, "tds_version", "4.9.5" );
1261 break;
1262 case CS_TDS_50:
1263 env_set( g_env, "tds_version", "5.0" );
1264 break;
1265 #if !defined(CS_TDS_50)
1266 case CS_TDS_70:
1267 env_set( g_env, "tds_version", "7.0" );
1268 break;
1269 case CS_TDS_71:
1270 env_set( g_env, "tds_version", "8.0" );
1271 break;
1272 case CS_TDS_72:
1273 env_set( g_env, "tds_version", "7.2" );
1274 break;
1275 case CS_TDS_73:
1276 env_set( g_env, "tds_version", "7.2" );
1277 break;
1278 case CS_TDS_74:
1279 env_set( g_env, "tds_version", "7.2" );
1280 break;
1281 #endif
1282 default:
1283 env_set( g_env, "tds_version", "unknown" );
1284 }
1285 }
1286 }
1287
1288 /*
1289 * If we use network authentication, then determine the credential timeout value
1290 * and issue a notification message.
1291 */
1292 if (NetAuthRequired == CS_TRUE)
1293 {
1294 ShowNetAuthCredExp( g_connection, argv[0] );
1295 }
1296
1297 /*-- If autouse has been set, use it --*/
1298 if (autouse != NULL && *autouse != '\0')
1299 {
1300 if (ct_cmd_alloc( g_connection, &cmd ) != CS_SUCCEED)
1301 goto connect_succeed;
1302
1303 sprintf( sqlbuf, "use %s", autouse );
1304
1305 if (ct_command( cmd, /* Command Structure */
1306 CS_LANG_CMD, /* Command Type */
1307 sqlbuf, /* Buffer */
1308 CS_NULLTERM, /* Buffer Length */
1309 CS_UNUSED /* Options */
1310 ) != CS_SUCCEED)
1311 {
1312 ct_cmd_drop( cmd );
1313 goto connect_fail;
1314 }
1315
1316 if (ct_send( cmd ) != CS_SUCCEED)
1317 {
1318 ct_cmd_drop( cmd );
1319 goto connect_fail;
1320 }
1321
1322 while (ct_results( cmd, &result_type ) != CS_END_RESULTS);
1323 ct_cmd_drop( cmd );
1324
1325 /*
1326 * sqsh-2.4 - Check in batch mode if the -D <database> is correctly set.
1327 * Otherwise, abort to prevent script execution in wrong default
1328 * database.
1329 */
1330 env_get ( g_env, "database", &database ) ; /* Need to refresh var pointer */
1331 env_get ( g_env, "usedbcheck", &usedbcheck ) ;
1332 if (g_interactive != True && *usedbcheck == '1' && strcmp (autouse, database) != 0)
1333 {
1334 fprintf (stderr, "sqsh: ERROR: Unable to use database '%s' in batch mode\n", autouse);
1335 sqsh_exit(254);
1336 }
1337 }
1338
1339 connect_succeed:
1340 /*
1341 * Now, since the connection was succesfull, we want to change
1342 * any environment variables to accurately reflect the current
1343 * connection.
1344 */
1345 env_commit( g_env );
1346 timeouts = 0; /* sqsh-2.1.6 */
1347
1348 if (locale != NULL)
1349 cs_loc_drop( g_context, locale );
1350
1351 /* Set chained mode, if necessary. */
1352 if ( chained != NULL && *chained != '\0') /* sqsh-2.1.6 sanity check */
1353 {
1354 if ( check_opt_capability( g_connection ) )
1355 {
1356 CS_BOOL value = (*chained == '1' ? CS_TRUE : CS_FALSE);
1357 retcode = ct_options( g_connection, CS_SET, CS_OPT_CHAINXACTS,
1358 &value, CS_UNUSED, NULL);
1359 if (retcode != CS_SUCCEED)
1360 {
1361 fprintf (stderr,
1362 "\\connect: WARNING: Unable to set transaction mode %s\n",
1363 (*chained == '1' ? "on" : "off"));
1364 }
1365 }
1366 }
1367
1368 return_code = CMD_LEAVEBUF;
1369 goto connect_leave;
1370
1371 connect_fail:
1372 DBG(sqsh_debug(DEBUG_ERROR, "connect: Failure encountered, cleaning up.\n");)
1373
1374 /*
1375 * Restore the environment back to its previous state.
1376 */
1377 env_rollback( g_env );
1378
1379 /*
1380 * And reset the password.
1381 */
1382 if (password_changed)
1383 {
1384 env_set( g_env, "password", orig_password );
1385 }
1386
1387 /*-- Clean up the connection if established --*/
1388 if (g_connection != NULL && sg_interrupted == False)
1389 {
1390 /*-- Find out if the we are connected or not --*/
1391 if (ct_con_props( g_connection, /* Connection */
1392 CS_GET, /* Action */
1393 CS_CON_STATUS, /* Property */
1394 (CS_VOID*)&con_status, /* Buffer */
1395 CS_UNUSED, /* Buffer Length */
1396 (CS_INT*)NULL
1397 ) != CS_SUCCEED)
1398 {
1399 DBG(sqsh_debug(DEBUG_ERROR, "connect: Unable to get con status.\n");)
1400 con_status = CS_CONSTAT_CONNECTED;
1401 }
1402
1403 /*-- If connected, disconnect --*/
1404 if (con_status == CS_CONSTAT_CONNECTED)
1405 {
1406 DBG(sqsh_debug(DEBUG_ERROR, "connect: Closing connection\n");)
1407 if (ct_close( g_connection, CS_UNUSED ) != CS_SUCCEED)
1408 ct_close( g_connection, CS_FORCE_CLOSE );
1409 }
1410 else
1411 {
1412 DBG(sqsh_debug(DEBUG_ERROR, "connect: Connection alread closed\n");)
1413 }
1414
1415 /*-- Free the memory --*/
1416 DBG(sqsh_debug(DEBUG_ERROR, "connect: Dropping connection\n");)
1417 ct_con_drop( g_connection );
1418 }
1419
1420 if (sg_interrupted == False)
1421 {
1422 if (locale != NULL)
1423 {
1424 DBG(sqsh_debug(DEBUG_ERROR, "connect: Dropping locale\n");)
1425 cs_loc_drop( g_context, locale );
1426 }
1427
1428 if (g_context != NULL)
1429 {
1430 DBG(sqsh_debug(DEBUG_ERROR, "connect: Dropping context\n");)
1431 if (ct_exit( g_context, CS_UNUSED ) != CS_SUCCEED)
1432 ct_exit( g_context, CS_FORCE_EXIT );
1433 cs_ctx_drop( g_context );
1434 }
1435 }
1436 g_connection = NULL;
1437 g_context = NULL;
1438 return_code = CMD_FAIL;
1439
1440 connect_leave:
1441
1442 if ( exp_buf != NULL)
1443 varbuf_destroy( exp_buf );
1444
1445 sig_restore();
1446 return return_code;
1447 }
1448
1449 /*
1450 * Function: cmd_snace()
1451 *
1452 * Show network authenticated credential expiration.
1453 */
cmd_snace(argc,argv)1454 int cmd_snace( argc, argv )
1455 int argc ;
1456 char *argv[] ;
1457 {
1458 /*
1459 * No arguments allowed.
1460 */
1461 if( argc != 1 ) {
1462 fprintf( stderr, "\\snace: Too many arguments; Use: \\snace\n" ) ;
1463 return CMD_FAIL ;
1464 }
1465
1466 ShowNetAuthCredExp ( g_connection, argv[0] );
1467
1468 return CMD_LEAVEBUF ;
1469 }
1470
check_opt_capability(g_connection)1471 static int check_opt_capability( g_connection )
1472 CS_CONNECTION *g_connection;
1473 {
1474 CS_BOOL val;
1475 CS_RETCODE ret;
1476
1477 ret = ct_capability( g_connection,
1478 CS_GET,
1479 CS_CAP_REQUEST,
1480 CS_OPTION_GET, (CS_VOID*)&val
1481 );
1482 if (ret != CS_SUCCEED || val == CS_FALSE)
1483 return 0;
1484
1485 return 1;
1486 }
1487
wrap_print(outfile,str)1488 static int wrap_print( outfile, str )
1489 FILE *outfile ;
1490 char *str ;
1491 {
1492 char *cptr ;
1493 char *start ;
1494 char *cur_end ;
1495 char *end ;
1496 int width ;
1497 int len ;
1498
1499 env_get( g_env, "width", &cptr ) ;
1500 if( cptr == NULL || (width = atoi(cptr)) <= 10 )
1501 width = 80 ;
1502
1503 len = strlen( str ) ;
1504 start = str ;
1505 cur_end = str + min(len, width) ;
1506 end = str + len ;
1507
1508 while( len > 0 && cur_end <= end )
1509 {
1510
1511 /*-- Move backwards until we hit whitespace --*/
1512 if( cur_end < end )
1513 {
1514 while( cur_end != start && !(isspace((int)*cur_end)) )
1515 --cur_end ;
1516
1517 /*-- If there were none, then give up --*/
1518 if( cur_end == start )
1519 cur_end = start + (min(len, width)) ;
1520 }
1521
1522 /*-- Print out the line --*/
1523 fprintf( outfile, "%*.*s", (int)(cur_end - start),
1524 (int)(cur_end - start),
1525 start ) ;
1526
1527 if (*(cur_end-1) != '\n')
1528 {
1529 fputc( '\n', outfile );
1530 }
1531
1532 while( cur_end != end && isspace((int)*cur_end) )
1533 ++cur_end ;
1534
1535 /*-- Less to print now --*/
1536 len -= (int)(cur_end - start) ;
1537 start = cur_end ;
1538 cur_end = cur_end + (min(len, width)) ;
1539
1540 }
1541
1542 return True ;
1543 }
1544
1545 /*
1546 * syb_server_cb():
1547 *
1548 * Sybase callback message handler for all messages coming from the
1549 * SQL Server or Open Server.
1550 *
1551 */
syb_server_cb(ctx,con,msg)1552 static CS_RETCODE syb_server_cb (ctx, con, msg)
1553 CS_CONTEXT *ctx;
1554 CS_CONNECTION *con;
1555 CS_SERVERMSG *msg;
1556 {
1557 char *thresh_display;
1558 char *thresh_fail;
1559 char var_value[31];
1560 int i;
1561 char *c;
1562 /* sqsh-2.5 - New variables to support feature p2f */
1563 char *p2faxm;
1564 int p2faxm_int;
1565 char *p2fname;
1566 FILE *dest_fp;
1567 int p2fstat = False;
1568
1569 /*
1570 * Record last error in $?
1571 */
1572 if (msg->severity > 10)
1573 {
1574 sprintf( var_value, "%d", (int) msg->msgnumber );
1575 env_set( g_internal_env, "?", var_value );
1576 }
1577
1578 /*
1579 * Check for "database changed", or "language changed" messages from
1580 * the client. If we get one of these, then we need to pull the
1581 * name of the database or charset from the message and set the
1582 * appropriate variable.
1583 */
1584 if( msg->msgnumber == 5701 || /* database context change */
1585 msg->msgnumber == 5703 || /* language changed */
1586 msg->msgnumber == 5704 ) /* charset changed */
1587 {
1588
1589 if (msg->text != NULL && (c = strchr( msg->text, '\'' )) != NULL)
1590 {
1591 i = 0;
1592 for( ++c; i <= 30 && *c != '\0' && *c != '\''; ++c )
1593 var_value[i++] = *c;
1594 var_value[i] = '\0';
1595
1596 /*
1597 * On some systems, if the charset is mis-configured in the
1598 * SQL Server, it will come back as the string "<NULL>". If
1599 * this is the case, then we want to ignore this value.
1600 */
1601 if (strcmp( var_value, "<NULL>" ) != 0)
1602 {
1603 switch (msg->msgnumber)
1604 {
1605 case 5701 :
1606 env_set( g_env, "database", var_value );
1607 #if defined(USE_READLINE)
1608 /* sqsh-2.1.8 - Feature dynamic keyword load */
1609 env_set( g_internal_env, "keyword_refresh", "1" );
1610 #endif
1611 break;
1612 case 5703 :
1613 env_set( g_env, "language", var_value );
1614 break;
1615 case 5704 :
1616 env_set( g_env, "charset", var_value );
1617 break;
1618 default :
1619 break;
1620 }
1621 }
1622 }
1623 return CS_SUCCEED;
1624 }
1625
1626 /*
1627 * If we got a login failed message, then record it as such and return.
1628 * sqsh-2.1.8 : Fix password_retry for Sybase RepServer and Microsoft MSSQL connections.
1629 * We also need to check for sg_login == True, i.e. we are in the middle of a login attempt.
1630 */
1631 if (sg_login == True && (msg->msgnumber == 1017 || /* DCO/ECDA Oracle */
1632 msg->msgnumber == 4002 || /* Sybase ASE/ASA/IQ */
1633 msg->msgnumber == 14021 || /* Sybase RepServer */
1634 msg->msgnumber == 18456)) /* Microsoft MSSQL */
1635 {
1636 wrap_print( stderr, msg->text );
1637 sg_login_failed = True;
1638 return CS_SUCCEED;
1639 }
1640
1641 /*
1642 * Retrieve the threshold severity level for ignoring errors.
1643 * If the variable is set and severity is less than the
1644 * threshold, then return without printing out the message.
1645 */
1646 env_get( g_env, "thresh_display", &thresh_display );
1647
1648 if (thresh_display == NULL || *thresh_display == '\0' ||
1649 msg->severity >= (CS_INT)atoi(thresh_display))
1650 {
1651 /*
1652 * If the severity is something other than 0 or the msg number is
1653 * 0 (user informational messages).
1654 */
1655 if (msg->severity >= 0 || msg->msgnumber == 10)
1656 {
1657 /*
1658 * sqsh-2.5 : Implementation of p2f feature.
1659 * When the number of messages handled by this callback handler exceeds the limit specified in p2faxm,
1660 * and a file is provided in $p2fname and is succesfully opened and we are in interactive mode, then
1661 * write the remaining messages from the current batch to this file instead of on screen.
1662 * Note that global variable g_p2fc will be reset to zero for each new batch in dsp.c.
1663 */
1664 env_get( g_env, "p2faxm", &p2faxm );
1665 p2faxm_int = (p2faxm != NULL) ? atoi(p2faxm) : 0;
1666 if (++g_p2fc > p2faxm_int &&
1667 p2faxm_int > 0 &&
1668 g_p2f_fp != NULL &&
1669 g_interactive == True)
1670 {
1671 if (g_p2fc == p2faxm_int + 1) {
1672 env_get( g_env, "p2fname", &p2fname );
1673 fprintf (stderr,"Warning: Number of printed server messages exceeds p2faxm=%d limit for current batch.\n", p2faxm_int);
1674 fprintf (stderr," Remaining server messages will be printed to file: %s\n", p2fname);
1675 fflush (stderr );
1676 fprintf (g_p2f_fp, "--------\n");
1677 }
1678 p2fstat = True;
1679 }
1680
1681 /*
1682 * If the message was something other than informational, and
1683 * the severity was greater than 0, then print information to
1684 * stderr with a little pre-amble information. According to
1685 * the Sybase System Administrator's guide, severity level 10
1686 * messages should not display severity information.
1687 */
1688 if (msg->msgnumber > 0 && msg->severity > 10)
1689 {
1690 dest_fp = (p2fstat == True) ? g_p2f_fp : stderr;
1691 fprintf( dest_fp, "Msg %d, Level %d, State %d\n",
1692 (int)msg->msgnumber, (int)msg->severity, (int)msg->state );
1693 if (msg->svrnlen > 0)
1694 fprintf( dest_fp, "Server '%s'", (char*)msg->svrname );
1695 if (msg->proclen > 0)
1696 fprintf( dest_fp, ", Procedure '%s'", (char*)msg->proc );
1697 if( msg->line > 0 )
1698 fprintf( dest_fp, ", Line %d", (int)msg->line );
1699 fprintf( dest_fp, "\n" );
1700 wrap_print( dest_fp, msg->text );
1701 fflush( dest_fp );
1702 }
1703 else
1704 {
1705 /*
1706 * Otherwise, it is just an informational (e.g. print) message
1707 * from the server, so send it to stdout.
1708 */
1709 dest_fp = (p2fstat == True) ? g_p2f_fp : stdout;
1710 wrap_print( dest_fp, msg->text );
1711 fflush( dest_fp );
1712 }
1713 }
1714 }
1715
1716 /*
1717 * If the user specifies a threshold to be considered a failure
1718 * then increment our failcount.
1719 */
1720 env_get( g_env, "thresh_fail", &thresh_fail );
1721
1722 if (thresh_fail != NULL && msg->severity >= (CS_INT)atoi(thresh_fail))
1723 {
1724 DBG(sqsh_debug(DEBUG_ERROR,
1725 "syb_server_cb: thresh_fail = %s, severity = %d, incrementing batch_failcount\n",
1726 thresh_fail, (int)msg->severity);)
1727
1728 /*-- Increment number of failed batches --*/
1729 env_set( g_env, "batch_failcount", "1" );
1730 }
1731
1732 return CS_SUCCEED;
1733 }
1734
syb_cs_cb(ctx,msg)1735 static CS_RETCODE syb_cs_cb ( ctx, msg )
1736 CS_CONTEXT *ctx;
1737 CS_CLIENTMSG *msg;
1738 {
1739 /*
1740 * For any other type of severity (that is not a server
1741 * message), we increment the batch_failcount.
1742 */
1743 env_set( g_env, "batch_failcount", "1" ) ;
1744
1745 /*
1746 * If this is the "The attempt to connect to the server failed"
1747 * message and we previously got a "Login Failed" message from
1748 * the server (44), then ignore it.
1749 *
1750 * sqsh-2.1.9: The same for the freetds "Adaptive Server connection failed"
1751 * message number 34. Also check for g_interactive and display the Client
1752 * Message if the session is not interactive.
1753 */
1754 if ((CS_NUMBER(msg->msgnumber) == 34 || CS_NUMBER(msg->msgnumber) == 44)
1755 && sg_login_failed == True && g_interactive == True)
1756 {
1757 return CS_SUCCEED;
1758 }
1759 fprintf( stderr, "Open Client Message\n" );
1760
1761 if (CS_NUMBER(msg->msgnumber) > 0)
1762 {
1763 fprintf( stderr, "Layer %d, Origin %d, Severity %d, Number %d\n",
1764 (int)CS_LAYER(msg->msgnumber),
1765 (int)CS_ORIGIN(msg->msgnumber),
1766 (int)CS_SEVERITY(msg->msgnumber),
1767 (int)CS_NUMBER(msg->msgnumber) );
1768 }
1769 if (msg->osstringlen > 0)
1770 {
1771 fprintf( stderr, "OS Error: %s\n", (char*)msg->osstring );
1772 }
1773 wrap_print( stderr, msg->msgstring );
1774 fflush( stderr ) ;
1775
1776 return CS_SUCCEED ;
1777 }
1778
1779 /*
1780 * Function syb_client_cb() error handler
1781 *
1782 * sqsh-2.1.6 feature - Client call back modified to facilitate handling
1783 * of query and login timeouts.
1784 */
1785 #define ERROR_SNOL(e,s,n,o,l) \
1786 ( (CS_SEVERITY(e) == s) && (CS_NUMBER(e) == n) \
1787 && (CS_ORIGIN(e) == o) && (CS_LAYER(e) == l) )
1788
syb_client_cb(ctx,con,msg)1789 static CS_RETCODE syb_client_cb ( ctx, con, msg )
1790 CS_CONTEXT *ctx;
1791 CS_CONNECTION *con;
1792 CS_CLIENTMSG *msg;
1793 {
1794 CS_INT status = 0;
1795 char *server;
1796 char *max_timeout;
1797
1798
1799 /*
1800 * sqsh-2.1.7 - Ignore a "security context has already expired" message
1801 * when this is caused by a ct_close.
1802 */
1803 if ( ERROR_SNOL(msg->msgnumber, 5, 2, 9, 7 ) )
1804 return CS_SUCCEED;
1805
1806 /*
1807 * Let the CS-Lib handler print the message out.
1808 */
1809 (void) syb_cs_cb( ctx, msg );
1810
1811 /*
1812 * If the dbprocess is dead or the dbproc is a NULL pointer and
1813 * we are not in the middle of logging in, then we need to exit.
1814 * We can't do anything from here on out anyway.
1815 */
1816 if (sg_login == False)
1817 {
1818 env_get( g_env, "DSQUERY", &server ) ;
1819 #if defined(CS_TDS_50)
1820 if (CS_SEVERITY(msg->msgnumber) >= CS_SV_COMM_FAIL ||
1821 ctx == NULL || con == NULL)
1822 #else
1823 /* Then we use freetds which uses enum instead of defines */
1824 if ((CS_SEVERITY(msg->msgnumber) >= CS_SV_COMM_FAIL &&
1825 CS_SEVERITY(msg->msgnumber) <= CS_SV_FATAL) ||
1826 ctx == NULL || con == NULL)
1827 #endif
1828 {
1829 fprintf (stderr, "%s: Aborting on severity %d\n", server, (int) CS_SEVERITY(msg->msgnumber) );
1830 sqsh_exit(254);
1831 }
1832 }
1833
1834 /*
1835 ** Is it a timeout error?
1836 */
1837 if ( ERROR_SNOL(msg->msgnumber, CS_SV_RETRY_FAIL, 63, 2, 1 ) )
1838 {
1839 timeouts++;
1840 env_get( g_env, "DSQUERY", &server ) ;
1841 env_get( g_env, "max_timeout", &max_timeout ) ;
1842 if (max_timeout != NULL && *max_timeout != '\0' && atoi(max_timeout) > 0)
1843 {
1844 if (timeouts >= atoi(max_timeout))
1845 {
1846 fprintf (stderr, "%s: Query or command timeout detected, session aborted\n", server);
1847 fprintf (stderr, "%s: The client connection has detected this %d time(s).\n", server, timeouts);
1848 fprintf (stderr, "%s: Aborting on max_timeout limit %s\n", server, max_timeout );
1849 sqsh_exit(254);
1850 }
1851 }
1852 if (ct_con_props (con, CS_GET, CS_LOGIN_STATUS, (CS_VOID *)&status, CS_UNUSED, NULL) != CS_SUCCEED)
1853 {
1854 fprintf (stderr,"%s: ct_con_props() failed\n", server);
1855 return CS_FAIL;
1856 }
1857
1858 if (status)
1859 {
1860 /* Result set timeout */
1861 (CS_VOID) ct_cancel(con, (CS_COMMAND *) NULL, CS_CANCEL_ATTN);
1862 fprintf (stderr, "%s: Query or command timeout detected, command/batch cancelled\n", server);
1863 fprintf (stderr, "%s: The client connection has detected this %d time(s).\n", server, timeouts);
1864 return CS_SUCCEED;
1865 }
1866 else
1867 {
1868 /* Login timeout */
1869 fprintf (stderr, "%s: Login timeout detected, aborting connection attempt\n", server);
1870 return CS_FAIL;
1871 }
1872 }
1873 return CS_SUCCEED ;
1874 }
1875
1876
1877 /*
1878 * sqsh-2.1.6 feature
1879 *
1880 * Function: SetNetAuth
1881 *
1882 * Purpose: Enable network user authentication using the appropriate security
1883 * mechanism (Kerberos, DCE or PAM) and set available options.
1884 *
1885 * Return : CS_FAIL or CS_SUCCEED
1886 */
SetNetAuth(conn,principal,keytab_file,secmech,req_options)1887 static CS_RETCODE SetNetAuth (conn, principal, keytab_file, secmech, req_options)
1888 CS_CONNECTION *conn;
1889 CS_CHAR *principal;
1890 CS_CHAR *keytab_file;
1891 CS_CHAR *secmech;
1892 CS_CHAR *req_options;
1893 {
1894
1895 #if 0 && defined(CS_SEC_NETWORKAUTH)
1896
1897 CS_CHAR buf[CS_MAX_CHAR+1];
1898 CS_INT buflen;
1899 CS_INT i;
1900 CS_BOOL OptSupported;
1901
1902 NET_SEC_SERVICE nss[] = {
1903 /*
1904 * CS_SEC_NETWORKAUTH must be the first entry
1905 */
1906 { CS_SEC_NETWORKAUTH, 'u', "Network user authentication (unified login)" },
1907 { CS_SEC_CHANBIND, 'b', "Channel binding" },
1908 { CS_SEC_CONFIDENTIALITY,'c', "Data confidentiality" },
1909 { CS_SEC_DELEGATION, 'd', "Credentials delegation" },
1910 { CS_SEC_INTEGRITY, 'i', "Data integrity" },
1911 { CS_SEC_MUTUALAUTH, 'm', "Mutual client/server authentication" },
1912 { CS_SEC_DATAORIGIN, 'o', "Data origin stamping" },
1913 { CS_SEC_DETECTSEQ, 'q', "Data out-of-sequence detection" },
1914 { CS_SEC_DETECTREPLAY, 'r', "Data replay detection" },
1915 { CS_UNUSED, ' ', "" }
1916 };
1917
1918
1919 DBG(sqsh_debug(DEBUG_ENV, "SetNetAuth - principal: %s\n", (principal == NULL ? "NULL" : principal));)
1920 DBG(sqsh_debug(DEBUG_ENV, "SetNetAuth - keytab_file: %s\n", (keytab_file == NULL ? "NULL" : keytab_file));)
1921 DBG(sqsh_debug(DEBUG_ENV, "SetNetAuth - secmech: %s\n", (secmech == NULL ? "NULL" : secmech));)
1922 DBG(sqsh_debug(DEBUG_ENV, "SetNetAuth - req_options: %s\n", (req_options == NULL ? "NULL" : req_options));)
1923
1924 /*
1925 * Set optional server principal. (Server principal name without the realm name)
1926 */
1927 if (principal != NULL && *principal != '\0')
1928 {
1929 if (ct_con_props( g_connection, /* Connection */
1930 CS_SET, /* Action */
1931 CS_SEC_SERVERPRINCIPAL, /* Property */
1932 (CS_VOID*)principal, /* Buffer */
1933 CS_NULLTERM, /* Buffer Length */
1934 (CS_INT*)NULL /* Output Length */
1935 ) != CS_SUCCEED)
1936 {
1937 DBG(sqsh_debug(DEBUG_ERROR, "SetNetAuth: Failed to set principal %s.\n", principal);)
1938 return CS_FAIL;
1939 }
1940 DBG(sqsh_debug(DEBUG_ERROR, "SetNetAuth: Succesfully set principal %s.\n", principal);)
1941 }
1942
1943 /*
1944 * Set optional keytab file for DCE network authentication.
1945 */
1946 if (keytab_file != NULL && *keytab_file != '\0')
1947 {
1948 if (ct_con_props( g_connection, /* Connection */
1949 CS_SET, /* Action */
1950 CS_SEC_KEYTAB, /* Property */
1951 (CS_VOID*)keytab_file, /* Buffer */
1952 CS_NULLTERM, /* Buffer Length */
1953 (CS_INT*)NULL /* Output Length */
1954 ) != CS_SUCCEED)
1955 {
1956 DBG(sqsh_debug(DEBUG_ERROR, "SetNetAuth: Failed to set keytab %s.\n", keytab_file);)
1957 return CS_FAIL;
1958 }
1959 DBG(sqsh_debug(DEBUG_ERROR, "SetNetAuth: Succesfully set keytab %s.\n", keytab_file);)
1960 }
1961
1962 /*
1963 * Set the security mechanism.
1964 * Note: If you do not supply a mechanism, or use "default", the first available
1965 * configured security mechanism from the libtcl.cfg file will be used.
1966 */
1967 if (secmech != NULL && *secmech != '\0' && strcasecmp(secmech, "default") != 0)
1968 {
1969 if (ct_con_props( g_connection, /* Connection */
1970 CS_SET, /* Action */
1971 CS_SEC_MECHANISM, /* Property */
1972 (CS_VOID*)secmech, /* Buffer */
1973 CS_NULLTERM, /* Buffer Length */
1974 (CS_INT*)NULL /* Output Length */
1975 ) != CS_SUCCEED)
1976 {
1977 DBG(sqsh_debug(DEBUG_ERROR, "SetNetAuth: Failed to set secmech %s.\n", secmech);)
1978 return CS_FAIL;
1979 }
1980 DBG(sqsh_debug(DEBUG_ERROR, "SetNetAuth: Succesfully set secmech %s.\n", secmech);)
1981 }
1982
1983 /*
1984 * Always set the CS_SEC_NETWORKAUTH option. (Option defined in nss[0]).
1985 */
1986 OptSupported = CS_TRUE;
1987 if (ct_con_props(conn, CS_SET, nss[0].service, &OptSupported,
1988 CS_UNUSED, NULL) != CS_SUCCEED)
1989 {
1990 DBG(sqsh_debug(DEBUG_ERROR, "SetNetAuth: Failed to enable option %s.\n", nss[0].name);)
1991 return CS_FAIL;
1992 }
1993 DBG(sqsh_debug(DEBUG_ERROR, "SetNetAuth: Succesfully enabled option %s.\n", nss[0].name);)
1994
1995 /*
1996 * Request for the secmech actually in use.
1997 */
1998 if (ct_con_props( g_connection, /* Connection */
1999 CS_GET, /* Action */
2000 CS_SEC_MECHANISM, /* Property */
2001 (CS_VOID*)buf, /* Buffer */
2002 CS_MAX_CHAR, /* Buffer Length */
2003 (CS_INT*)&buflen /* Output Length */
2004 ) != CS_SUCCEED)
2005 {
2006 DBG(sqsh_debug(DEBUG_ERROR, "SetNetAuth: Failed to get secmech name.\n");)
2007 return CS_FAIL;
2008 }
2009 DBG(sqsh_debug(DEBUG_ERROR, "SetNetAuth: Succesfully obtained secmech name %s.\n", buf);)
2010 env_put( g_env, "secmech", buf, ENV_F_TRAN );
2011
2012 /*
2013 * Loop through the list of all other available security options
2014 * and check if the current option is requested (-V option),
2015 * otherwise continue with the next option.
2016 * Note: If the req_options pointer is NULL or is empty, all
2017 * possible options supported by the secmech are enabled.
2018 * (Should we report on non-existent options requested, or leave
2019 * it just the way it is?)
2020 */
2021 for (i = 1; nss[i].service != CS_UNUSED; i++)
2022 {
2023 if (req_options != NULL && strlen(req_options) > 0 &&
2024 strchr (req_options, nss[i].optchar) == NULL)
2025 continue;
2026
2027 /*
2028 * First check if the secmech supports the requested service.
2029 */
2030 if (ct_con_props(conn, CS_SUPPORTED, nss[i].service, &OptSupported,
2031 CS_UNUSED, NULL) != CS_SUCCEED)
2032 {
2033 DBG(sqsh_debug(DEBUG_ERROR, "SetNetAuth: Failed to request support for option %s.\n",
2034 nss[i].name);)
2035 return CS_FAIL;
2036 }
2037
2038 /*
2039 * Is the service supported by the secmech, then activate it.
2040 */
2041 if (OptSupported == CS_TRUE)
2042 {
2043 if ( ct_con_props(conn, CS_SET, nss[i].service, &OptSupported,
2044 CS_UNUSED, NULL) != CS_SUCCEED)
2045 {
2046 DBG(sqsh_debug(DEBUG_ERROR, "SetNetAuth: Failed to enable option %s.\n",
2047 nss[i].name);)
2048 return CS_FAIL;
2049 }
2050 DBG(sqsh_debug(DEBUG_ERROR, "SetNetAuth: Succesfully enabled option %s.\n",
2051 nss[i].name);)
2052 }
2053 }
2054 return CS_SUCCEED;
2055 #else
2056 fprintf (stderr, "sqsh: ERROR: Network Authentication not supported\n");
2057 return CS_FAIL;
2058 #endif
2059 }
2060
2061
2062 /*
2063 * Function: ShowNetAuthCredExp()
2064 *
2065 * Show the credential expiration timeout period of a network authenticated session.
2066 */
ShowNetAuthCredExp(conn,cmdname)2067 static CS_RETCODE ShowNetAuthCredExp (conn, cmdname)
2068 CS_CONNECTION *conn;
2069 CS_CHAR *cmdname;
2070 {
2071 #if 0 && defined(CS_SEC_NETWORKAUTH)
2072 CS_INT CredTimeOut;
2073 CS_BOOL NETWORKAUTH;
2074 char *datetime;
2075 char dttm[32];
2076 time_t exp_time;
2077 char *fmt;
2078 char *cp;
2079
2080 /*
2081 * Check if current session is Network Authenticated.
2082 */
2083 if (ct_con_props( g_connection, /* Connection */
2084 CS_GET, /* Action */
2085 CS_SEC_NETWORKAUTH, /* Property */
2086 (CS_VOID*)&NETWORKAUTH, /* Buffer */
2087 CS_UNUSED, /* Buffer Length */
2088 (CS_INT*)NULL ) != CS_SUCCEED)
2089 {
2090 fprintf (stderr, "%s: ERROR: Unable to determine status of CS_SEC_NETWORKAUTH\n",
2091 cmdname );
2092 return CS_FAIL;
2093 }
2094
2095 if (NETWORKAUTH == CS_TRUE)
2096 {
2097 /*
2098 * If we use network authentication, then determine the credential timeout period.
2099 */
2100 if (ct_con_props( g_connection, /* Connection */
2101 CS_GET, /* Action */
2102 CS_SEC_CREDTIMEOUT, /* Property */
2103 (CS_VOID*)&CredTimeOut, /* Buffer */
2104 CS_UNUSED, /* Buffer Length */
2105 (CS_INT*)NULL ) != CS_SUCCEED)
2106 {
2107 fprintf (stderr, "%s: ERROR: Unable to determine value of CS_SEC_CREDTIMEOUT\n",
2108 cmdname );
2109 return CS_FAIL;
2110 }
2111
2112 switch (CredTimeOut)
2113 {
2114 case CS_NO_LIMIT:
2115 fprintf (stdout, "%s: Network Authenticated session does not expire\n", cmdname);
2116 break;
2117
2118 case CS_UNEXPIRED:
2119 fprintf (stdout, "%s: Network Authenticated session is not expired\n", cmdname);
2120 break;
2121
2122 case 0:
2123 fprintf (stdout, "%s: Network Authenticated session is already expired\n", cmdname);
2124 break;
2125
2126 default:
2127 exp_time = time (NULL) + CredTimeOut;
2128 env_get( g_env, "datetime", &datetime ) ;
2129 if (datetime == NULL || datetime[0] == '\0' || strcmp(datetime, "default") == 0)
2130 {
2131 cftime( dttm, "%Y%m%d %H:%M:%S", &exp_time );
2132 }
2133 else
2134 {
2135 fmt = (char *) malloc (sizeof(char) * strlen(datetime)+2);
2136 for (cp = fmt; *datetime != '\0'; datetime++)
2137 {
2138 if (*datetime == '.' && *(datetime+1) == '%' && *(datetime+2) == 'q')
2139 {
2140 datetime += 2;
2141 }
2142 else if (*datetime != '[' && *datetime != ']')
2143 *cp++ = *datetime;
2144 }
2145 *cp = '\0';
2146 cftime( dttm, fmt, &exp_time );
2147 free (fmt);
2148 }
2149 fprintf (stdout, "%s: Network Authenticated session expires at: %s (%d secs)\n",
2150 cmdname, dttm, (int) CredTimeOut );
2151 }
2152 }
2153 else
2154 fprintf (stdout, "%s: No Network Authenticated session active\n", cmdname );
2155 return CS_SUCCEED;
2156 #else
2157 fprintf (stdout, "%s: Network Authentication not supported\n", cmdname);
2158 return CS_SUCCEED;
2159 #endif
2160 }
2161
2162
2163 /*
2164 * connect_run_sigint():
2165 *
2166 * This function is called whenever a SIGINT signal is received while processing cmd_connect.
2167 * Its only real job is to return to the point where the SETJMP function was executed.
2168 */
connect_run_sigint(sig,user_data)2169 static void connect_run_sigint (sig, user_data )
2170 int sig;
2171 void *user_data;
2172 {
2173 sg_interrupted = True;
2174 LONGJMP (sg_jmp_buf, 1);
2175 }
2176
2177
2178 #if defined(CS_SSLVALIDATE_CB)
2179 /*
2180 * sqsh-2.5 - Validate the servername in a host:port:ssl type of connection
2181 * to be valid for the chosen certificate if the servername does
2182 * not match the CN in the certificate.
2183 */
validate_srvname_cb(userdata,certptr,certcount,valid)2184 static CS_RETCODE validate_srvname_cb (userdata, certptr, certcount, valid)
2185 CS_VOID *userdata;
2186 CS_SSLCERT *certptr;
2187 CS_INT certcount;
2188 CS_INT valid;
2189 {
2190 if (valid == CS_SSL_INVALID_MISMATCHNAME)
2191 {
2192 return CS_SSL_VALID_CERT;
2193 }
2194 else
2195 {
2196 return valid;
2197 }
2198 }
2199 #endif
2200
2201