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