/*
 *    auth_pass.c
 *
 * Abstract:
 *
 *    This module is the complete source for a sample "authinfo generic"
 *    program.  This program takes a user's login name and password
 *    (supplied either as arguments or as responses to prompts) and
 *    validates them against the contents of the password database.
 *
 *    If the user properly authenticates themselves, an nnrp.auth style
 *    record indicating the user's authenticated login and permitting
 *    reading and posting to all groups is output on stderr (for reading by
 *    nnrpd) and the program exits with a 0 status.  If the user fails to
 *    authenticate, then a record with the attempted login name and no
 *    access is output on stderr and a non-zero exit status is returned.
 *
 * Exit statuses:
 *    0    Successfully authenticated.
 *    1    getpeername() failed, returned a bad address family, or
 *         gethostbyaddr() failed.
 *    2    Entry not found in password file.
 *    3    No permission to read passwords, or password field is '*'.
 *    4    Bad password match.
 *    5    Cannot read username or password.
 *
 * Environment:
 *    Run by nnrpd with stdin/stdout connected to the reader and stderr
 *    connected back to nnrpd.  This program will need to be run as suid
 *    root on systems where passwords are stored in a file readable only by
 *    root.
 *
 * Written 1996 July 6 by Douglas Wade Needham (dneedham@oucsace.cs.ohiou.edu).
 *
 * Various bug fixes, code and documentation improvements since then
 * in 1997, 2002, 2003, 2007, 2011, 2014, 2021, 2024.
 */

#include "portable/system.h"

#include "portable/socket.h"
#include <errno.h>
#include <netdb.h>
#include <pwd.h>

#if HAVE_CRYPT_H
#    include <crypt.h>
#endif


int
main(int argc, char **argv)
/*+
 * Abstract:
 *    Main routine of the program, implementing all prompting, validation,
 *    and status returns.
 *
 * Arguments:
 *    argc        Argument count.
 *    argv        Null terminated argument vector.
 *
 * Returns:
 *      Exits according to program status values.
 *
 * Variables:
 *    hp        Pointer to host entry.
 *    length    General integer variable.
 *    password  Password given by user.
 *    peername  Hostname of the peer.
 *    pwd       Pointer to entry from passwd file.
 *    sin       Socket address structure.
 *    username  User's login name.
 */
{
    struct hostent *hp;
    socklen_t length;
    char password[256];
    char peername[1024];
    struct passwd *pwd;
    struct sockaddr_in sin;
    char username[32];

    /*
     * Get the user name and password if needed.
     */
    if (argc < 2) {
        fprintf(stdout, "Username: ");
        fflush(stdout);
        if (fgets(username, sizeof(username), stdin) == NULL) {
            fprintf(stderr, "cannot read username: %s", strerror(errno));
            exit(5);
        }
    } else {
        strlcpy(username, argv[1], sizeof(username));
    }
    if (argc < 3) {
        fprintf(stdout, "Password: ");
        fflush(stdout);
        if (fgets(password, sizeof(password), stdin) == NULL) {
            fprintf(stderr, "cannot read username: %s", strerror(errno));
            exit(5);
        }
    } else {
        strlcpy(password, argv[2], sizeof(password));
    }

    /*
     *  Strip CR's and NL's from the end.
     */
    length = strlen(username) - 1;
    while (username[length] == '\r' || username[length] == '\n') {
        username[length--] = '\0';
    }
    length = strlen(password) - 1;
    while (password[length] == '\r' || password[length] == '\n') {
        password[length--] = '\0';
    }

    /*
     *  Get the hostname of the peer.
     */
    length = sizeof(sin);
    if (getpeername(0, (struct sockaddr *) &sin, &length) < 0) {
        if (!isatty(0)) {
            fprintf(stderr, "cant getpeername()::%s:+:!*\n", username);
            exit(1);
        }
        strlcpy(peername, "localhost", sizeof(peername));
    } else if (sin.sin_family != AF_INET) {
        fprintf(stderr, "Bad address family %ld::%s:+:!*\n",
                (long) sin.sin_family, username);
        exit(1);
    } else if ((hp = gethostbyaddr((char *) &sin.sin_addr,
                                   sizeof(sin.sin_addr), AF_INET))
               == NULL) {
        strlcpy(peername, inet_ntoa(sin.sin_addr), sizeof(peername));
    } else {
        strlcpy(peername, hp->h_name, sizeof(peername));
    }

    /*
     *  Get the user name in the passwd file.
     */
    if ((pwd = getpwnam(username)) == NULL) {

        /*
         *  No entry in the passwd file.
         */
        fprintf(stderr, "%s::%s:+:!*\n", peername, username);
        exit(2);
    }

    /*
     *  Make sure we managed to read in the password.
     */
    if (strcmp(pwd->pw_passwd, "*") == 0) {

        /*
         *  No permission to read passwords.
         */
        fprintf(stderr, "%s::%s:+:!*\n", peername, username);
        exit(3);
    }

    /*
     *  Verify the password.
     */
    if (strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) != 0) {

        /*
         * Password was invalid.
         */
        fprintf(stderr, "%s::%s:+:!*\n", peername, username);
        exit(4);
    }

    /*
     *  We managed to authenticate the user.
     */
    fprintf(stderr, "%s:RP:%s:+:*\n", peername, username);
    exit(0);
}
