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