"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.6.1/src/auth.c" (22 Dec 2021, 17722 Bytes) of package /linux/misc/tin-2.6.1.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 "auth.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.6.0_vs_2.6.1.

    1 /*
    2  *  Project   : tin - a Usenet reader
    3  *  Module    : auth.c
    4  *  Author    : Dirk Nimmich <nimmich@muenster.de>
    5  *  Created   : 1997-04-05
    6  *  Updated   : 2018-06-04
    7  *  Notes     : Routines to authenticate to a news server via NNTP.
    8  *              DON'T USE get_respcode() THROUGHOUT THIS CODE.
    9  *
   10  * Copyright (c) 1997-2022 Dirk Nimmich <nimmich@muenster.de>
   11  * All rights reserved.
   12  *
   13  * Redistribution and use in source and binary forms, with or without
   14  * modification, are permitted provided that the following conditions
   15  * are met:
   16  *
   17  * 1. Redistributions of source code must retain the above copyright notice,
   18  *    this list of conditions and the following disclaimer.
   19  *
   20  * 2. Redistributions in binary form must reproduce the above copyright
   21  *    notice, this list of conditions and the following disclaimer in the
   22  *    documentation and/or other materials provided with the distribution.
   23  *
   24  * 3. Neither the name of the copyright holder nor the names of its
   25  *    contributors may be used to endorse or promote products derived from
   26  *    this software without specific prior written permission.
   27  *
   28  * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   29  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   31  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   32  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   38  * POSSIBILITY OF SUCH DAMAGE.
   39  */
   40 
   41 
   42 #ifndef TIN_H
   43 #   include "tin.h"
   44 #endif /* !TIN_H */
   45 #ifndef TCURSES_H
   46 #   include "tcurses.h"
   47 #endif /* !TCURSES_H */
   48 
   49 
   50 /*
   51  * we don't need authentication stuff at all if we don't have NNTP support
   52  */
   53 #ifdef NNTP_ABLE
   54 /*
   55  * local prototypes
   56  */
   57 static int do_authinfo_user(char *server, char *authuser, char *authpass);
   58 static t_bool read_newsauth_file(char *server, char *authuser, char *authpass);
   59 static t_bool authinfo_plain(char *server, char *authuser, t_bool startup);
   60 #   ifdef USE_SASL
   61     static char *sasl_auth_plain(char *user, char *pass);
   62     static int do_authinfo_sasl_plain(char *authuser, char *authpass);
   63 #   endif /* USE_SASL */
   64 
   65 
   66 /*
   67  * Read the ${TIN_HOMEDIR:-"$HOME"}/.newsauth file and put authentication
   68  * username and password for the specified server in the given strings.
   69  * Returns TRUE if at least a password was found, FALSE if there was
   70  * no .newsauth file or no matching server.
   71  */
   72 static t_bool
   73 read_newsauth_file(
   74     char *server,
   75     char *authuser,
   76     char *authpass)
   77 {
   78     FILE *fp;
   79     char *_authpass;
   80     char *ptr;
   81     char filename[PATH_LEN];
   82     char line[PATH_LEN];
   83     int found = 0;
   84     int fd;
   85     struct stat statbuf;
   86 
   87     joinpath(filename, sizeof(filename), homedir, ".newsauth");
   88 
   89     if ((fp = fopen(filename, "r"))) {
   90         if ((fd = fileno(fp)) == -1) {
   91             fclose(fp);
   92             return FALSE;
   93         }
   94         if (fstat(fd, &statbuf) == -1) {
   95             fclose(fp);
   96             return FALSE;
   97         }
   98 
   99 #   ifndef FILE_MODE_BROKEN
  100         if (S_ISREG(statbuf.st_mode) && (statbuf.st_mode|S_IRUSR|S_IWUSR) != (S_IRUSR|S_IWUSR|S_IFREG)) {
  101             error_message(4, _(txt_error_insecure_permissions), filename, statbuf.st_mode);
  102             /*
  103              * TODO: fix permissions?
  104              * #ifdef HAVE_FCHMOD
  105              *  fchmod(fd, S_IRUSR|S_IWUSR);
  106              * #else
  107              * #    ifdef HAVE_CHMOD
  108              *  chmod(filename, S_IRUSR|S_IWUSR);
  109              * #    endif
  110              * #endif
  111              */
  112         }
  113 #   endif /* !FILE_MODE_BROKEN */
  114 
  115         /*
  116          * Search through authorization file for correct NNTP server
  117          * File has format: 'nntp-server' 'password' ['username']
  118          */
  119         while (fgets(line, sizeof(line), fp) != NULL) {
  120             /* strip trailing newline character */
  121             ptr = strchr(line, '\n');
  122             if (ptr != NULL)
  123                 *ptr = '\0';
  124 
  125             /* Get server from 1st part of the line */
  126             ptr = strpbrk(line, " \t");
  127 
  128             if (ptr == NULL)        /* no passwd, no auth, skip */
  129                 continue;
  130 
  131             *ptr++ = '\0';      /* cut off server part */
  132 
  133             if ((strcasecmp(line, server)))
  134                 continue;       /* wrong server, keep on */
  135 
  136             /* Get password from 2nd part of the line */
  137             while (*ptr == ' ' || *ptr == '\t')
  138                 ptr++;  /* skip any blanks */
  139 
  140             _authpass = ptr;
  141 
  142             if (*_authpass == '"') {    /* skip "embedded" password string */
  143                 ptr = strrchr(_authpass, '"');
  144                 if ((ptr != NULL) && (ptr > _authpass)) {
  145                     _authpass++;
  146                     *ptr++ = '\0';  /* cut off trailing " */
  147                 } else  /* no matching ", proceed as normal */
  148                     ptr = _authpass;
  149             }
  150 
  151             /* Get user from 3rd part of the line */
  152             ptr = strpbrk(ptr, " \t");  /* find next separating blank */
  153 
  154             if (ptr != NULL) {  /* a 3rd argument follows */
  155                 while (*ptr == ' ' || *ptr == '\t') /* skip any blanks */
  156                     *ptr++ = '\0';
  157                 if (*ptr != '\0')   /* if it is not just empty */
  158                     strcpy(authuser, ptr);  /* so will replace default user */
  159             }
  160             strcpy(authpass, _authpass);
  161             found++;
  162             break;  /* if we end up here, everything seems OK */
  163         }
  164         fclose(fp);
  165         return (found > 0);
  166     }
  167     return FALSE;
  168 }
  169 
  170 
  171 /*
  172  * Perform authentication with AUTHINFO USER method. Return response
  173  * code from server.
  174  *
  175  * we don't handle ERR_ENCRYPT right now
  176  *
  177  * we don't convert authuser and authpass to UTF-8 as required by 3977
  178  * and we do in do_authinfo_sasl_plain(); if we want todo so it should
  179  * lkely be done in authinfo_plain() instead.
  180  */
  181 static int
  182 do_authinfo_user(
  183     char *server,
  184     char *authuser,
  185     char *authpass)
  186 {
  187     char line[PATH_LEN];
  188     int ret;
  189 
  190     /* may violate RFC 3977 3.1; use MIN(NNTP_STRLEN, sizeof(line)) ? */
  191     snprintf(line, sizeof(line), "AUTHINFO USER %s", authuser);
  192 #   ifdef DEBUG
  193     if ((debug & DEBUG_NNTP) && verbose > 1)
  194         debug_print_file("NNTP", "authorization %s", line);
  195 #   endif /* DEBUG */
  196     put_server(line);
  197     if ((ret = get_only_respcode(NULL, 0)) != NEED_AUTHDATA)
  198         return ret;
  199 
  200     if ((authpass == NULL) || (*authpass == '\0')) {
  201 #   ifdef DEBUG
  202         if ((debug & DEBUG_NNTP) && verbose > 1)
  203             debug_print_file("NNTP", "authorization failed: no password");
  204 #   endif /* DEBUG */
  205         error_message(2, _(txt_auth_failed_nopass), server);
  206         return ERR_AUTHBAD;
  207     }
  208 
  209     /* may violate RFC 3977 3.1; use MIN(NNTP_STRLEN, sizeof(line)) ? */
  210     snprintf(line, sizeof(line), "AUTHINFO PASS %s", authpass);
  211 #   ifdef DEBUG
  212     if ((debug & DEBUG_NNTP) && verbose > 1)
  213         debug_print_file("NNTP", "authorization %s", line);
  214 #   endif /* DEBUG */
  215     put_server(line);
  216     ret = get_only_respcode(line, sizeof(line));
  217     if (!batch_mode || verbose || ret != OK_AUTH)
  218         wait_message(2, (ret == OK_AUTH ? _(txt_authorization_ok) : _(txt_authorization_fail)), authuser);
  219     return ret;
  220 }
  221 
  222 
  223 /*
  224  * NNTP user authorization. Returns TRUE if authorization succeeded,
  225  * FALSE if not.
  226  *
  227  * tries AUTHINFO SASL PLAIN (if available) fist and if not successful
  228  * AUTHINFO USER/PASS
  229  *
  230  * If username/passwd already given, and server wasn't changed, retry those.
  231  * Otherwise, read password from ~/.newsauth or, if not present or no matching
  232  * server found, from console.
  233  *
  234  * The ~/.newsauth authorization file has the format:
  235  *   nntpserver1 password [user]
  236  *   nntpserver2 password [user]
  237  *   etc.
  238  *
  239  * TODO: convert authuser and authpass to UTF-8 as required by 3977?
  240  */
  241 static t_bool
  242 authinfo_plain(
  243     char *server,
  244     char *authuser,
  245     t_bool startup)
  246 {
  247     char *authpass;
  248     int ret = ERR_AUTHBAD, changed;
  249     static char authusername[PATH_LEN] = "";
  250     static char authpassword[PATH_LEN] = "";
  251     static char last_server[PATH_LEN] = "";
  252     static t_bool already_failed = FALSE;
  253     static t_bool initialized = FALSE;
  254 
  255     if ((changed = strcmp(server, last_server)))    /* do we need new auth values? */
  256         STRCPY(last_server, server);
  257 
  258     /*
  259      * Let's try the previous auth pair first, if applicable.
  260      * Else, proceed to the other mechanisms.
  261      */
  262     if (initialized && !changed && !already_failed) {
  263 #   ifdef USE_SASL
  264         if (nntp_caps.sasl & SASL_PLAIN)
  265             ret = do_authinfo_sasl_plain(authusername, authpassword);
  266         if (ret != OK_AUTH)
  267 #   endif /* USE_SASL */
  268         {
  269             if (nntp_caps.type != CAPABILITIES || nntp_caps.authinfo_user)
  270                 ret = do_authinfo_user(server, authusername, authpassword);
  271         }
  272         return (ret == OK_AUTH);
  273     }
  274 
  275     authpassword[0] = '\0';
  276     STRCPY(authusername, authuser);
  277     authuser = authusername;
  278     authpass = authpassword;
  279 
  280     /*
  281      * No username/password given yet.
  282      * Read .newsauth only if we had not failed authentication yet for the
  283      * current server (we don't want to try wrong username/password pairs
  284      * more than once because this may lead to an infinite loop at connection
  285      * startup: nntp_open tries to authenticate, it fails, server closes
  286      * connection; next time tin tries to access the server it will do
  287      * nntp_open again ...). This means, however, that if configuration
  288      * changed on the server between two authentication attempts tin will
  289      * prompt you the second time instead of reading .newsauth (except when
  290      * at startup time; in this case, it will just leave); you have to leave
  291      * and restart tin or change to another server and back in order to get
  292      * it read again.
  293      */
  294     if ((changed || !initialized) && !already_failed) {
  295         if (read_newsauth_file(server, authuser, authpass)) {
  296 #   ifdef USE_SASL
  297             if (nntp_caps.sasl & SASL_PLAIN)
  298                 ret = do_authinfo_sasl_plain(authuser, authpass);
  299 
  300             if (ret != OK_AUTH)
  301 #   endif /* USE_SASL */
  302             {
  303                 if (force_auth_on_conn_open || nntp_caps.type != CAPABILITIES || (nntp_caps.type == CAPABILITIES && nntp_caps.authinfo_user))
  304                     ret = do_authinfo_user(server, authuser, authpass);
  305             }
  306             already_failed = (ret != OK_AUTH);
  307 
  308             if (ret == OK_AUTH) {
  309 #   ifdef DEBUG
  310                 if ((debug & DEBUG_NNTP) && verbose > 1)
  311                     debug_print_file("NNTP", "authorization succeeded");
  312 #   endif /* DEBUG */
  313                 initialized = TRUE;
  314                 return TRUE;
  315             }
  316         }
  317 #   ifdef DEBUG
  318         else {
  319             if ((debug & DEBUG_NNTP) && verbose > 1)
  320                 debug_print_file("NNTP", "read_newsauth_file(\"%s\", \"%s\", \"%s\") failed", server, authuser, authpass);
  321         }
  322 #   endif /* DEBUG */
  323     }
  324 
  325     /*
  326      * At this point, either authentication with username/password pair from
  327      * .newsauth has failed or there's no .newsauth file respectively no
  328      * matching username/password for the current server. If we are not at
  329      * startup we ask the user to enter such a pair by hand. Don't ask him
  330      * at startup except if requested by -A option because if he doesn't need
  331      * to authenticate (we don't know), the "Server expects authentication"
  332      * messages are annoying (and even wrong).
  333      * UNSURE: Maybe we want to make this decision configurable in the
  334      * options menu, too, so that the user doesn't need -A.
  335      * TODO: Put questions into do_authinfo_user() because it is possible
  336      * that the server doesn't want a password; so only ask for it if needed.
  337      */
  338     if (force_auth_on_conn_open || !startup) {
  339         if (batch_mode) { /* no interactive username/password prompting */
  340             error_message(0, _(txt_auth_needed));
  341             return (ret == OK_AUTH);
  342         }
  343         if (nntp_caps.type != CAPABILITIES || (nntp_caps.type == CAPABILITIES && !nntp_caps.authinfo_state && ((nntp_caps.sasl & SASL_PLAIN) || nntp_caps.authinfo_user || (!nntp_caps.authinfo_user && !(nntp_caps.sasl & SASL_PLAIN))))) {
  344 #   ifdef USE_CURSES
  345             int state = RawState();
  346 #   endif /* USE_CURSES */
  347 
  348             wait_message(0, _(txt_auth_needed));
  349 #   ifdef USE_CURSES
  350             Raw(TRUE);
  351 #   endif /* USE_CURSES */
  352             if (!prompt_default_string(_(txt_auth_user), authuser, sizeof(authusername) - 1, authusername, HIST_NONE)) {
  353 #   ifdef DEBUG
  354                 if ((debug & DEBUG_NNTP) && verbose > 1)
  355                     debug_print_file("NNTP", "authorization failed: no username");
  356 #   endif /* DEBUG */
  357                 return FALSE;
  358             }
  359 
  360 #   ifdef USE_CURSES
  361             Raw(state);
  362             my_printf("%s", _(txt_auth_pass));
  363             wgetnstr(stdscr, authpassword, sizeof(authpassword) - 1);
  364             authpassword[sizeof(authpassword) - 1] = '\0';
  365             Raw(TRUE);
  366 #   else
  367             /*
  368              * on some systems (i.e. Solaris) getpass(3) is limited
  369              * to 8 chars -> we use tin_getline()
  370              */
  371             STRCPY(authpassword, tin_getline(_(txt_auth_pass), 0, NULL, sizeof(authpassword) - 1, TRUE, HIST_NONE));
  372 #   endif /* USE_CURSES */
  373 
  374 #   ifdef USE_SASL
  375             if (nntp_caps.sasl & SASL_PLAIN)
  376                 ret = do_authinfo_sasl_plain(authuser, authpass);
  377             if (ret != OK_AUTH)
  378 #   endif /* USE_SASL */
  379             {
  380                 if (nntp_caps.type != CAPABILITIES || (nntp_caps.authinfo_user || !nntp_caps.authinfo_sasl)) {
  381 #   ifdef DEBUG
  382                     if (debug & DEBUG_NNTP) {
  383                         if (nntp_caps.type == CAPABILITIES && !nntp_caps.authinfo_sasl && !nntp_caps.authinfo_user)
  384                             debug_print_file("NNTP", "!!! No supported authmethod available, trying AUTHINFO USER/PASS");
  385                     }
  386 #   endif /* DEBUG */
  387                     ret = do_authinfo_user(server, authuser, authpass);
  388                     if (ret != OK_AUTH)
  389                         already_failed = TRUE;
  390                     /*
  391                      * giganews once responded to CAPABILITIES with just
  392                      * "VERSION 2", no mode-switching indication, no reader
  393                      * indication, no post indication, no authentication
  394                      * indication, ... so in case AUTHINFO USER/PASS succeeds
  395                      * if not advertised we simply go on but fully ignore
  396                      * CAPABILITIES
  397                      */
  398                     if (nntp_caps.type == CAPABILITIES && !nntp_caps.authinfo_user && !nntp_caps.authinfo_sasl && ret == OK_AUTH)
  399                         nntp_caps.type = BROKEN;
  400                 }
  401             }
  402             initialized = TRUE;
  403             my_retouch();           /* Get rid of the chaff */
  404         } else {
  405             /*
  406              * TODO:
  407              * nntp_caps.type == CAPABILITIES && nntp_caps.authinfo_state
  408              * can we change the state here? and if so how? SARTTLS? MODE
  409              * READER?
  410              */
  411 #   ifdef DEBUG
  412             if ((debug & DEBUG_NNTP) && verbose > 1)
  413                 debug_print_file("NNTP", "authorization not allowed in current state");
  414 #   endif /* DEBUG */
  415             /*
  416              * we return OK_AUTH here once so tin doesn't exit just because a
  417              * single command requested auth ...
  418              */
  419             if (!already_failed)
  420                 ret = OK_AUTH;
  421         }
  422     }
  423 
  424 #   ifdef DEBUG
  425     if ((debug & DEBUG_NNTP) && verbose > 1)
  426         debug_print_file("NNTP", "authorization %s", (ret == OK_AUTH ? "succeeded" : "failed"));
  427 #   endif /* DEBUG */
  428 
  429     return (ret == OK_AUTH);
  430 }
  431 
  432 
  433 /*
  434  * Do authentication stuff. Return TRUE if authentication was successful,
  435  * FALSE otherwise.
  436  *
  437  * try ORIGINAL AUTHINFO method.
  438  * Other authentication methods can easily be added.
  439  */
  440 t_bool
  441 authenticate(
  442     char *server,
  443     char *user,
  444     t_bool startup)
  445 {
  446     char line[NNTP_STRLEN];
  447     t_bool ret;
  448 
  449     ret = authinfo_plain(server, user, startup);
  450 
  451     if (ret && nntp_caps.type == CAPABILITIES) {
  452         /* resend CAPABILITIES, but "manually" to avoid AUTH loop */
  453         snprintf(line, sizeof(line), "%s", "CAPABILITIES");
  454 #   ifdef DEBUG
  455         if ((debug & DEBUG_NNTP) && verbose > 1)
  456             debug_print_file("NNTP", "authenticate(%s)", line);
  457 #   endif /* DEBUG */
  458         put_server(line);
  459 
  460         check_extensions(get_only_respcode(line, sizeof(line)));
  461     }
  462 
  463     return ret;
  464 }
  465 
  466 
  467 #   ifdef USE_SASL
  468 static int
  469 do_authinfo_sasl_plain(
  470     char *authuser,
  471     char *authpass)
  472 {
  473     char line[PATH_LEN];
  474     char *foo;
  475     char *utf8user;
  476     char *utf8pass;
  477     int ret;
  478 #       ifdef CHARSET_CONVERSION
  479     char *cp;
  480     int i, c = 0;
  481     t_bool contains_8bit = FALSE;
  482 #       endif /* CHARSET_CONVERSION */
  483 
  484     utf8user = my_strdup(authuser);
  485     utf8pass = my_strdup(authpass);
  486 #       ifdef CHARSET_CONVERSION
  487     /* RFC 4616 */
  488     if (!IS_LOCAL_CHARSET("UTF-8")) {
  489         for (cp = utf8user; *cp && !contains_8bit; cp++) {
  490             if (!isascii(*cp)) {
  491                 contains_8bit = TRUE;
  492                 break;
  493             }
  494         }
  495         for (cp = utf8pass; *cp && !contains_8bit; cp++) {
  496             if (!isascii(*cp)) {
  497                 contains_8bit = TRUE;
  498                 break;
  499             }
  500         }
  501         if (contains_8bit) {
  502             for (i = 0; txt_mime_charsets[i] != NULL; i++) {
  503                 if (!strcasecmp("UTF-8", txt_mime_charsets[i])) {
  504                     c = i;
  505                     break;
  506                 }
  507             }
  508             if (c == i) { /* should never fail */
  509                 if (!buffer_to_network(utf8user, c)) {
  510                     free(utf8user);
  511                     utf8user = my_strdup(authuser);
  512                 } else {
  513                     if (!buffer_to_network(utf8pass, c)) {
  514                         free(utf8pass);
  515                         utf8pass = my_strdup(authpass);
  516                     }
  517                 }
  518             }
  519         }
  520     }
  521 #       endif /* CHARSET_CONVERSION */
  522 
  523 #       ifdef DEBUG
  524     if ((debug & DEBUG_NNTP) && verbose > 1)
  525         debug_print_file("NNTP", "do_authinfo_sasl_plain(\"%s\", \"%s\")", BlankIfNull(authuser), BlankIfNull(authpass));
  526 #       endif /* DEBUG */
  527 
  528     if ((foo = sasl_auth_plain(utf8user, utf8pass)) == NULL) {
  529         free(utf8user);
  530         free(utf8pass);
  531         return ERR_AUTHBAD;
  532     }
  533 
  534     free(utf8user);
  535     free(utf8pass);
  536 
  537     snprintf(line, sizeof(line), "AUTHINFO SASL PLAIN %s", foo);
  538     FreeIfNeeded(foo);
  539 #       ifdef DEBUG
  540     if ((debug & DEBUG_NNTP) && verbose > 1)
  541         debug_print_file("NNTP", "authorization %s", line);
  542 #       endif /* DEBUG */
  543     put_server(line);
  544     ret = get_only_respcode(line, sizeof(line));
  545     if (!batch_mode || verbose || ret != OK_AUTH)
  546         wait_message(2, (ret == OK_AUTH ? _(txt_authorization_ok) : _(txt_authorization_fail)), authuser);
  547     return ret;
  548 }
  549 
  550 
  551 static char *
  552 sasl_auth_plain(
  553     char *user,
  554     char *pass)
  555 {
  556     Gsasl *ctx = NULL;
  557     Gsasl_session *session;
  558     char *p = NULL;
  559     const char *mech = "PLAIN";
  560 
  561     if (gsasl_init(&ctx) != GSASL_OK) /* TODO: do this only once at startup */
  562         return p;
  563     if (gsasl_client_start(ctx, mech, &session) != GSASL_OK) {
  564         gsasl_done(ctx);
  565         return p;
  566     }
  567     /* GSASL_AUTHZID (authorization identity) is (usually) unused with NNTP */
  568     gsasl_property_set(session, GSASL_AUTHID, user);    /* authentication identity */
  569     gsasl_property_set(session, GSASL_PASSWORD, pass);  /* password of the authentication identity */
  570     if (gsasl_step64(session, NULL, &p) != GSASL_OK)
  571         FreeAndNull(p);
  572     gsasl_finish(session);
  573     gsasl_done(ctx);
  574     return p;
  575 }
  576 #   endif /* USE_SASL */
  577 
  578 #else
  579 static void no_authenticate(void);          /* proto-type */
  580 static void
  581 no_authenticate(                    /* ANSI C requires non-empty source file */
  582     void)
  583 {
  584 }
  585 #endif /* NNTP_ABLE */