97192b6cac2e3293f79d071fe5708b32e30a095e
[fwknop.git] / client / fwknop.c
1 /*
2  *****************************************************************************
3  *
4  * File:    fwknop.c
5  *
6  * Author:  Damien S. Stuart
7  *
8  * Purpose: An implementation of an fwknop client.
9  *
10  * Copyright 2009-2010 Damien Stuart (dstuart@dstuart.org)
11  *
12  *  License (GNU Public License):
13  *
14  *  This program is free software; you can redistribute it and/or
15  *  modify it under the terms of the GNU General Public License
16  *  as published by the Free Software Foundation; either version 2
17  *  of the License, or (at your option) any later version.
18  *
19  *  This program is distributed in the hope that it will be useful,
20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *  GNU General Public License for more details.
23  *
24  *  You should have received a copy of the GNU General Public License
25  *  along with this program; if not, write to the Free Software
26  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
27  *  USA
28  *
29  *****************************************************************************
30 */
31 #include "fwknop.h"
32 #include "config_init.h"
33 #include "spa_comm.h"
34 #include "utils.h"
35 #include "getpasswd.h"
36
37 /* prototypes
38 */
39 static char * get_user_pw(fko_ctx_t ctx,
40         fko_cli_options_t *options, const int crypt_op);
41 static void display_ctx(fko_ctx_t ctx);
42 static void errmsg(const char *msg, const int err);
43 static void show_last_command(void);
44 static void save_args(int argc, char **argv);
45 static void run_last_args(fko_cli_options_t *options);
46 static int set_message_type(fko_ctx_t ctx, fko_cli_options_t *options);
47 static int set_nat_access(fko_ctx_t ctx, fko_cli_options_t *options);
48 static int get_rand_port(fko_ctx_t ctx);
49 int resolve_ip_http(fko_cli_options_t *options);
50
51 int
52 main(int argc, char **argv)
53 {
54     fko_ctx_t           ctx, ctx2;
55     int                 res;
56     char               *spa_data, *version;
57     char                access_buf[MAX_LINE_LEN];
58
59     fko_cli_options_t   options;
60
61     /* Handle command line
62     */
63     config_init(&options, argc, argv);
64
65     /* Handle options that don't require a libfko context
66     */
67     if(options.run_last_command)
68         run_last_args(&options);
69     else if(options.show_last_command)
70         show_last_command();
71     else if (!options.no_save_args)
72         save_args(argc, argv);
73
74     /* Intialize the context
75     */
76     res = fko_new(&ctx);
77     if(res != FKO_SUCCESS)
78     {
79         errmsg("fko_new", res);
80         return(EXIT_FAILURE);
81     }
82
83     /* Display version info and exit.
84     */
85     if (options.version) {
86         fko_get_version(ctx, &version);
87
88         fprintf(stdout, "fwknop client %s, FKO protocol version %s\n",
89             MY_VERSION, version);
90
91         fko_destroy(ctx);
92         return(EXIT_SUCCESS);
93     }
94
95     /* Set client timeout
96     */
97     if(options.fw_timeout >= 0)
98     {
99         res = fko_set_spa_client_timeout(ctx, options.fw_timeout);
100         if(res != FKO_SUCCESS)
101         {
102             errmsg("fko_set_spa_client_timeout", res);
103             fko_destroy(ctx);
104             return(EXIT_FAILURE);
105         }
106     }
107
108     /* Set the SPA packet message type based on command line options
109     */
110     res = set_message_type(ctx, &options);
111     if(res != FKO_SUCCESS)
112     {
113         errmsg("fko_set_spa_message_type", res);
114         fko_destroy(ctx);
115         return(EXIT_FAILURE);
116     }
117
118     /* Adjust the SPA timestamp if necessary
119     */
120     if(options.time_offset_plus > 0)
121     {
122         res = fko_set_timestamp(ctx, options.time_offset_plus);
123         if(res != FKO_SUCCESS)
124         {
125             errmsg("fko_set_timestamp", res);
126             fko_destroy(ctx);
127             return(EXIT_FAILURE);
128         }
129     }
130     if(options.time_offset_minus > 0)
131     {
132         res = fko_set_timestamp(ctx, -options.time_offset_minus);
133         if(res != FKO_SUCCESS)
134         {
135             errmsg("fko_set_timestamp", res);
136             fko_destroy(ctx);
137             return(EXIT_FAILURE);
138         }
139     }
140
141     if(options.server_command[0] != 0x0)
142     {
143         /* Set the access message to a command that the server will
144          * execute
145         */
146         snprintf(access_buf, MAX_LINE_LEN, "%s%s%s",
147                 options.allow_ip_str, ",", options.server_command);
148     }
149     else
150     {
151         /* Resolve the client's public facing IP address if requestesd.
152          * if this fails, consider it fatal.
153         */
154         if (options.resolve_ip_http)
155         {
156             if(resolve_ip_http(&options) < 0)
157             {
158                 fko_destroy(ctx);
159                 return(EXIT_FAILURE);
160             }
161         }
162
163         /* Set a message string by combining the allow IP and the
164          * port/protocol.  The fwknopd server allows no port/protocol
165          * to be specified as well, so in this case append the string
166          * "none/0" to the allow IP.
167         */
168         if(options.access_str[0] != 0x0)
169         {
170             snprintf(access_buf, MAX_LINE_LEN, "%s%s%s",
171                     options.allow_ip_str, ",", options.access_str);
172         }
173         else
174         {
175             snprintf(access_buf, MAX_LINE_LEN, "%s%s%s",
176                     options.allow_ip_str, ",", "none/0");
177         }
178     }
179     res = fko_set_spa_message(ctx, access_buf);
180     if(res != FKO_SUCCESS)
181     {
182         errmsg("fko_set_spa_message", res);
183         fko_destroy(ctx);
184         return(EXIT_FAILURE);
185     }
186
187     /* Set NAT access string
188     */
189     if (options.nat_local || options.nat_access_str[0] != 0x0)
190     {
191         res = set_nat_access(ctx, &options);
192         if(res != FKO_SUCCESS)
193         {
194             errmsg("fko_set_nat_access_str", res);
195             fko_destroy(ctx);
196             return(EXIT_FAILURE);
197         }
198     }
199
200     /* Set username
201     */
202     if(options.spoof_user[0] != 0x0)
203     {
204         res = fko_set_username(ctx, options.spoof_user);
205         if(res != FKO_SUCCESS)
206         {
207             errmsg("fko_set_username", res);
208             fko_destroy(ctx);
209             return(EXIT_FAILURE);
210         }
211     }
212
213     /* Set up for using GPG if specified.
214     */
215     if(options.use_gpg)
216     {
217         /* If use-gpg-agent was not specified, then remove the GPG_AGENT_INFO
218          * ENV variable if it exists.
219         */
220 #ifndef WIN32
221         if(!options.use_gpg_agent)
222             unsetenv("GPG_AGENT_INFO");
223 #endif
224
225         res = fko_set_spa_encryption_type(ctx, FKO_ENCRYPTION_GPG);
226         if(res != FKO_SUCCESS)
227         {
228             errmsg("fko_set_spa_encryption_type", res);
229             fko_destroy(ctx);
230             return(EXIT_FAILURE);
231         }
232
233         /* If a GPG home dir was specified, set it here.  Note: Setting
234          * this has to occur before calling any of the other GPG-related
235          * functions.
236         */
237         if(options.gpg_home_dir != NULL && strlen(options.gpg_home_dir) > 0)
238         {
239             res = fko_set_gpg_home_dir(ctx, options.gpg_home_dir);
240             if(res != FKO_SUCCESS)
241             {
242                 errmsg("fko_set_gpg_home_dir", res);
243                 fko_destroy(ctx);
244                 return(EXIT_FAILURE);
245             }
246         }
247
248         res = fko_set_gpg_recipient(ctx, options.gpg_recipient_key);
249         if(res != FKO_SUCCESS)
250         {
251             errmsg("fko_set_gpg_recipient", res);
252
253             if(IS_GPG_ERROR(res))
254                 fprintf(stderr, "GPG ERR: %s\n", fko_gpg_errstr(ctx));
255             fko_destroy(ctx);
256             return(EXIT_FAILURE);
257         }
258
259         if(options.gpg_signer_key != NULL && strlen(options.gpg_signer_key))
260         {
261             res = fko_set_gpg_signer(ctx, options.gpg_signer_key);
262             if(res != FKO_SUCCESS)
263             {
264                 errmsg("fko_set_gpg_signer", res);
265
266                 if(IS_GPG_ERROR(res))
267                     fprintf(stderr, "GPG ERR: %s\n", fko_gpg_errstr(ctx));
268
269                 fko_destroy(ctx);
270                 return(EXIT_FAILURE);
271             }
272         }
273     }
274
275     /* Set Digest type.
276     */
277     if(options.digest_type)
278     {
279         fko_set_spa_digest_type(ctx, options.digest_type);
280         if(res != FKO_SUCCESS)
281         {
282             errmsg("fko_set_spa_digest_type", res);
283             fko_destroy(ctx);
284             return(EXIT_FAILURE);
285         }
286     }
287
288     /* Finalize the context data (encrypt and encode the SPA data)
289     */
290     res = fko_spa_data_final(ctx, get_user_pw(ctx, &options, CRYPT_OP_ENCRYPT));
291     if(res != FKO_SUCCESS)
292     {
293         errmsg("fko_spa_data_final", res);
294
295         if(IS_GPG_ERROR(res))
296             fprintf(stderr, "GPG ERR: %s\n", fko_gpg_errstr(ctx));
297
298         fko_destroy(ctx);
299         return(EXIT_FAILURE);
300     }
301
302     /* Display the context data.
303     */
304     if (options.verbose || options.test)
305         display_ctx(ctx);
306
307     /* Save packet data payload if requested.
308     */
309     if (options.save_packet_file[0] != 0x0)
310         write_spa_packet_data(ctx, &options);
311
312     if (options.rand_port)
313         options.spa_dst_port = get_rand_port(ctx);
314
315     res = send_spa_packet(ctx, &options);
316     if(res < 0)
317     {
318         fprintf(stderr, "send_spa_packet: packet not sent.\n");
319         fko_destroy(ctx);
320         return(EXIT_FAILURE);
321     }
322     else
323     {
324         if(options.verbose)
325             fprintf(stderr, "send_spa_packet: bytes sent: %i\n", res);
326     }
327
328     /* Run through a decode cycle in test mode (--DSS XXX: This test/decode
329      * portion should be moved elsewhere).
330     */
331     if (options.test)
332     {
333         /************** Decoding now *****************/
334
335         /* Now we create a new context based on data from the first one.
336         */
337         res = fko_get_spa_data(ctx, &spa_data);
338         if(res != FKO_SUCCESS)
339         {
340             errmsg("fko_get_spa_data", res);
341             fko_destroy(ctx);
342             return(EXIT_FAILURE);
343         }
344
345         /* If gpg-home-dir is specified, we have to defer decrypting if we
346          * use the fko_new_with_data() function because we need to set the
347          * gpg home dir after the context is created, but before we attempt
348          * to decrypt the data.  Therefore we either pass NULL for the
349          * decryption key to fko_new_with_data() or use fko_new() to create
350          * an empty context, populate it with the encrypted data, set our
351          * options, then decode it.
352         */
353         res = fko_new_with_data(&ctx2, spa_data, NULL);
354         if(res != FKO_SUCCESS)
355         {
356             errmsg("fko_new_with_data", res);
357             fko_destroy(ctx);
358             fko_destroy(ctx2);
359             return(EXIT_FAILURE);
360         }
361
362         /* See if we are using gpg and if we need to set the GPG home dir.
363         */
364         if(options.use_gpg)
365         {
366             if(options.gpg_home_dir != NULL && strlen(options.gpg_home_dir) > 0)
367             {
368                 res = fko_set_gpg_home_dir(ctx2, options.gpg_home_dir);
369                 if(res != FKO_SUCCESS)
370                 {
371                     errmsg("fko_set_gpg_home_dir", res);
372                     fko_destroy(ctx);
373                     fko_destroy(ctx2);
374                     return(EXIT_FAILURE);
375                 }
376             }
377         }
378
379         res = fko_decrypt_spa_data(
380             ctx2, get_user_pw(ctx2, &options, CRYPT_OP_DECRYPT)
381         );
382
383         if(res != FKO_SUCCESS)
384         {
385             errmsg("fko_decrypt_spa_data", res);
386
387             if(IS_GPG_ERROR(res)) {
388                 /* we most likely could not decrypt the gpg-encrypted data
389                  * because we don't have access to the private key associated
390                  * with the public key we used for encryption.  Since this is
391                  * expected, return 0 instead of an error condition (so calling
392                  * programs like the fwknop test suite don't interpret this as
393                  * an unrecoverable error), but print the error string for
394                  debugging purposes. */
395                 fprintf(stderr, "GPG ERR: %s\n%s\n", fko_gpg_errstr(ctx2),
396                     "No access to recipient private key?\n");
397                 fko_destroy(ctx);
398                 fko_destroy(ctx2);
399                 return(EXIT_SUCCESS);
400             }
401
402             fko_destroy(ctx);
403             fko_destroy(ctx2);
404             return(EXIT_FAILURE);
405         }
406
407         printf("\nDump of the Decoded Data\n");
408         display_ctx(ctx2);
409
410         fko_destroy(ctx2);
411     }
412
413     fko_destroy(ctx);
414
415     free_configs(&options);
416
417     return(EXIT_SUCCESS);
418 }
419
420 void
421 free_configs(fko_cli_options_t *opts)
422 {
423     if (opts->resolve_url != NULL)
424         free(opts->resolve_url);
425 }
426
427 static int
428 get_rand_port(fko_ctx_t ctx)
429 {
430     char *rand_val = NULL;
431     int   port     = 0;
432     int   res      = 0;
433
434     res = fko_get_rand_value(ctx, &rand_val);
435     if(res != FKO_SUCCESS)
436     {
437         errmsg("get_rand_port(), fko_get_rand_value", res);
438         exit(EXIT_FAILURE);
439     }
440
441     /* Convert to a random value between 1024 and 65535
442     */
443     port = (MIN_HIGH_PORT + (abs(atoi(rand_val)) % (MAX_PORT - MIN_HIGH_PORT)));
444
445     /* Force libfko to calculate a new random value since we don't want to
446      * given anyone a hint (via the port value) about the contents of the
447      * encrypted SPA data.
448     */
449     res = fko_set_rand_value(ctx, NULL);
450     if(res != FKO_SUCCESS)
451     {
452         errmsg("get_rand_port(), fko_get_rand_value", res);
453         exit(EXIT_FAILURE);
454     }
455
456     return port;
457 }
458
459 /* See if the string is of the format "<ipv4 addr>:<port>",
460  */
461 static int
462 ipv4_str_has_port(char *str)
463 {
464     int o1, o2, o3, o4, p;
465
466     /* Force the ':' (if any) to a ','
467     */
468     char *ndx = strchr(str, ':');
469     if(ndx != NULL)
470         *ndx = ',';
471
472     /* Check format and values.
473     */
474     if((sscanf(str, "%u.%u.%u.%u,%u", &o1, &o2, &o3, &o4, &p)) == 5
475         && o1 >= 0 && o1 <= 255
476         && o2 >= 0 && o2 <= 255
477         && o3 >= 0 && o3 <= 255
478         && o4 >= 0 && o4 <= 255
479         && p  >  0 && p  <  65536)
480     {
481         return 1;
482     }
483
484     return 0;
485 }
486
487 /* Set NAT access string
488 */
489 static int
490 set_nat_access(fko_ctx_t ctx, fko_cli_options_t *options)
491 {
492     char nat_access_buf[MAX_LINE_LEN] = "";
493     int nat_port = 0;
494
495     if (options->nat_rand_port)
496         nat_port = get_rand_port(ctx);
497     else if (options->nat_port)
498         nat_port = options->nat_port;
499     else
500         nat_port = DEFAULT_NAT_PORT;
501
502     if (options->nat_local && options->nat_access_str[0] == 0x0)
503     {
504         snprintf(nat_access_buf, MAX_LINE_LEN, "%s,%d",
505             options->spa_server_str, nat_port);
506     }
507
508     if (nat_access_buf[0] == 0x0 && options->nat_access_str[0] != 0x0)
509     {
510         if (ipv4_str_has_port(options->nat_access_str))
511         {
512             snprintf(nat_access_buf, MAX_LINE_LEN, "%s",
513                 options->nat_access_str);
514         }
515         else
516         {
517             snprintf(nat_access_buf, MAX_LINE_LEN, "%s,%d",
518                 options->nat_access_str, nat_port);
519         }
520     }
521
522     return fko_set_spa_nat_access(ctx, nat_access_buf);
523 }
524
525 static int
526 get_save_file(char *args_save_file)
527 {
528     char *homedir = NULL;
529     int rv = 0;
530
531     homedir = getenv("HOME");
532
533     if (homedir != NULL) {
534         snprintf(args_save_file, MAX_PATH_LEN, "%s%s%s",
535             homedir, "/", ".fwknop.run");
536         rv = 1;
537     }
538
539     return rv;
540 }
541
542 /* Show the last command that was executed
543 */
544 static void
545 show_last_command(void)
546 {
547     char args_save_file[MAX_PATH_LEN];
548     char args_str[MAX_LINE_LEN] = "";
549     FILE *args_file_ptr = NULL;
550
551 #ifdef WIN32
552     /* Not sure what the right thing is here on Win32, just exit
553      * for now.
554     */
555     fprintf(stderr, "--show-last not implemented on Win32 yet.");
556     exit(EXIT_FAILURE);
557 #endif
558
559     if (get_save_file(args_save_file)) {
560         if ((args_file_ptr = fopen(args_save_file, "r")) == NULL) {
561             fprintf(stderr, "Could not open args file: %s\n",
562                 args_save_file);
563             exit(EXIT_FAILURE);
564         }
565         if ((fgets(args_str, MAX_LINE_LEN, args_file_ptr)) != NULL) {
566             printf("Last fwknop client command line: %s", args_str);
567         } else {
568             printf("Could not read line from file: %s\n", args_save_file);
569         }
570         fclose(args_file_ptr);
571     }
572
573     exit(EXIT_SUCCESS);
574 }
575
576 /* Get the command line arguments from the previous invocation
577 */
578 static void
579 run_last_args(fko_cli_options_t *options)
580 {
581     FILE           *args_file_ptr = NULL;
582
583     int             current_arg_ctr = 0;
584     int             argc_new = 0;
585     int             i = 0;
586
587     char            args_save_file[MAX_PATH_LEN] = {0};
588     char            args_str[MAX_LINE_LEN] = {0};
589     char            arg_tmp[MAX_LINE_LEN]  = {0};
590     char           *argv_new[200];  /* should be way more than enough */
591
592
593 #ifdef WIN32
594     /* Not sure what the right thing is here on Win32, just return
595      * for now.
596     */
597     return;
598 #endif
599
600     if (get_save_file(args_save_file))
601     {
602         if ((args_file_ptr = fopen(args_save_file, "r")) == NULL)
603         {
604             fprintf(stderr, "Could not open args file: %s\n",
605                 args_save_file);
606             exit(EXIT_FAILURE);
607         }
608         if ((fgets(args_str, MAX_LINE_LEN, args_file_ptr)) != NULL)
609         {
610             args_str[MAX_LINE_LEN-1] = '\0';
611             if (options->verbose)
612                 printf("Executing: %s\n", args_str);
613             for (i=0; i < (int)strlen(args_str); i++)
614             {
615                 if (!isspace(args_str[i]))
616                 {
617                     arg_tmp[current_arg_ctr] = args_str[i];
618                     current_arg_ctr++;
619                 }
620                 else
621                 {
622                     arg_tmp[current_arg_ctr] = '\0';
623                     argv_new[argc_new] = malloc(strlen(arg_tmp)+1);
624                     if (argv_new[argc_new] == NULL)
625                     {
626                         fprintf(stderr, "malloc failure for cmd line arg.\n");
627                         exit(EXIT_FAILURE);
628                     }
629                     strlcpy(argv_new[argc_new], arg_tmp, strlen(arg_tmp)+1);
630                     current_arg_ctr = 0;
631                     argc_new++;
632                 }
633             }
634         }
635         fclose(args_file_ptr);
636
637         /* Reset the options index so we can run through them again.
638         */
639         optind = 0;
640
641         config_init(options, argc_new, argv_new);
642     }
643
644     return;
645 }
646
647 /* Save our command line arguments
648 */
649 static void
650 save_args(int argc, char **argv)
651 {
652     char args_save_file[MAX_PATH_LEN];
653     char args_str[MAX_LINE_LEN] = "";
654     FILE *args_file_ptr = NULL;
655     int i = 0, args_str_len = 0;
656
657 #ifdef WIN32
658     /* Not sure what the right thing is here on Win32, just return
659      * for now.
660     */
661     return;
662 #endif
663
664
665     if (get_save_file(args_save_file)) {
666         if ((args_file_ptr = fopen(args_save_file, "w")) == NULL) {
667             fprintf(stderr, "Could not open args file: %s\n",
668                 args_save_file);
669             exit(EXIT_FAILURE);
670         }
671         for (i=0; i < argc; i++) {
672             args_str_len += strlen(argv[i]);
673             if (args_str_len >= MAX_PATH_LEN) {
674                 fprintf(stderr, "argument string too long, exiting.\n");
675                 exit(EXIT_FAILURE);
676             }
677             strlcat(args_str, argv[i], MAX_PATH_LEN);
678             strlcat(args_str, " ", MAX_PATH_LEN);
679         }
680         fprintf(args_file_ptr, "%s\n", args_str);
681         fclose(args_file_ptr);
682     }
683     return;
684 }
685
686 /* Set the SPA packet message type
687 */
688 static int
689 set_message_type(fko_ctx_t ctx, fko_cli_options_t *options)
690 {
691     short message_type;
692
693     if(options->server_command[0] != 0x0)
694     {
695         message_type = FKO_COMMAND_MSG;
696     }
697     else if(options->nat_local)
698     {
699         if (options->fw_timeout >= 0)
700             message_type = FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG;
701         else
702             message_type = FKO_LOCAL_NAT_ACCESS_MSG;
703     }
704     else if(options->nat_access_str[0] != 0x0)
705     {
706         if (options->fw_timeout >= 0)
707             message_type = FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG;
708         else
709             message_type = FKO_NAT_ACCESS_MSG;
710     }
711     else
712     {
713         if (options->fw_timeout >= 0)
714             message_type = FKO_CLIENT_TIMEOUT_ACCESS_MSG;
715         else
716             message_type = FKO_ACCESS_MSG;
717     }
718
719     return fko_set_spa_message_type(ctx, message_type);
720 }
721
722 /* Prompt for and receive a user password.
723 */
724 static char*
725 get_user_pw(fko_ctx_t ctx, fko_cli_options_t *options, const int crypt_op)
726 {
727     char        *pw_ptr = NULL;
728     static char *no_pw  = "";
729
730     /* First of all if we are using GPG and GPG_AGENT
731      * then there is no password to return.
732     */
733     if(options->use_gpg
734       && (options->use_gpg_agent
735            || (crypt_op == CRYPT_OP_ENCRYPT && options->gpg_signer_key == NULL)))
736         return(no_pw);
737
738     /* If --get-key file was specified grab the key/password from it.
739     */
740     if (options->get_key_file[0] != 0x0)
741     {
742         pw_ptr = getpasswd_file(ctx, options);
743     }
744     else if (options->use_gpg)
745     {
746         if(crypt_op == CRYPT_OP_DECRYPT)
747             pw_ptr = getpasswd("Enter passphrase for secret key: ");
748         else if(options->gpg_signer_key && strlen(options->gpg_signer_key))
749             pw_ptr = getpasswd("Enter passphrase for signing: ");
750         else
751             pw_ptr = no_pw;
752     }
753     else
754     {
755         if(crypt_op == CRYPT_OP_ENCRYPT)
756             pw_ptr = getpasswd("Enter encryption password: ");
757         else if(crypt_op == CRYPT_OP_DECRYPT)
758             pw_ptr = getpasswd("Enter decryption password: ");
759         else
760             pw_ptr = getpasswd("Enter password: ");
761     }
762
763     /* Empty password is allowed, NULL password is not.
764     */
765     if (pw_ptr == NULL)
766     {
767         fprintf(stderr, "Received no password data, exiting.\n");
768         fko_destroy(ctx);
769         exit(EXIT_FAILURE);
770     }
771
772     return pw_ptr;
773 }
774
775 /* Display an FKO error message.
776 */
777 void
778 errmsg(const char *msg, const int err) {
779     fprintf(stderr, "%s: %s: Error %i - %s\n",
780         MY_NAME, msg, err, fko_errstr(err));
781 }
782
783 /* Show the fields of the FKO context.
784 */
785 static void
786 display_ctx(fko_ctx_t ctx)
787 {
788     char       *rand_val        = NULL;
789     char       *username        = NULL;
790     char       *version         = NULL;
791     char       *spa_message     = NULL;
792     char       *nat_access      = NULL;
793     char       *server_auth     = NULL;
794     char       *enc_data        = NULL;
795     char       *spa_digest      = NULL;
796     char       *spa_data        = NULL;
797
798     time_t      timestamp       = 0;
799     short       msg_type        = -1;
800     short       digest_type     = -1;
801     int         client_timeout  = -1;
802
803     /* Should be checking return values, but this is temp code. --DSS
804     */
805     fko_get_rand_value(ctx, &rand_val);
806     fko_get_username(ctx, &username);
807     fko_get_timestamp(ctx, &timestamp);
808     fko_get_version(ctx, &version);
809     fko_get_spa_message_type(ctx, &msg_type);
810     fko_get_spa_message(ctx, &spa_message);
811     fko_get_spa_nat_access(ctx, &nat_access);
812     fko_get_spa_server_auth(ctx, &server_auth);
813     fko_get_spa_client_timeout(ctx, &client_timeout);
814     fko_get_spa_digest_type(ctx, &digest_type);
815     fko_get_encoded_data(ctx, &enc_data);
816     fko_get_spa_digest(ctx, &spa_digest);
817     fko_get_spa_data(ctx, &spa_data);
818
819     printf("\nFKO Field Values:\n=================\n\n");
820     printf("   Random Value: %s\n", rand_val == NULL ? "<NULL>" : rand_val);
821     printf("       Username: %s\n", username == NULL ? "<NULL>" : username);
822     printf("      Timestamp: %u\n", (unsigned int) timestamp);
823     printf("    FKO Version: %s\n", version == NULL ? "<NULL>" : version);
824     printf("   Message Type: %i\n", msg_type);
825     printf(" Message String: %s\n", spa_message == NULL ? "<NULL>" : spa_message);
826     printf("     Nat Access: %s\n", nat_access == NULL ? "<NULL>" : nat_access);
827     printf("    Server Auth: %s\n", server_auth == NULL ? "<NULL>" : server_auth);
828     printf(" Client Timeout: %u\n", client_timeout);
829     printf("    Digest Type: %u\n", digest_type);
830     printf("\n   Encoded Data: %s\n", enc_data == NULL ? "<NULL>" : enc_data);
831     printf("\nSPA Data Digest: %s\n", spa_digest == NULL ? "<NULL>" : spa_digest);
832     printf("\nFinal Packed/Encrypted/Encoded Data:\n\n%s\n\n", spa_data);
833 }
834
835 /***EOF***/