"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.6.2/src/pgp.c" (9 Dec 2022, 13291 Bytes) of package /linux/misc/tin-2.6.2.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "pgp.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.6.1_vs_2.6.2.

    1 /*
    2  *  Project   : tin - a Usenet reader
    3  *  Module    : pgp.c
    4  *  Author    : Steven J. Madsen
    5  *  Created   : 1995-05-12
    6  *  Updated   : 2022-02-19
    7  *  Notes     : PGP support
    8  *
    9  * Copyright (c) 1995-2023 Steven J. Madsen <steve@erinet.com>
   10  * All rights reserved.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  *
   16  * 1. Redistributions of source code must retain the above copyright notice,
   17  *    this list of conditions and the following disclaimer.
   18  *
   19  * 2. Redistributions in binary form must reproduce the above copyright
   20  *    notice, this list of conditions and the following disclaimer in the
   21  *    documentation and/or other materials provided with the distribution.
   22  *
   23  * 3. Neither the name of the copyright holder nor the names of its
   24  *    contributors may be used to endorse or promote products derived from
   25  *    this software without specific prior written permission.
   26  *
   27  * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   37  * POSSIBILITY OF SUCH DAMAGE.
   38  */
   39 
   40 
   41 #ifndef TIN_H
   42 #   include "tin.h"
   43 #endif /* !TIN_H */
   44 
   45 #ifdef HAVE_PGP_GPG
   46 #   ifndef TCURSES_H
   47 #       include "tcurses.h"
   48 #   endif /* !TCURSES_H */
   49 
   50 
   51 /*
   52  * The first two args are typically the PGP command name and then $PGPOPTS
   53  * NB: The '1' variations on DO_{SIGN,BOTH} are used when local-user name is
   54  * used and are valid only when signing
   55  */
   56 #   if defined(HAVE_PGP) /* pgp-2 */
   57 #       define PGPNAME      PATH_PGP
   58 #       define PGPDIR       ".pgp"
   59 #       define PGP_PUBRING  "pubring.pgp"
   60 #       define CHECK_SIGN   "%s %s -f <%s %s"
   61 #       define ADD_KEY      "%s %s -ka %s"
   62 #       define APPEND_KEY   "%s %s -kxa %s %s", PGPNAME, pgpopts, buf, keyfile
   63 #       define DO_ENCRYPT   "%s %s -ate %s %s", PGPNAME, pgpopts, pt, mailto
   64 #       define DO_SIGN      "%s %s -ats %s %s", PGPNAME, pgpopts, pt, mailto
   65 #       define DO_SIGN1     "%s %s -ats %s %s -u %s", PGPNAME, pgpopts, pt, mailto, mailfrom
   66 #       define DO_BOTH      "%s %s -ates %s %s", PGPNAME, pgpopts, pt, mailto
   67 #       define DO_BOTH1     "%s %s -ates %s %s -u %s", PGPNAME, pgpopts, pt, mailto, mailfrom
   68 #   endif /* HAVE_PGP */
   69 
   70 #   if defined(HAVE_PGPK) /* pgp-5 */
   71 #       define PGPNAME      "pgp"   /* FIXME: this is AFAIK not PATH_PGPK */
   72 #       define PGPDIR       ".pgp"
   73 #       define PGP_PUBRING  "pubring.pkr"
   74 #       define CHECK_SIGN   "%sv %s -f <%s %s"
   75 #       define ADD_KEY      "%sk %s -a %s"
   76 #       define APPEND_KEY   "%sk %s -xa %s -o %s", PGPNAME, pgpopts, keyfile, buf
   77 #       define DO_ENCRYPT   "%se %s -at %s %s", PGPNAME, pgpopts, pt, mailto
   78 #       define DO_SIGN      "%ss %s -at %s %s", PGPNAME, pgpopts, pt, mailto
   79 #       define DO_SIGN1     "%ss %s -at %s %s -u %s", PGPNAME, pgpopts, pt, mailto, mailfrom
   80 #       define DO_BOTH      "%se %s -ats %s %s", PGPNAME, pgpopts, pt, mailto
   81 #       define DO_BOTH1     "%se %s -ats %s %s -u %s", PGPNAME, pgpopts, pt, mailto, mailfrom
   82 #   endif /* HAVE_PGPK */
   83 
   84 #   if defined(HAVE_GPG) /* gpg */
   85 #       define PGPNAME      PATH_GPG
   86 #       define PGPDIR       ".gnupg"
   87 #       define PGP_PUBRING  "pubring.gpg"
   88 #       if 0 /* gpg 1.4.11 doesn't like this */
   89 #           define CHECK_SIGN   "%s %s --no-batch --decrypt <%s %s"
   90 #       else
   91 #           define CHECK_SIGN   "%s %s < %s %s"
   92 #       endif /* 0 */
   93 #       define ADD_KEY      "%s %s --no-batch --import %s"
   94 #       define APPEND_KEY   "%s %s --no-batch --armor --output %s --export %s", PGPNAME, pgpopts, keyfile, buf
   95 #       define DO_ENCRYPT   \
   96 "%s %s --textmode --armor --no-batch --output %s.asc --recipient %s --encrypt %s", \
   97 PGPNAME, pgpopts, pt, mailto, pt
   98 #       define DO_SIGN      \
   99 "%s %s --textmode --armor --no-batch --output %s.asc --escape-from --clearsign %s", \
  100 PGPNAME, pgpopts, pt, pt
  101 #       define DO_SIGN1     \
  102 "%s %s --textmode --armor --no-batch --local-user %s --output %s.asc --escape-from --clearsign %s", \
  103 PGPNAME, pgpopts, mailfrom, pt, pt
  104 #       define DO_BOTH      \
  105 "%s %s --textmode --armor --no-batch --output %s.asc --recipient %s --sign --encrypt %s", \
  106 PGPNAME, pgpopts, pt, mailto, pt
  107 #       define DO_BOTH1     \
  108 "%s %s --textmode --armor --no-batch --output %s.asc --recipient %s --local-user %s --sign --encrypt %s", \
  109 PGPNAME, pgpopts, pt, mailto, mailfrom, pt
  110 #   endif /* HAVE_GPG */
  111 
  112 /* RFC 4880 6.2 */
  113 #   define PGP_SIG_TAG "-----BEGIN PGP SIGNED MESSAGE-----\n"
  114 #   define PGP_KEY_TAG "-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
  115 
  116 #   define HEADERS  "tin-%ld.h"
  117 #   ifdef HAVE_LONG_FILE_NAMES
  118 #       define PLAINTEXT    "tin-%ld.pt"
  119 #       define CIPHERTEXT   "tin-%ld.pt.asc"
  120 #       define KEYFILE      "tin-%ld.k.asc"
  121 #   else
  122 #       define PLAINTEXT    "tn-%ld.p"
  123 #       define CIPHERTEXT   "tn-%ld.p.asc"
  124 #       define KEYFILE      "tn-%ld.k.asc"
  125 #   endif /* HAVE_LONG_FILE_NAMES */
  126 
  127 
  128 /*
  129  * local prototypes
  130  */
  131 static t_bool pgp_available(void);
  132 static void do_pgp(t_function what, const char *file, const char *mail_to);
  133 static void join_files(const char *file);
  134 static void pgp_append_public_key(char *file);
  135 static void split_file(const char *file);
  136 
  137 static char pgp_data[PATH_LEN];
  138 static char hdr[PATH_LEN], pt[PATH_LEN], ct[PATH_LEN];
  139 static const char *pgpopts = "";
  140 
  141 
  142 void
  143 init_pgp(
  144     void)
  145 {
  146     char *ptr;
  147 
  148     pgpopts = get_val("PGPOPTS", "");
  149 
  150 #   ifdef HAVE_GPG
  151     if ((ptr = getenv("GNUPGHOME")) != NULL)
  152         my_strncpy(pgp_data, ptr, sizeof(pgp_data) - 1);
  153     else
  154 #   endif /* HAVE_GPG */
  155     {
  156         if ((ptr = getenv("PGPPATH")) != NULL)
  157             my_strncpy(pgp_data, ptr, sizeof(pgp_data) - 1);
  158         else
  159             joinpath(pgp_data, sizeof(pgp_data), homedir, PGPDIR);
  160     }
  161 }
  162 
  163 
  164 /*
  165  * Write the header file then the ciphertext file to the art file
  166  * This function is void, no way to return errs
  167  */
  168 static void
  169 join_files(
  170     const char *file)
  171 {
  172     FILE *art, *header, *text;
  173 
  174     if ((header = fopen(hdr, "r")) != NULL) {
  175         if ((text = fopen(ct, "r")) != NULL) {
  176             if ((art = fopen(file, "w")) != NULL) {
  177                 if (copy_fp(header, art))
  178                     copy_fp(text, art);
  179                 fclose(art);
  180             }
  181             fclose(text);
  182         }
  183         fclose(header);
  184     }
  185 
  186     unlink(hdr);
  187     unlink(pt);
  188     unlink(ct);
  189 }
  190 
  191 
  192 /*
  193  * Split the file parameter into a header file 'hdr' and a plaintext
  194  * file 'pt'
  195  */
  196 static void
  197 split_file(
  198     const char *file)
  199 {
  200     FILE *art, *header, *plaintext;
  201     char buf[LEN];
  202     char tmp[PATH_LEN];
  203     mode_t mask;
  204 
  205     snprintf(tmp, sizeof(tmp), HEADERS, (long) process_id);
  206     joinpath(hdr, sizeof(hdr), tmpdir, tmp);
  207     snprintf(tmp, sizeof(tmp), PLAINTEXT, (long) process_id);
  208     joinpath(pt, sizeof(pt), tmpdir, tmp);
  209     snprintf(tmp, sizeof(tmp), CIPHERTEXT, (long) process_id);
  210     joinpath(ct, sizeof(ct), tmpdir, tmp);
  211 
  212     if ((art = fopen(file, "r")) == NULL)
  213         return;
  214 
  215     mask = umask((mode_t) (S_IRWXO|S_IRWXG));
  216 
  217     if ((header = fopen(hdr, "w")) == NULL)
  218         goto err_art;
  219 
  220     if ((plaintext = fopen(pt, "w")) == NULL)
  221         goto err_hdr;
  222 
  223     if (fgets(buf, LEN, art) != NULL) {         /* Copy the hdr up to and including the \n */
  224         while (strcmp(buf, "\n")) {
  225             fputs(buf, header);
  226             fgets(buf, LEN, art);
  227         }
  228         fputs(buf, header);
  229         copy_fp(art, plaintext);
  230     }
  231 
  232     fclose(plaintext);
  233 err_hdr:
  234     fclose(header);
  235 err_art:
  236     fclose(art);
  237     umask(mask);
  238 }
  239 
  240 
  241 static void
  242 do_pgp(
  243     t_function what,
  244     const char *file,
  245     const char *mail_to)
  246 {
  247     char cmd[LEN];
  248     char mailfrom[LEN];
  249     const char *mailto = BlankIfNull(mail_to);
  250 
  251     mailfrom[0] = '\0';
  252 
  253     split_file(file);
  254 
  255     /*
  256      * <mailfrom> is valid only when signing and a local address exists
  257      */
  258     if ((CURR_GROUP.attribute->from) != NULL)
  259         strip_name(CURR_GROUP.attribute->from, mailfrom);
  260 
  261     switch (what) {
  262         case PGP_KEY_SIGN:
  263             if (*mailfrom)
  264                 sh_format(cmd, sizeof(cmd), DO_SIGN1);
  265             else
  266                 sh_format(cmd, sizeof(cmd), DO_SIGN);
  267             invoke_cmd(cmd);
  268             break;
  269 
  270         case PGP_KEY_ENCRYPT_SIGN:
  271             if (*mailfrom)
  272                 sh_format(cmd, sizeof(cmd), DO_BOTH1);
  273             else
  274                 sh_format(cmd, sizeof(cmd), DO_BOTH);
  275             invoke_cmd(cmd);
  276             break;
  277 
  278         case PGP_KEY_ENCRYPT:
  279             sh_format(cmd, sizeof(cmd), DO_ENCRYPT);
  280             invoke_cmd(cmd);
  281             break;
  282 
  283         default:
  284             break;
  285     }
  286 
  287     join_files(file);
  288 }
  289 
  290 
  291 static void
  292 pgp_append_public_key(
  293     char *file)
  294 {
  295     FILE *fp, *key;
  296     char cmd[LEN], buf[LEN];
  297     char keyfile[PATH_LEN], tmp[PATH_LEN];
  298 
  299     if ((CURR_GROUP.attribute->from) != NULL && strlen(CURR_GROUP.attribute->from))
  300         strip_name(CURR_GROUP.attribute->from, buf);
  301     else
  302         snprintf(buf, sizeof(buf), "%s@%s", userid, BlankIfNull(get_host_name()));
  303 
  304     snprintf(tmp, sizeof(tmp), KEYFILE, (long) process_id);
  305     joinpath(keyfile, sizeof(keyfile), tmpdir, tmp);
  306 
  307 /*
  308  * TODO: I'm guessing the pgp append key command creates 'keyfile' and that
  309  * we should remove it
  310  */
  311     sh_format(cmd, sizeof(cmd), APPEND_KEY);
  312     if (invoke_cmd(cmd)) {
  313         if ((fp = fopen(file, "a")) != NULL) {
  314             if ((key = fopen(keyfile, "r")) != NULL) {
  315                 fputc('\n', fp);            /* Add a blank line */
  316                 copy_fp(key, fp);           /* and copy in the key */
  317                 fclose(key);
  318             }
  319             fclose(fp);
  320         }
  321         unlink(keyfile);
  322     }
  323 }
  324 
  325 
  326 /*
  327  * Simply check for existence of keyring file
  328  */
  329 static t_bool
  330 pgp_available(
  331     void)
  332 {
  333     FILE *fp;
  334     char keyring[PATH_LEN];
  335 
  336     joinpath(keyring, sizeof(keyring), pgp_data, PGP_PUBRING);
  337     if ((fp = fopen(keyring, "r")) == NULL) {
  338         wait_message(2, _(txt_pgp_not_avail), keyring);
  339         return FALSE;
  340     }
  341 
  342     fclose(fp);
  343     return TRUE;
  344 }
  345 
  346 
  347 void
  348 invoke_pgp_mail(
  349     const char *nam,
  350     char *mail_to)
  351 {
  352     char keyboth[MAXKEYLEN], keyencrypt[MAXKEYLEN], keyquit[MAXKEYLEN];
  353     char keysign[MAXKEYLEN];
  354     t_function func, default_func = PGP_KEY_SIGN;
  355 
  356     if (!pgp_available())
  357         return;
  358 
  359     func = prompt_slk_response(default_func, pgp_mail_keys, _(txt_pgp_mail),
  360             PrintFuncKey(keyencrypt, PGP_KEY_ENCRYPT, pgp_mail_keys),
  361             PrintFuncKey(keysign, PGP_KEY_SIGN, pgp_mail_keys),
  362             PrintFuncKey(keyboth, PGP_KEY_ENCRYPT_SIGN, pgp_mail_keys),
  363             PrintFuncKey(keyquit, GLOBAL_QUIT, pgp_mail_keys));
  364     switch (func) {
  365         case PGP_KEY_SIGN:
  366 #   ifdef HAVE_PGPK
  367             ClearScreen();
  368             MoveCursor(cLINES - 7, 0);
  369 #   endif /* HAVE_PGPK */
  370             do_pgp(func, nam, NULL);
  371             break;
  372 
  373         case PGP_KEY_ENCRYPT_SIGN:
  374 #   ifdef HAVE_PGPK
  375             ClearScreen();
  376             MoveCursor(cLINES - 7, 0);
  377 #   endif /* HAVE_PGPK */
  378             do_pgp(func, nam, mail_to);
  379             break;
  380 
  381         case PGP_KEY_ENCRYPT:
  382             do_pgp(func, nam, mail_to);
  383             break;
  384 
  385         case GLOBAL_ABORT:
  386         case GLOBAL_QUIT:
  387         default:
  388             break;
  389     }
  390 }
  391 
  392 
  393 void
  394 invoke_pgp_news(
  395     char *artfile)
  396 {
  397     char keyinclude[MAXKEYLEN], keyquit[MAXKEYLEN], keysign[MAXKEYLEN];
  398     t_function func, default_func = PGP_KEY_SIGN;
  399 
  400     if (!pgp_available())
  401         return;
  402 
  403     func = prompt_slk_response(default_func, pgp_news_keys, _(txt_pgp_news),
  404                 PrintFuncKey(keysign, PGP_KEY_SIGN, pgp_news_keys),
  405                 PrintFuncKey(keyinclude, PGP_INCLUDE_KEY, pgp_news_keys),
  406                 PrintFuncKey(keyquit, GLOBAL_QUIT, pgp_news_keys));
  407     switch (func) {
  408         case GLOBAL_ABORT:
  409         case GLOBAL_QUIT:
  410             break;
  411 
  412         case PGP_KEY_SIGN:
  413 #   ifdef HAVE_PGPK
  414             info_message(" ");
  415             MoveCursor(cLINES - 7, 0);
  416             my_printf("\n");
  417 #   endif /* HAVE_PGPK */
  418             do_pgp(func, artfile, NULL);
  419             break;
  420 
  421         case PGP_INCLUDE_KEY:
  422 #   ifdef HAVE_PGPK
  423             info_message(" ");
  424             MoveCursor(cLINES - 7, 0);
  425             my_printf("\n");
  426 #   endif /* HAVE_PGPK */
  427             do_pgp(PGP_KEY_SIGN, artfile, NULL);
  428             pgp_append_public_key(artfile);
  429             break;
  430 
  431         default:
  432             break;
  433     }
  434 }
  435 
  436 
  437 t_bool
  438 pgp_check_article(
  439     t_openartinfo *artinfo)
  440 {
  441     FILE *art;
  442     char artfile[PATH_LEN], buf[LEN], cmd[LEN];
  443     t_bool pgp_signed = FALSE;
  444     t_bool pgp_key = FALSE;
  445 
  446     if (!pgp_available())
  447         return FALSE;
  448 
  449     joinpath(artfile, sizeof(artfile), homedir, TIN_ARTICLE_NAME);
  450 #   ifdef APPEND_PID
  451     snprintf(artfile + strlen(artfile), sizeof(artfile) - strlen(artfile), ".%ld", (long) process_id);
  452 #   endif /* APPEND_PID */
  453     if ((art = fopen(artfile, "w")) == NULL) {
  454         info_message(_(txt_cannot_open), artfile);
  455         return FALSE;
  456     }
  457     /* -> start of body */
  458     if (fseek(artinfo->raw, artinfo->hdr.ext->offset, SEEK_SET) != 0) {
  459         fclose(art);
  460         return FALSE;
  461     }
  462 
  463     if (fgets(buf, LEN, artinfo->raw) != NULL) {        /* Copy the body whilst looking for SIG/KEY tags */
  464         while (!feof(artinfo->raw)) {
  465             if (!pgp_signed && !strcmp(buf, PGP_SIG_TAG))
  466                 pgp_signed = TRUE;
  467             if (!pgp_key && !strcmp(buf, PGP_KEY_TAG))
  468                 pgp_key = TRUE;
  469             fputs(buf, art);
  470             fgets(buf, LEN, artinfo->raw);
  471         }
  472     }
  473     fclose(art);
  474 
  475     if (!(pgp_signed || pgp_key)) {
  476         info_message(_(txt_pgp_nothing));
  477         return FALSE;
  478     }
  479     ClearScreen();
  480 
  481     if (pgp_signed) {
  482         Raw(FALSE);
  483 
  484         /*
  485          * We don't use sh_format here else the redirection get misquoted
  486          */
  487         snprintf(cmd, sizeof(cmd), CHECK_SIGN, PGPNAME, pgpopts, artfile, REDIRECT_PGP_OUTPUT);
  488         invoke_cmd(cmd);
  489         my_printf("\n");
  490         Raw(TRUE);
  491     }
  492 #   ifndef USE_CURSES
  493     EndWin();
  494     Raw(FALSE);
  495 #   endif /* !USE_CURSES */
  496     prompt_continue();
  497 #   ifndef USE_CURSES
  498     Raw(TRUE);
  499     InitWin();
  500 #   endif /* !USE_CURSES */
  501     if (pgp_key) {
  502         if (prompt_yn(_(txt_pgp_add), FALSE) == 1) {
  503             Raw(FALSE);
  504 
  505             sh_format(cmd, sizeof(cmd), ADD_KEY, PGPNAME, pgpopts, artfile);
  506             invoke_cmd(cmd);
  507             my_printf("\n");
  508             Raw(TRUE);
  509         }
  510     }
  511 
  512     unlink(artfile);
  513     return TRUE;
  514 }
  515 #endif /* HAVE_PGP_GPG */