"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.5/src/pgp.c" (1 Dec 2020, 13315 Bytes) of package /linux/misc/tin-2.4.5.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.4.4_vs_2.4.5.

    1 /*
    2  *  Project   : tin - a Usenet reader
    3  *  Module    : pgp.c
    4  *  Author    : Steven J. Madsen
    5  *  Created   : 1995-05-12
    6  *  Updated   : 2017-03-28
    7  *  Notes     : PGP support
    8  *
    9  * Copyright (c) 1995-2021 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 #   define PGP_SIG_TAG "-----BEGIN PGP SIGNED MESSAGE-----\n"
  113 #   define PGP_KEY_TAG "-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
  114 
  115 #   define HEADERS  "tin-%ld.h"
  116 #   ifdef HAVE_LONG_FILE_NAMES
  117 #       define PLAINTEXT    "tin-%ld.pt"
  118 #       define CIPHERTEXT   "tin-%ld.pt.asc"
  119 #       define KEYFILE      "tin-%ld.k.asc"
  120 #   else
  121 #       define PLAINTEXT    "tn-%ld.p"
  122 #       define CIPHERTEXT   "tn-%ld.p.asc"
  123 #       define KEYFILE      "tn-%ld.k.asc"
  124 #   endif /* HAVE_LONG_FILE_NAMES */
  125 
  126 
  127 /*
  128  * local prototypes
  129  */
  130 static t_bool pgp_available(void);
  131 static void do_pgp(t_function what, const char *file, const char *mail_to);
  132 static void join_files(const char *file);
  133 static void pgp_append_public_key(char *file);
  134 static void split_file(const char *file);
  135 
  136 static char pgp_data[PATH_LEN];
  137 static char hdr[PATH_LEN], pt[PATH_LEN], ct[PATH_LEN];
  138 static const char *pgpopts = "";
  139 
  140 
  141 void
  142 init_pgp(
  143     void)
  144 {
  145     char *ptr;
  146 
  147     pgpopts = get_val("PGPOPTS", "");
  148 
  149 #   ifdef HAVE_GPG
  150     if ((ptr = getenv("GNUPGHOME")) != NULL)
  151         my_strncpy(pgp_data, ptr, sizeof(pgp_data) - 1);
  152     else
  153 #   endif /* HAVE_GPG */
  154     {
  155         if ((ptr = getenv("PGPPATH")) != NULL)
  156             my_strncpy(pgp_data, ptr, sizeof(pgp_data) - 1);
  157         else
  158             joinpath(pgp_data, sizeof(pgp_data), homedir, PGPDIR);
  159     }
  160 }
  161 
  162 
  163 /*
  164  * Write the header file then the ciphertext file to the art file
  165  * This function is void, no way to return errs
  166  */
  167 static void
  168 join_files(
  169     const char *file)
  170 {
  171     FILE *art, *header, *text;
  172 
  173     if ((header = fopen(hdr, "r")) != NULL) {
  174         if ((text = fopen(ct, "r")) != NULL) {
  175             if ((art = fopen(file, "w")) != NULL) {
  176                 if (copy_fp(header, art))
  177                     copy_fp(text, art);
  178                 fclose(art);
  179             }
  180             fclose(text);
  181         }
  182         fclose(header);
  183     }
  184 
  185     unlink(hdr);
  186     unlink(pt);
  187     unlink(ct);
  188 }
  189 
  190 
  191 /*
  192  * Split the file parameter into a header file 'hdr' and a plaintext
  193  * file 'pt'
  194  */
  195 static void
  196 split_file(
  197     const char *file)
  198 {
  199     FILE *art, *header, *plaintext;
  200     char buf[LEN];
  201     char tmp[PATH_LEN];
  202     mode_t mask;
  203 
  204     snprintf(tmp, sizeof(tmp), HEADERS, (long) process_id);
  205     joinpath(hdr, sizeof(hdr), TMPDIR, tmp);
  206     snprintf(tmp, sizeof(tmp), PLAINTEXT, (long) process_id);
  207     joinpath(pt, sizeof(pt), TMPDIR, tmp);
  208     snprintf(tmp, sizeof(tmp), CIPHERTEXT, (long) process_id);
  209     joinpath(ct, sizeof(ct), TMPDIR, tmp);
  210 
  211     if ((art = fopen(file, "r")) == NULL)
  212         return;
  213 
  214     mask = umask((mode_t) (S_IRWXO|S_IRWXG));
  215 
  216     if ((header = fopen(hdr, "w")) == NULL)
  217         goto err_art;
  218 
  219     if ((plaintext = fopen(pt, "w")) == NULL)
  220         goto err_hdr;
  221 
  222     fgets(buf, LEN, art);           /* Copy the hdr up to and including the \n */
  223     while (strcmp(buf, "\n")) {
  224         fputs(buf, header);
  225         fgets(buf, LEN, art);
  226     }
  227     fputs(buf, header);
  228     copy_fp(art, plaintext);
  229 
  230     fclose(plaintext);
  231 err_hdr:
  232     fclose(header);
  233 err_art:
  234     fclose(art);
  235     umask(mask);
  236 }
  237 
  238 
  239 static void
  240 do_pgp(
  241     t_function what,
  242     const char *file,
  243     const char *mail_to)
  244 {
  245     char cmd[LEN];
  246     char mailfrom[LEN];
  247     const char *mailto = BlankIfNull(mail_to);
  248 
  249     mailfrom[0] = '\0';
  250 
  251     split_file(file);
  252 
  253     /*
  254      * <mailfrom> is valid only when signing and a local address exists
  255      */
  256     if ((CURR_GROUP.attribute->from) != NULL)
  257         strip_name(CURR_GROUP.attribute->from, mailfrom);
  258 
  259     switch (what) {
  260         case PGP_KEY_SIGN:
  261             if (strlen(mailfrom))
  262                 sh_format(cmd, sizeof(cmd), DO_SIGN1);
  263             else
  264                 sh_format(cmd, sizeof(cmd), DO_SIGN);
  265             invoke_cmd(cmd);
  266             break;
  267 
  268         case PGP_KEY_ENCRYPT_SIGN:
  269             if (strlen(mailfrom))
  270                 sh_format(cmd, sizeof(cmd), DO_BOTH1);
  271             else
  272                 sh_format(cmd, sizeof(cmd), DO_BOTH);
  273             invoke_cmd(cmd);
  274             break;
  275 
  276         case PGP_KEY_ENCRYPT:
  277             sh_format(cmd, sizeof(cmd), DO_ENCRYPT);
  278             invoke_cmd(cmd);
  279             break;
  280 
  281         default:
  282             break;
  283     }
  284 
  285     join_files(file);
  286 }
  287 
  288 
  289 static void
  290 pgp_append_public_key(
  291     char *file)
  292 {
  293     FILE *fp, *key;
  294     char cmd[LEN], buf[LEN];
  295     char keyfile[PATH_LEN], tmp[PATH_LEN];
  296 
  297     if ((CURR_GROUP.attribute->from) != NULL && strlen(CURR_GROUP.attribute->from))
  298         strip_name(CURR_GROUP.attribute->from, buf);
  299     else
  300         snprintf(buf, sizeof(buf), "%s@%s", userid, BlankIfNull(get_host_name()));
  301 
  302     snprintf(tmp, sizeof(tmp), KEYFILE, (long) process_id);
  303     joinpath(keyfile, sizeof(keyfile), TMPDIR, tmp);
  304 
  305 /*
  306  * TODO: I'm guessing the pgp append key command creates 'keyfile' and that
  307  * we should remove it
  308  */
  309     sh_format(cmd, sizeof(cmd), APPEND_KEY);
  310     if (invoke_cmd(cmd)) {
  311         if ((fp = fopen(file, "a")) != NULL) {
  312             if ((key = fopen(keyfile, "r")) != NULL) {
  313                 fputc('\n', fp);            /* Add a blank line */
  314                 copy_fp(key, fp);           /* and copy in the key */
  315                 fclose(key);
  316             }
  317             fclose(fp);
  318         }
  319         unlink(keyfile);
  320     }
  321 }
  322 
  323 
  324 /*
  325  * Simply check for existence of keyring file
  326  */
  327 static t_bool
  328 pgp_available(
  329     void)
  330 {
  331     FILE *fp;
  332     char keyring[PATH_LEN];
  333 
  334     joinpath(keyring, sizeof(keyring), pgp_data, PGP_PUBRING);
  335     if ((fp = fopen(keyring, "r")) == NULL) {
  336         wait_message(2, _(txt_pgp_not_avail), keyring);
  337         return FALSE;
  338     }
  339 
  340     fclose(fp);
  341     return TRUE;
  342 }
  343 
  344 
  345 void
  346 invoke_pgp_mail(
  347     const char *nam,
  348     char *mail_to)
  349 {
  350     char keyboth[MAXKEYLEN], keyencrypt[MAXKEYLEN], keyquit[MAXKEYLEN];
  351     char keysign[MAXKEYLEN];
  352     t_function func, default_func = PGP_KEY_SIGN;
  353 
  354     if (!pgp_available())
  355         return;
  356 
  357     func = prompt_slk_response(default_func, pgp_mail_keys, _(txt_pgp_mail),
  358             printascii(keyencrypt, func_to_key(PGP_KEY_ENCRYPT, pgp_mail_keys)),
  359             printascii(keysign, func_to_key(PGP_KEY_SIGN, pgp_mail_keys)),
  360             printascii(keyboth, func_to_key(PGP_KEY_ENCRYPT_SIGN, pgp_mail_keys)),
  361             printascii(keyquit, func_to_key(GLOBAL_QUIT, pgp_mail_keys)));
  362     switch (func) {
  363         case PGP_KEY_SIGN:
  364 #   ifdef HAVE_PGPK
  365             ClearScreen();
  366             MoveCursor(cLINES - 7, 0);
  367 #   endif /* HAVE_PGPK */
  368             do_pgp(func, nam, NULL);
  369             break;
  370 
  371         case PGP_KEY_ENCRYPT_SIGN:
  372 #   ifdef HAVE_PGPK
  373             ClearScreen();
  374             MoveCursor(cLINES - 7, 0);
  375 #   endif /* HAVE_PGPK */
  376             do_pgp(func, nam, mail_to);
  377             break;
  378 
  379         case PGP_KEY_ENCRYPT:
  380             do_pgp(func, nam, mail_to);
  381             break;
  382 
  383         case GLOBAL_ABORT:
  384         case GLOBAL_QUIT:
  385         default:
  386             break;
  387     }
  388 }
  389 
  390 
  391 void
  392 invoke_pgp_news(
  393     char *artfile)
  394 {
  395     char keyinclude[MAXKEYLEN], keyquit[MAXKEYLEN], keysign[MAXKEYLEN];
  396     t_function func, default_func = PGP_KEY_SIGN;
  397 
  398     if (!pgp_available())
  399         return;
  400 
  401     func = prompt_slk_response(default_func, pgp_news_keys, _(txt_pgp_news),
  402                 printascii(keysign, func_to_key(PGP_KEY_SIGN, pgp_news_keys)),
  403                 printascii(keyinclude, func_to_key(PGP_INCLUDE_KEY, pgp_news_keys)),
  404                 printascii(keyquit, func_to_key(GLOBAL_QUIT, pgp_news_keys)));
  405     switch (func) {
  406         case GLOBAL_ABORT:
  407         case GLOBAL_QUIT:
  408             break;
  409 
  410         case PGP_KEY_SIGN:
  411 #   ifdef HAVE_PGPK
  412             info_message(" ");
  413             MoveCursor(cLINES - 7, 0);
  414             my_printf("\n");
  415 #   endif /* HAVE_PGPK */
  416             do_pgp(func, artfile, NULL);
  417             break;
  418 
  419         case PGP_INCLUDE_KEY:
  420 #   ifdef HAVE_PGPK
  421             info_message(" ");
  422             MoveCursor(cLINES - 7, 0);
  423             my_printf("\n");
  424 #   endif /* HAVE_PGPK */
  425             do_pgp(PGP_KEY_SIGN, artfile, NULL);
  426             pgp_append_public_key(artfile);
  427             break;
  428 
  429         default:
  430             break;
  431     }
  432 }
  433 
  434 
  435 t_bool
  436 pgp_check_article(
  437     t_openartinfo *artinfo)
  438 {
  439     FILE *art;
  440     char artfile[PATH_LEN], buf[LEN], cmd[LEN];
  441     t_bool pgp_signed = FALSE;
  442     t_bool pgp_key = FALSE;
  443 
  444     if (!pgp_available())
  445         return FALSE;
  446 
  447     joinpath(artfile, sizeof(artfile), homedir, TIN_ARTICLE_NAME);
  448 #   ifdef APPEND_PID
  449     snprintf(artfile + strlen(artfile), sizeof(artfile) - strlen(artfile), ".%ld", (long) process_id);
  450 #   endif /* APPEND_PID */
  451     if ((art = fopen(artfile, "w")) == NULL) {
  452         info_message(_(txt_cannot_open), artfile);
  453         return FALSE;
  454     }
  455     /* -> start of body */
  456     if (fseek(artinfo->raw, artinfo->hdr.ext->offset, SEEK_SET) != 0) {
  457         fclose(art);
  458         return FALSE;
  459     }
  460 
  461     fgets(buf, LEN, artinfo->raw);      /* Copy the body whilst looking for SIG/KEY tags */
  462     while (!feof(artinfo->raw)) {
  463         if (!pgp_signed && !strcmp(buf, PGP_SIG_TAG))
  464             pgp_signed = TRUE;
  465         if (!pgp_key && !strcmp(buf, PGP_KEY_TAG))
  466             pgp_key = TRUE;
  467         fputs(buf, art);
  468         fgets(buf, LEN, artinfo->raw);
  469     }
  470     fclose(art);
  471 
  472     if (!(pgp_signed || pgp_key)) {
  473         info_message(_(txt_pgp_nothing));
  474         return FALSE;
  475     }
  476     ClearScreen();
  477 
  478     if (pgp_signed) {
  479         Raw(FALSE);
  480 
  481         /*
  482          * We don't use sh_format here else the redirection get misquoted
  483          */
  484         snprintf(cmd, sizeof(cmd), CHECK_SIGN, PGPNAME, pgpopts, artfile, REDIRECT_PGP_OUTPUT);
  485         invoke_cmd(cmd);
  486         my_printf("\n");
  487         Raw(TRUE);
  488     }
  489 #   ifndef USE_CURSES
  490     EndWin();
  491     Raw(FALSE);
  492 #   endif /* !USE_CURSES */
  493     prompt_continue();
  494 #   ifndef USE_CURSES
  495     Raw(TRUE);
  496     InitWin();
  497 #   endif /* !USE_CURSES */
  498     if (pgp_key) {
  499         if (prompt_yn(_(txt_pgp_add), FALSE) == 1) {
  500             Raw(FALSE);
  501 
  502             sh_format(cmd, sizeof(cmd), ADD_KEY, PGPNAME, pgpopts, artfile);
  503             invoke_cmd(cmd);
  504             my_printf("\n");
  505             Raw(TRUE);
  506         }
  507     }
  508 
  509     unlink(artfile);
  510     return TRUE;
  511 }
  512 #endif /* HAVE_PGP_GPG */