Exemple #1
0
int _pam_parse (int argc, const char **argv) {
    int ctrl = 0;
    const char *current_secret = NULL;

    /* otherwise the list will grow with each call */
    memset(tac_srv, 0, sizeof(tacplus_server_t) * TAC_PLUS_MAXSERVERS);
    tac_srv_no = 0;

    tac_service[0] = 0;
    tac_protocol[0] = 0;
    tac_prompt[0] = 0;
    tac_login[0] = 0;

    for (ctrl = 0; argc-- > 0; ++argv) {
        if (!strcmp (*argv, "debug")) { /* all */
            ctrl |= PAM_TAC_DEBUG;
        } else if (!strcmp (*argv, "use_first_pass")) {
            ctrl |= PAM_TAC_USE_FIRST_PASS;
        } else if (!strcmp (*argv, "try_first_pass")) { 
            ctrl |= PAM_TAC_TRY_FIRST_PASS;
        } else if (!strncmp (*argv, "service=", 8)) { /* author & acct */
            xstrcpy (tac_service, *argv + 8, sizeof(tac_service));
        } else if (!strncmp (*argv, "protocol=", 9)) { /* author & acct */
            xstrcpy (tac_protocol, *argv + 9, sizeof(tac_protocol));
        } else if (!strncmp (*argv, "prompt=", 7)) { /* authentication */
            xstrcpy (tac_prompt, *argv + 7, sizeof(tac_prompt));
            /* Replace _ with space */
            unsigned chr;
            for (chr = 0; chr < strlen(tac_prompt); chr++) {
                if (tac_prompt[chr] == '_') {
                    tac_prompt[chr] = ' ';
                }
            }
        } else if (!strncmp (*argv, "login="******"acct_all")) {
            ctrl |= PAM_TAC_ACCT;
        } else if (!strncmp (*argv, "server=", 7)) { /* authen & acct */
            if(tac_srv_no < TAC_PLUS_MAXSERVERS) { 
                struct addrinfo hints, *servers, *server;
                int rv;
                char *close_bracket, *server_name, *port, server_buf[256];

                memset(&hints, 0, sizeof hints);
                hints.ai_family = AF_UNSPEC;  /* use IPv4 or IPv6, whichever */
                hints.ai_socktype = SOCK_STREAM;

                if (strlen(*argv + 7) >= sizeof(server_buf)) {
                    _pam_log(LOG_ERR, "server address too long, sorry");
                    continue;
                }
                strcpy(server_buf, *argv + 7);

                if (*server_buf == '[' && (close_bracket = strchr(server_buf, ']')) != NULL) { /* Check for URI syntax */
                    server_name = server_buf + 1;
                    port = strchr(close_bracket, ':');
                    *close_bracket = '\0';
                } else { /* Fall back to traditional syntax */
                    server_name = server_buf;
                    port = strchr(server_buf, ':');
                }
                if (port != NULL) {
                    *port = '\0';
                    port++;
                }
                if ((rv = getaddrinfo(server_name, (port == NULL) ? "49" : port, &hints, &servers)) == 0) {
                    for(server = servers; server != NULL && tac_srv_no < TAC_PLUS_MAXSERVERS; server = server->ai_next) {
                        set_tac_srv_addr (tac_srv_no, server);
                        set_tac_srv_key (tac_srv_no, current_secret);
                        tac_srv_no++;
                    }
                    freeaddrinfo (servers);
                } else {
                    _pam_log (LOG_ERR,
                        "skip invalid server: %s (getaddrinfo: %s)",
                        server_name, gai_strerror(rv));
                }
            } else {
                _pam_log(LOG_ERR, "maximum number of servers (%d) exceeded, skipping",
                    TAC_PLUS_MAXSERVERS);
            }
        } else if (!strncmp (*argv, "secret=", 7)) {
            unsigned int i;

            current_secret = *argv + 7;     /* points right into argv (which is const) */

            /* if 'secret=' was given after a 'server=' parameter, fill in the current secret */
            for(i = tac_srv_no-1; i != 0; i--) {
                if (tac_srv[i].key != NULL)
                    break;

                set_tac_srv_key (i, current_secret);
            }
        } else if (!strncmp (*argv, "timeout=", 8)) {

#ifdef HAVE_STRTOL
            tac_timeout = strtol(*argv + 8, NULL, 10);

#else
            tac_timeout = atoi(*argv + 8);
#endif
            if(tac_timeout == LONG_MAX) {
                _pam_log(LOG_ERR, "timeout parameter cannot be parsed as integer: %s", *argv);
                tac_timeout = 0;
            } else {
                tac_readtimeout_enable = 1;
            }
        } else {
            _pam_log (LOG_WARNING, "unrecognized option: %s", *argv);
        }
    }

    if (ctrl & PAM_TAC_DEBUG) {
        unsigned long n;

        _pam_log(LOG_DEBUG, "%d servers defined", tac_srv_no);

        for(n = 0; n < tac_srv_no; n++) {
            _pam_log(LOG_DEBUG, "server[%u] { addr=%s, key='%s' }", n, tac_ntop(tac_srv[n].addr->ai_addr),
                     tac_srv[n].key);
        }

        _pam_log(LOG_DEBUG, "tac_service='%s'", tac_service);
        _pam_log(LOG_DEBUG, "tac_protocol='%s'", tac_protocol);
        _pam_log(LOG_DEBUG, "tac_prompt='%s'", tac_prompt);
        _pam_log(LOG_DEBUG, "tac_login='******'", tac_login);
    }

    return ctrl;
}    /* _pam_parse */
Exemple #2
0
/* authenticates user on remote TACACS+ server
 * returns PAM_SUCCESS if the supplied username and password
 * pair is valid
 */
PAM_EXTERN
int pam_sm_authenticate (pam_handle_t * pamh, int flags,
    int argc, const char **argv) {

    int ctrl, retval;
    char *user;
    char *pass;
    char *tty;
    char *r_addr;
    int srv_i;
    int tac_fd, status, msg, communicating;

    user = pass = tty = r_addr = NULL;

    ctrl = _pam_parse(argc, argv);

    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)",
            __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT);

    if ((user = _pam_get_user(pamh)) == NULL)
        return PAM_USER_UNKNOWN;

    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: user [%s] obtained", __FUNCTION__, user);

    // read config file
    _read_config(ctrl);
    
    retval = tacacs_get_password (pamh, flags, ctrl, &pass);
    if (retval != PAM_SUCCESS || pass == NULL || *pass == '\0') {
        _pam_log(LOG_ERR, "unable to obtain password");
        xfree(pass);
        return PAM_CRED_INSUFFICIENT;
    }

    retval = pam_set_item (pamh, PAM_AUTHTOK, pass);
    if (retval != PAM_SUCCESS) {
        _pam_log(LOG_ERR, "unable to set password");
        xfree(pass);
        return PAM_CRED_INSUFFICIENT;
    }

    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: password obtained", __FUNCTION__);

    tty = _pam_get_terminal(pamh);
    if (!strncmp(tty, "/dev/", 5))
        tty += 5;
    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: tty [%s] obtained", __FUNCTION__, tty);

    r_addr = _pam_get_rhost(pamh);
    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: rhost [%s] obtained", __FUNCTION__, r_addr);

    status = PAM_AUTHINFO_UNAVAIL;
    for (srv_i = 0; srv_i < tac_srv_no; srv_i++) {
        if (ctrl & PAM_TAC_DEBUG)
            syslog(LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i );

        tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, NULL, tac_timeout);
        if (tac_fd < 0) {
            _pam_log(LOG_ERR, "connection failed srv %d: %m", srv_i);
            continue;
        }
        if (tac_authen_send(tac_fd, user, pass, tty, r_addr, TAC_PLUS_AUTHEN_LOGIN) < 0) {
            close(tac_fd);
            _pam_log(LOG_ERR, "error sending auth req to TACACS+ server");
            continue;
        }
        communicating = 1;
        while (communicating) {
            struct areply re = { .attr = NULL, .msg = NULL, status = 0, flags = 0 };
            struct pam_message conv_msg = { .msg_style = 0, .msg = NULL };
            struct pam_response *resp = NULL;

            msg = tac_authen_read(tac_fd, &re);

            if (NULL != re.msg) {
                conv_msg.msg = re.msg;
            }

            /* talk the protocol */
            switch (msg) {
                case TAC_PLUS_AUTHEN_STATUS_PASS:
                    /* success */
                    if (ctrl & PAM_TAC_DEBUG)
                        syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_PASS");
  
                    if (NULL != conv_msg.msg) {
                        int retval = -1;

                        conv_msg.msg_style = PAM_TEXT_INFO;
                        retval = converse(pamh, 1, &conv_msg, &resp);
                        if (PAM_SUCCESS == retval) {
                            if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG))
                                syslog(LOG_DEBUG, "send msg=\"%s\"", conv_msg.msg);
                        }
                        else {
                            _pam_log(LOG_WARNING, "%s: error sending msg=\"%s\", retval=%d",
                                     __FUNCTION__, conv_msg.msg, retval);
                        }

                    }
                    status = PAM_SUCCESS;
                    communicating = 0;
                    if (active_server.addr != NULL)
                    {
                        xfree(active_server.addr);
                    }
                    active_server.addr = (struct addrinfo*)xcalloc(1, sizeof(struct addrinfo));
                    bcopy(tac_srv[srv_i].addr, active_server.addr, sizeof(struct addrinfo));
                    if (active_server.key != NULL)
                    {
                        xfree(active_server.key);
                    }
                    active_server.key = xstrdup(tac_srv[srv_i].key);

                    if (ctrl & PAM_TAC_DEBUG)
                        syslog(LOG_DEBUG, "%s: active srv %d", __FUNCTION__, srv_i);

                    break;

                case TAC_PLUS_AUTHEN_STATUS_FAIL:
                    if (ctrl & PAM_TAC_DEBUG)
                        syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_FAIL");

                    if (NULL != conv_msg.msg) {
                        int retval = -1;

                        conv_msg.msg_style = PAM_ERROR_MSG;
                        retval = converse(pamh, 1, &conv_msg, &resp);
                        if (PAM_SUCCESS == retval) {
                            if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG))
                                syslog(LOG_DEBUG, "send msg=\"%s\"", conv_msg.msg);
                        }
                        else {
                            _pam_log(LOG_WARNING, "%s: error sending msg=\"%s\", retval=%d",
                                     __FUNCTION__, conv_msg.msg, retval);
                        }

                    }
                    status = PAM_AUTH_ERR;
                    communicating = 0;

                    _pam_log(LOG_ERR, "auth failed: %d", msg);

                    break;

                case TAC_PLUS_AUTHEN_STATUS_GETDATA:
                    if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG))
                        syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETDATA");

                    if (NULL != conv_msg.msg) {
                        int retval = -1;
                        int echo_off = (0x1 == (re.flags & 0x1));
                        
                        conv_msg.msg_style = echo_off ? PAM_PROMPT_ECHO_OFF : PAM_PROMPT_ECHO_ON;
                        retval = converse(pamh, 1, &conv_msg, &resp);
                        if (PAM_SUCCESS == retval) {
                            if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG)) 
                                syslog(LOG_DEBUG, "sent msg=\"%s\", resp=\"%s\"",
                                       conv_msg.msg, resp->resp);

                            if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG))
                                syslog(LOG_DEBUG, "%s: calling tac_cont_send", __FUNCTION__);

                            if (0 > tac_cont_send_seq(tac_fd, resp->resp, re.seq_no + 1)) {
                                _pam_log(LOG_ERR, "error sending continue req to TACACS+ server");
                                status = PAM_AUTH_ERR;
                                communicating = 0;
                            }
                        }
                        else {
                            _pam_log(LOG_WARNING, "%s: error sending msg=\"%s\", retval=%d (%s)",
                                     __FUNCTION__, conv_msg.msg, retval, pam_strerror(pamh, retval));
                            status = PAM_AUTH_ERR;
                            communicating = 0;
                        }
                    }
                    else { 
                        syslog(LOG_ERR, "GETDATA response with no message, returning PAM_AUTH_ERR");

                        status = PAM_AUTH_ERR;
                        communicating = 0;
                    }

                    break;

                case TAC_PLUS_AUTHEN_STATUS_GETUSER:
                    /* not implemented */
                    if (ctrl & PAM_TAC_DEBUG)
                        syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETUSER");

                    communicating = 0;
                    break;

                case TAC_PLUS_AUTHEN_STATUS_GETPASS:
                    if (ctrl & PAM_TAC_DEBUG)
                        syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETPASS");

                    if (ctrl & PAM_TAC_DEBUG)
                        syslog(LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__);

                    if (tac_cont_send(tac_fd, pass) < 0) {
                        _pam_log (LOG_ERR, "error sending continue req to TACACS+ server");
                        communicating = 0;
                    }
                    /* continue the while loop; go read tac response */
                    break;

                case TAC_PLUS_AUTHEN_STATUS_RESTART:
                    /* try it again */
                    if (ctrl & PAM_TAC_DEBUG)
                        syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_RESTART");

                    /*
                     * not implemented
                     * WdJ: I *think* you can just do tac_authen_send(user, pass) again
                     *      but I'm not sure
                     */
                    communicating = 0;
                    break;

                case TAC_PLUS_AUTHEN_STATUS_ERROR:
                    /* server has problems */
                    if (ctrl & PAM_TAC_DEBUG)
                        syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_ERROR");

                    communicating = 0;
                    break;

                case TAC_PLUS_AUTHEN_STATUS_FOLLOW:
                    /* server tells to try a different server address */
                    /* not implemented */
                    if (ctrl & PAM_TAC_DEBUG)
                        syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_FOLLOW");

                    communicating = 0;
                    break;

                default:
                    if (msg < 0) {
                        /* connection error */
                        communicating = 0;
                        if (ctrl & PAM_TAC_DEBUG)
                            syslog(LOG_DEBUG, "error communicating with tacacs server");
                        break;
                    }

                    /* unknown response code */
                    communicating = 0;
                    if (ctrl & PAM_TAC_DEBUG)
                        syslog(LOG_DEBUG, "tacacs status: unknown response 0x%02x", msg);
            }

            if (NULL != resp)
            {
                if (resp->resp != NULL)
                {
                    xfree(resp->resp);
                }
                xfree(resp);
            }
                
            if (re.msg != NULL)
            {
                xfree(re.msg);
            }

        }    /* end while(communicating) */
        close(tac_fd);

        if (status == PAM_SUCCESS || status == PAM_AUTH_ERR)
            break;
    }
    if (status != PAM_SUCCESS && status != PAM_AUTH_ERR)
        _pam_log(LOG_ERR, "no more servers to connect");

    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: exit with pam status: %d", __FUNCTION__, status);

    if (NULL != pass) {
        bzero(pass, strlen (pass));
        xfree(pass);
        pass = NULL;
    }

    return status;
}    /* pam_sm_authenticate */


/* no-op function to satisfy PAM authentication module */
PAM_EXTERN
int pam_sm_setcred (pam_handle_t * pamh, int flags,
    int argc, const char **argv) {

    int ctrl = _pam_parse (argc, argv);

    if (ctrl & PAM_TAC_DEBUG)
        syslog (LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)"
            , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT);

    return PAM_SUCCESS;
}    /* pam_sm_setcred */


/* authorizes user on remote TACACS+ server, i.e. checks
 * his permission to access requested service
 * returns PAM_SUCCESS if the service is allowed
 */
PAM_EXTERN
int pam_sm_acct_mgmt (pam_handle_t * pamh, int flags,
    int argc, const char **argv) {

    int retval, ctrl, status=PAM_AUTH_ERR;
    char *user;
    char *tty;
    char *r_addr;
    struct areply arep;
    struct tac_attrib *attr = NULL;
    int tac_fd;

    user = tty = r_addr = NULL;
    memset(&arep, 0, sizeof(arep));

    /* this also obtains service name for authorization
       this should be normally performed by pam_get_item(PAM_SERVICE)
       but since PAM service names are incompatible TACACS+
       we have to pass it via command line argument until a better
       solution is found ;) */
    ctrl = _pam_parse (argc, argv);

    if (ctrl & PAM_TAC_DEBUG)
        syslog (LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)"
            , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT);

    if ((user = _pam_get_user(pamh)) == NULL)
        return PAM_USER_UNKNOWN;

    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: username obtained [%s]", __FUNCTION__, user);

    // read config file
    _read_config(ctrl);

    tty = _pam_get_terminal(pamh);
    if(!strncmp(tty, "/dev/", 5))
        tty += 5;
    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: tty obtained [%s]", __FUNCTION__, tty);

    r_addr = _pam_get_rhost(pamh);
    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: rhost obtained [%s]", __FUNCTION__, r_addr);

    /* checks if user has been successfully authenticated
       by TACACS+; we cannot solely authorize user if it hasn't
       been authenticated or has been authenticated by method other
       than TACACS+ */
    if(active_server.addr == NULL) {
        _pam_log (LOG_ERR, "user not authenticated by TACACS+");
        return PAM_AUTH_ERR;
    }
    if (ctrl & PAM_TAC_DEBUG)
        syslog (LOG_DEBUG, "%s: active server is [%s]", __FUNCTION__,
            tac_ntop(active_server.addr->ai_addr));

    /* checks for specific data required by TACACS+, which should
       be supplied in command line  */
    if(!*tac_service) {
        _pam_log (LOG_ERR, "SM: TACACS+ service type not configured");
        return PAM_AUTH_ERR;
    }
    if(!*tac_protocol) {
        _pam_log (LOG_ERR, "SM: TACACS+ protocol type not configured (IGNORED)");
    }

    tac_add_attrib(&attr, "service", tac_service);
    if(tac_protocol[0] != '\0')
        tac_add_attrib(&attr, "protocol", tac_protocol);
    
    tac_fd = tac_connect_single(active_server.addr, active_server.key, NULL, tac_timeout);
    if(tac_fd < 0) {
        _pam_log (LOG_ERR, "TACACS+ server unavailable");
        if(arep.msg != NULL)
            xfree (arep.msg);
        return PAM_AUTH_ERR;
    }

    retval = tac_author_send(tac_fd, user, tty, r_addr, attr);

    tac_free_attrib(&attr);

    if(retval < 0) {
        _pam_log (LOG_ERR, "error getting authorization");
        if(arep.msg != NULL)
            xfree (arep.msg);

        close(tac_fd);
        return PAM_AUTH_ERR;
    }

    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: sent authorization request", __FUNCTION__);

    tac_author_read(tac_fd, &arep);

    if(arep.status != AUTHOR_STATUS_PASS_ADD &&
        arep.status != AUTHOR_STATUS_PASS_REPL) {

        _pam_log (LOG_ERR, "TACACS+ authorisation failed for [%s]", user);
        if(arep.msg != NULL)
            xfree (arep.msg);

        close(tac_fd);
        return PAM_PERM_DENIED;
    }

    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: user [%s] successfully authorized", __FUNCTION__, user);

    status = PAM_SUCCESS;

    attr = arep.attr;
    while (attr != NULL)  {
        char attribute[attr->attr_len];
        char value[attr->attr_len];
        char *sep;

        sep = index(attr->attr, '=');
        if(sep == NULL)
            sep = index(attr->attr, '*');
        if(sep != NULL) {
            bcopy(attr->attr, attribute, attr->attr_len-strlen(sep));
            attribute[attr->attr_len-strlen(sep)] = '\0';
            bcopy(sep, value, strlen(sep));
            value[strlen(sep)] = '\0';

            size_t i;
            for (i = 0; attribute[i] != '\0'; i++) {
                attribute[i] = toupper(attribute[i]);
                if (attribute[i] == '-')
                    attribute[i] = '_';
            }

            if (ctrl & PAM_TAC_DEBUG)
                syslog(LOG_DEBUG, "%s: returned attribute `%s%s' from server", __FUNCTION__, attribute, value);

            /* make returned attributes available for other PAM modules via PAM environment */
            if (pam_putenv(pamh, strncat(attribute, value, strlen(value))) != PAM_SUCCESS)
                syslog(LOG_WARNING, "%s: unable to set PAM environment", __FUNCTION__);

        } else {
            syslog(LOG_WARNING, "%s: invalid attribute `%s', no separator", __FUNCTION__, attr->attr);
        }
        attr = attr->next;
    }

    /* free returned attributes */
    if(arep.attr != NULL)
        tac_free_attrib(&arep.attr);

    if(arep.msg != NULL)
        xfree (arep.msg);

    close(tac_fd);

    return status;
}    /* pam_sm_acct_mgmt */
/*
 * Talk to the server for authentication
 */
static int tac_auth_converse(int ctrl, int fd, int *sptr,
    char *pass, pam_handle_t * pamh) {
    int msg, status, flags;
    int ret = 1;
    struct areply re = { .attr = NULL, .msg = NULL, .status = 0, .flags = 0 };
    struct pam_message conv_msg = { .msg_style = 0, .msg = NULL };
    struct pam_response *resp = NULL;

    msg = tac_authen_read(fd, &re);

    if (NULL != re.msg) {
        conv_msg.msg = re.msg;
    }

    /* talk the protocol */
    switch (msg) {
        case TAC_PLUS_AUTHEN_STATUS_PASS:
            /* success */
            if (ctrl & PAM_TAC_DEBUG)
                syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_PASS");
            if (NULL != conv_msg.msg) {
                int retval = -1;

                conv_msg.msg_style = PAM_TEXT_INFO;
                retval = converse(pamh, 1, &conv_msg, &resp);
                if (PAM_SUCCESS == retval) {
                    if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG))
                        syslog(LOG_DEBUG, "send msg=\"%s\"", conv_msg.msg);
                }
                else {
                    _pam_log(LOG_WARNING, "%s: error sending msg=\"%s\", retval=%d",
                             __func__, conv_msg.msg, retval);
                }
            }
            *sptr = PAM_SUCCESS;
            ret = 0;
            break;

        case TAC_PLUS_AUTHEN_STATUS_FAIL:
            /*
             * This can be a user unknown case, so we don't want to stop
             * trying other servers when we hit this case during authentication
             */
            if (ctrl & PAM_TAC_DEBUG)
                syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_FAIL");
            if (NULL != conv_msg.msg) {
                int retval = -1;

                conv_msg.msg_style = PAM_ERROR_MSG;
                retval = converse(pamh, 1, &conv_msg, &resp);
                if (PAM_SUCCESS == retval) {
                    if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG))
                        syslog(LOG_DEBUG, "send msg=\"%s\"", conv_msg.msg);
                }
                else {
                    _pam_log(LOG_WARNING, "%s: error sending msg=\"%s\", retval=%d",
                             __func__, conv_msg.msg, retval);
                }
            }

            *sptr = PAM_AUTH_ERR;
            ret = 0;
            _pam_log(LOG_NOTICE, "auth failed %d", msg);
            break;

        case TAC_PLUS_AUTHEN_STATUS_GETDATA:
            /* not implemented */
            if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG))
                syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETDATA");

            if (NULL != conv_msg.msg) {
                int retval = -1;
                int echo_off = (0x1 == (re.flags & 0x1));

                conv_msg.msg_style = echo_off ? PAM_PROMPT_ECHO_OFF : PAM_PROMPT_ECHO_ON;
                retval = converse(pamh, 1, &conv_msg, &resp);
                if (PAM_SUCCESS == retval) {
                    if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG))
                        syslog(LOG_DEBUG, "sent msg=\"%s\", resp=\"%s\"",
                               conv_msg.msg, resp->resp);

                    if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG))
                        syslog(LOG_DEBUG, "%s: calling tac_cont_send", __func__);

                    if (0 > tac_cont_send_seq(fd, resp->resp, re.seq_no + 1)) {
                        _pam_log(LOG_ERR, "error sending continue req to TACACS+ server");
                        status = PAM_AUTH_ERR;
                    }
                }
                else {
                    _pam_log(LOG_WARNING, "%s: error sending msg=\"%s\", retval=%d (%s)",
                             __func__, conv_msg.msg, retval, pam_strerror(pamh, retval));
                    status = PAM_AUTH_ERR;
                }
            }
            else {
                syslog(LOG_ERR, "GETDATA response with no message, returning PAM_AUTH_ERR");

                status = PAM_AUTH_ERR;
            }

            ret = 0;
            break;

        case TAC_PLUS_AUTHEN_STATUS_GETUSER:
            /* not implemented */
            if (ctrl & PAM_TAC_DEBUG)
                syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETUSER");

            ret = 0;
            break;

        case TAC_PLUS_AUTHEN_STATUS_GETPASS:
            if (ctrl & PAM_TAC_DEBUG)
                syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETPASS");

            if (ctrl & PAM_TAC_DEBUG)
                syslog(LOG_DEBUG, "%s: tac_cont_send called", __func__);

            if (tac_cont_send(fd, pass) < 0) {
                _pam_log (LOG_ERR, "error sending continue req to TACACS+ server");
                ret = 0;
                break;
            }
            /* continue the while loop; go read tac response */
            break;

        case TAC_PLUS_AUTHEN_STATUS_RESTART:
            /* try it again */
            if (ctrl & PAM_TAC_DEBUG)
                syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_RESTART (not impl)");

            /*
             * not implemented
             * WdJ: I *think* you can just do tac_authen_send(user, pass) again
             *      but I'm not sure
             */
            ret = 0;
            break;

        case TAC_PLUS_AUTHEN_STATUS_ERROR:
            /* server has problems */
            if (ctrl & PAM_TAC_DEBUG)
                syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_ERROR");

            ret = 0;
            break;

        case TAC_PLUS_AUTHEN_STATUS_FOLLOW:
            /* server tells to try a different server address */
            /* not implemented */
            if (ctrl & PAM_TAC_DEBUG)
                syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_FOLLOW");

            ret = 0;
            break;

        default:
            if (msg < 0) {
                /* connection error */
                ret = 0;
                if (ctrl & PAM_TAC_DEBUG)
                    syslog(LOG_DEBUG, "error communicating with tacacs server");
                break;
            }

            /* unknown response code */
            ret = 0;
            if (ctrl & PAM_TAC_DEBUG)
                syslog(LOG_DEBUG, "tacacs status: unknown response 0x%02x", msg);
    }

    if (NULL != resp) {
       free(resp->resp);
       free(resp);
    }

    if (NULL != re.msg);
        free(re.msg);

    return ret;
}

/*
 * Only acct and auth now; should handle all the cases here
 * Talk to the tacacs server for each type of transaction conversation
 */
static void talk_tac_server(int ctrl, int fd, char *user, char *pass,
                            char *tty, char *r_addr, struct tac_attrib **attr,
                            int *sptr, struct areply *reply,
                            pam_handle_t * pamh) {
    if(!pass && attr) {  /* acct, much simpler */
        int retval;
        struct areply arep;
        retval = tac_author_send(fd, user, tty, r_addr, *attr);
        if(retval < 0) {
            _pam_log (LOG_ERR, "error getting authorization");

            *sptr =  PAM_AUTH_ERR;
            return;
        }

        if (ctrl & PAM_TAC_DEBUG)
            syslog(LOG_DEBUG, "%s: sent authorization request for [%s]",
                __func__, user);

        arep.msg = NULL;
        tac_author_read(fd, &arep);
        if (reply)
            *reply = arep;

        if(arep.status != AUTHOR_STATUS_PASS_ADD &&
            arep.status != AUTHOR_STATUS_PASS_REPL) {
            /*
             * this is debug because we can get called for any user for
             * commands like sudo, not just tacacs users
             */
            *sptr = PAM_PERM_DENIED;
            _pam_log (LOG_ERR, "TACACS+ authorization failed for [%s] (status=%d)",
                user, arep.status);
            if(arep.msg != NULL && !reply)
                free (arep.msg); /*  if reply is set, caller will free */
        }
        else  {
            *sptr = PAM_SUCCESS;
        }
    }
    else if (pass)  { /* auth */
        if (tac_authen_send(fd, user, pass, tty, r_addr, TAC_PLUS_AUTHEN_LOGIN) < 0) {
            _pam_log(LOG_ERR, "error sending auth req to TACACS+ server");
        }
        else {
            while ( tac_auth_converse(ctrl, fd, sptr, pass, pamh))
                    ;
        }
    }
}


/*
 * find a responding tacacs server, and converse with it.
 * See comments at do_tac_connect() below
 */
static void find_tac_server(int ctrl, int *tacfd, char *user, char *pass,
                           char *tty, char *r_addr, struct tac_attrib **attr,
                           int *sptr, struct areply *reply, pam_handle_t * pamh) {
    int fd = -1, srv_i;

    for (srv_i = 0; srv_i < tac_srv_no; srv_i++) {
        if (ctrl & PAM_TAC_DEBUG)
            syslog(LOG_DEBUG, "%s: trying srv[%d] %s", __func__, srv_i,
                tac_srv[srv_i].addr ?
                tac_ntop(tac_srv[srv_i].addr->ai_addr) : "not set");

        fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, NULL,
            __vrfname);
        if (fd < 0) {
            _pam_log(LOG_ERR, "connection to srv[%d] %s failed: %m", srv_i,
                tac_srv[srv_i].addr ?
                tac_ntop(tac_srv[srv_i].addr->ai_addr) : "not set");
            active_server.addr = NULL; /*  in case last in list */
            continue;
        }

        talk_tac_server(ctrl, fd, user, pass, tty, r_addr, attr, sptr,
            reply, pamh);

        if (*sptr == PAM_SUCCESS || *sptr == PAM_AUTH_ERR ||
            *sptr == PAM_PERM_DENIED) {
            if (ctrl & PAM_TAC_DEBUG)
                syslog(LOG_DEBUG, "%s: srv[%d] %s, pam_status=%d", __func__,
                   srv_i, tac_ntop(tac_srv[srv_i].addr->ai_addr), *sptr);
            if (*sptr == PAM_SUCCESS) {
                if (active_server.addr == NULL) {
                    active_server.addr = tac_srv[srv_i].addr;
                    active_server.key = tac_srv[srv_i].key;
                }
                break;
            }
            /*  else try other servers, if any. On errs, won't need fd */
        }
        else /*  in case end of list */
            active_server.addr = NULL;

        close(fd);
        fd = -1;
    }
    *tacfd = fd;
}

/*
 * We have to make a new connection each time, because libtac is single
 * threaded (doesn't support multiple connects at the same time due to
 * use of globals), and doesn't have support for persistent connections.
 * That's fixable, but not worth the effort at this point.
 *
 * Trying to make this common code is ugly, but worth it to simplify
 * maintenance and debugging.
 *
 * The problem is that the definition allows for multiple tacacs
 * servers to be consulted, but a lot of the code was written such
 * that once a server is found that responds, it keeps using it.
 * That means when we are finding a server we need to do the full sequence.
 * The related issue is that the lower level code can't communicate
 * with multiple servers at the same time, and can't keep a connection
 * open.
 *
 * TODO: Really should have a structure to pass user, pass, tty, and r_addr
 * around everywhere.
 */
static int do_tac_connect(int ctrl, int *tacfd, char *user, char *pass,
                          char *tty, char *r_addr, struct tac_attrib **attr,
                          struct areply *reply, pam_handle_t * pamh) {
    int status = PAM_AUTHINFO_UNAVAIL, fd;

    if (active_server.addr == NULL) { /* find a server with the info we want */
        find_tac_server(ctrl, &fd, user, pass, tty, r_addr, attr, &status,
            reply, pamh);
    }
    else { /* connect to the already chosen server, so we get
            * consistent results.  */
        if (ctrl & PAM_TAC_DEBUG)
            syslog(LOG_DEBUG, "%s: use previous server %s", __func__,
               tac_ntop(active_server.addr->ai_addr));

        fd = tac_connect_single(active_server.addr, active_server.key, NULL,
            __vrfname);
        if (fd < 0)
            _pam_log(LOG_ERR, "reconnect failed: %m");
        else
            talk_tac_server(ctrl, fd, user, pass, tty, r_addr, attr, &status,
                reply, pamh);
    }

    /*
     * this is debug because we can get called for any user for
     * commands like sudo, not just tacacs users, so it's not an
     * error to fail here.  The caller can handle the logging.
     */
    if ((ctrl & PAM_TAC_DEBUG) && status != PAM_SUCCESS &&
        status != PAM_AUTH_ERR)
        _pam_log(LOG_ERR, "no more servers to connect");
    if (tacfd)
        *tacfd = fd; /* auth caller needs fd */
    else if (fd != -1)
        close(fd); /* acct caller doesn't need connection */
    return status;
}
/*
 * Send an accounting record to the TACACS+ server.
 * We send the start/stop accounting records even if the user is not known
 * to the TACACS+ server.   This seems non-intuitive, but it's the way
 * this code is written to work.
 */
int _pam_account(pam_handle_t *pamh, int argc, const char **argv,
    int type, char *cmd) {

    int retval;
    int ctrl;
    char *user = NULL;
    char *tty = NULL;
    char *r_addr = NULL;
    char *typemsg;
    int status = PAM_SESSION_ERR;
    int srv_i, tac_fd;

    typemsg = tac_acct_flag2str(type);
    ctrl = _pam_parse (argc, argv);

    if (ctrl & PAM_TAC_DEBUG)
        syslog (LOG_DEBUG, "%s: [%s] called (pam_tacplus v%u.%u.%u)",
            __func__, typemsg, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT);

    _pam_get_user(pamh, &user);
    if (user == NULL)
        return PAM_USER_UNKNOWN;

    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: username [%s] obtained", __func__, user);

    if (!task_id)
#if defined(HAVE_OPENSSL_RAND_H) && defined(HAVE_LIBCRYPTO)
        RAND_pseudo_bytes((unsigned char *) &task_id, sizeof(task_id));
#else
        task_id = (short unsigned int) tac_magic();
#endif

    _pam_get_terminal(pamh, &tty);
    if(!strncmp(tty, "/dev/", 5))
        tty += 5;
    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: tty [%s] obtained", __func__, tty);

    _pam_get_rhost(pamh, &r_addr);
    if (ctrl & PAM_TAC_DEBUG)
        syslog(LOG_DEBUG, "%s: rhost [%s] obtained", __func__, r_addr);

    /* checks for specific data required by TACACS+, which should
       be supplied in command line  */
    if(tac_protocol[0] == '\0') {
        _pam_log (LOG_ERR, "ACC: TACACS+ protocol type not configured");
        return PAM_AUTH_ERR;
    }

    /* when this module is called from within pppd or other
       application dealing with serial lines, it is likely
       that we will get hit with signal caused by modem hangup;
       this is important only for STOP packets, it's relatively
       rare that modem hangs up on accounting start */
    if(type == TAC_PLUS_ACCT_FLAG_STOP) {
        signal(SIGALRM, SIG_IGN);
        signal(SIGCHLD, SIG_IGN);
        signal(SIGHUP, SIG_IGN);
    }

    /*
     * If PAM_SESSION_ERR is used, then the pam config can't
     * ignore server failures, so use PAM_AUTHINFO_UNAVAIL.
     *
     * We have to make a new connection each time, because libtac is single
     * threaded (doesn't support multiple connects at the same time due to
     * use of globals)), and doesn't have support for persistent connections.
     * That's fixable, but not worth the effort at this point.
     *
     * TODO: this should be converted to use do_tac_connect eventually.
     */
    status = PAM_AUTHINFO_UNAVAIL;
    for(srv_i = 0; srv_i < tac_srv_no; srv_i++) {
        tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key,
            NULL, __vrfname);
        if (tac_fd < 0) {
            _pam_log(LOG_WARNING, "%s: error sending %s (fd)", __func__,
                typemsg);
            continue;
        }
        if (ctrl & PAM_TAC_DEBUG)
            syslog(LOG_DEBUG, "%s: connected with fd=%d to srv[%d] %s", __func__,
                tac_fd, srv_i, tac_srv[srv_i].addr ?
                tac_ntop(tac_srv[srv_i].addr->ai_addr) : "not set");

        retval = _pam_send_account(tac_fd, type, user, tty, r_addr, cmd);
        if (retval < 0) {
            _pam_log(LOG_WARNING, "%s: error sending %s (acct)",
                __func__, typemsg);
        } else {
            status = PAM_SUCCESS;
            if (ctrl & PAM_TAC_DEBUG)
                syslog(LOG_DEBUG, "%s: [%s] for [%s] sent", __func__, typemsg, user);
        }

        if ((status == PAM_SUCCESS) && !(ctrl & PAM_TAC_ACCT)) {
            /* do not send acct start/stop packets to _all_ servers */
            break;
        }
    }

    if (type == TAC_PLUS_ACCT_FLAG_STOP) {
        signal(SIGALRM, SIG_DFL);
        signal(SIGCHLD, SIG_DFL);
        signal(SIGHUP, SIG_DFL);
    }
    return status;
}
Exemple #5
0
/* authorizes user on remote TACACS+ server, i.e. checks
 * his permission to access requested service
 * returns PAM_SUCCESS if the service is allowed
 */
PAM_EXTERN 
int pam_sm_acct_mgmt (pam_handle_t * pamh, int flags,
    int argc, const char **argv) {

    int retval, ctrl, status=PAM_AUTH_ERR;
    char *user;
    char *tty;
    char *r_addr;
    struct areply arep;
    struct tac_attrib *attr = NULL;
    int tac_fd;

    user = tty = r_addr = NULL;
  
    /* this also obtains service name for authorization
       this should be normally performed by pam_get_item(PAM_SERVICE)
       but since PAM service names are incompatible TACACS+
       we have to pass it via command line argument until a better
       solution is found ;) */
    ctrl = _pam_parse (argc, argv);

    if (ctrl & PAM_TAC_DEBUG)
        _pam_log (LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)"
            , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT);
  
    if ((user = _pam_get_user(pamh)) == NULL)
        return PAM_USER_UNKNOWN;

    if (ctrl & PAM_TAC_DEBUG)
        _pam_log(LOG_DEBUG, "%s: username obtained [%s]", __FUNCTION__, user);
  
    tty = _pam_get_terminal(pamh);
    if(!strncmp(tty, "/dev/", 5)) 
        tty += 5;
    if (ctrl & PAM_TAC_DEBUG)
        _pam_log(LOG_DEBUG, "%s: tty obtained [%s]", __FUNCTION__, tty);

    r_addr = _pam_get_rhost(pamh);
    if (ctrl & PAM_TAC_DEBUG)
        _pam_log(LOG_DEBUG, "%s: rhost obtained [%s]", __FUNCTION__, r_addr);
  
    /* checks if user has been successfully authenticated
       by TACACS+; we cannot solely authorize user if it hasn't
       been authenticated or has been authenticated by method other
       than TACACS+ */
    if(!active_server) {
        _pam_log (LOG_ERR, "user not authenticated by TACACS+");
        return PAM_AUTH_ERR;
    }
    if (ctrl & PAM_TAC_DEBUG)
        _pam_log (LOG_DEBUG, "%s: active server is [%s]", __FUNCTION__,
            tac_ntop(active_server->ai_addr, active_server->ai_addrlen));

    /* checks for specific data required by TACACS+, which should
       be supplied in command line  */
    if(tac_service == NULL || *tac_service == '\0') {
        _pam_log (LOG_ERR, "TACACS+ service type not configured");
        return PAM_AUTH_ERR;
    }
    if(tac_protocol == NULL || *tac_protocol == '\0') {
        _pam_log (LOG_ERR, "TACACS+ protocol type not configured");
        return PAM_AUTH_ERR;
    }

    tac_add_attrib(&attr, "service", tac_service);
    tac_add_attrib(&attr, "protocol", tac_protocol);

    tac_fd = tac_connect_single(active_server, active_key);
    if(tac_fd < 0) {
        _pam_log (LOG_ERR, "TACACS+ server unavailable");
        if(arep.msg != NULL) free (arep.msg);
        close(tac_fd);
        return PAM_AUTH_ERR;
    }

    retval = tac_author_send(tac_fd, user, tty, r_addr, attr);

    tac_free_attrib(&attr);
  
    if(retval < 0) {
        _pam_log (LOG_ERR, "error getting authorization");
        if(arep.msg != NULL) free (arep.msg);
        close(tac_fd);
        return PAM_AUTH_ERR;
    }

    if (ctrl & PAM_TAC_DEBUG)
        _pam_log(LOG_DEBUG, "%s: sent authorization request", __FUNCTION__);
  
    tac_author_read(tac_fd, &arep);

    if(arep.status != AUTHOR_STATUS_PASS_ADD &&
        arep.status != AUTHOR_STATUS_PASS_REPL) {

        _pam_log (LOG_ERR, "TACACS+ authorisation failed for [%s]", user);
        if(arep.msg != NULL) free (arep.msg);
        close(tac_fd);
        return PAM_PERM_DENIED;
    }

    if (ctrl & PAM_TAC_DEBUG)
        _pam_log(LOG_DEBUG, "%s: user [%s] successfully authorized", __FUNCTION__, user);
  
    status = PAM_SUCCESS;
  
    attr = arep.attr;
    while (attr != NULL)  {
        char attribute[attr->attr_len];
        char value[attr->attr_len];
        char *sep;

        sep = index(attr->attr, '=');
        if(sep == NULL)
            sep = index(attr->attr, '*');
        if(sep != NULL) {
            bcopy(attr->attr, attribute, attr->attr_len-strlen(sep));
            attribute[attr->attr_len-strlen(sep)] = '\0';
            bcopy(sep, value, strlen(sep));
            value[strlen(sep)] = '\0';

            size_t i;
            for (i = 0; attribute[i] != '\0'; i++) {
                attribute[i] = toupper(attribute[i]);
                if (attribute[i] == '-')
                    attribute[i] = '_';
            }

            if (ctrl & PAM_TAC_DEBUG)
                _pam_log(LOG_DEBUG, "%s: returned attribute `%s%s' from server", __FUNCTION__, attribute, value);

            /* make returned attributes available for other PAM modules via PAM environment */
            if (pam_putenv(pamh, strncat(attribute, value, strlen(value))) != PAM_SUCCESS)
                _pam_log(LOG_WARNING, "%s: unable to set PAM environment", __FUNCTION__);

        } else {
            _pam_log(LOG_WARNING, "%s: invalid attribute `%s', no separator", __FUNCTION__, attr->attr);
        }
        attr = attr->next;
    }

    /* free returned attributes */
    if(arep.attr != NULL) tac_free_attrib(&arep.attr);
    if(arep.msg != NULL) free (arep.msg);
    close(tac_fd);

    return status;
}    /* pam_sm_acct_mgmt */
Exemple #6
0
/* Returns file descriptor of open connection
   to the first available server from list passed
   in server table.
*/
int tac_connect(struct addrinfo **server, char **key, int servers) {
	int tries = 0;
	int fd, flags, retval;
	fd_set readfds, writefds;
	struct timeval tv;
	socklen_t len;
	struct sockaddr_storage addr;

	if(!servers) {
		DEBUG_TACPLUS("no TACACS+ servers defined");
		return(-1);
	}

	while(tries < servers) {	
		if((fd=socket(server[tries]->ai_family, server[tries]->ai_socktype, server[tries]->ai_protocol)) == -1) {
       	   		DEBUG_TACPLUS("ocket creation error");
			tries++;
			continue;
		}

		/* put socket in non blocking mode for timeout support */
		flags = fcntl(fd, F_GETFL, 0);
		if(fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
     	  		DEBUG_TACPLUS("cannot set socket non blocking"); 
			tries++;
			continue;
		}

		retval = connect(fd, server[tries]->ai_addr, server[tries]->ai_addrlen);
		if((retval == -1) && (errno != EINPROGRESS)) {
     	  		DEBUG_TACPLUS("connection to %s failed", tac_ntop(server[tries]->ai_addr, server[tries]->ai_addrlen));
			if(fcntl(fd, F_SETFL, flags)) {
     	  			DEBUG_TACPLUS("cannot restore socket flags"); 
			}
			tries++;
			continue;
    		}

		/* set fds for select */
		FD_ZERO(&readfds);
		FD_SET(fd, &readfds);
		writefds = readfds;

		/* set timeout seconds */
		tv.tv_sec = tac_timeout;
		tv.tv_usec = 0;

		/* check if socket is ready for read and write */
		if(select(fd+1, &readfds, &writefds, NULL, &tv) < 1) {
     	  		DEBUG_TACPLUS("connection failed with %s", tac_ntop(server[tries]->ai_addr, server[tries]->ai_addrlen));
			if(fcntl(fd, F_SETFL, flags)) {
     	  			DEBUG_TACPLUS("cannot restore socket flags"); 
			}
			tries++;
			continue;
		} else {
			/* check with getpeername if we have a valid connection */
			len = sizeof(addr);
			if(getpeername(fd, (struct sockaddr*)&addr, &len) == -1) {
     	  			DEBUG_TACPLUS("connection failed with %s", tac_ntop(server[tries]->ai_addr, server[tries]->ai_addrlen));
				if(fcntl(fd, F_SETFL, flags)) {
     	  				DEBUG_TACPLUS("cannot restore socket flags"); 
				}
				tries++;
				continue;
			}
		}

		/* connected ok */
		if(fcntl(fd, F_SETFL, flags)) {
     	  		DEBUG_TACPLUS("cannot restore socket flags"); 
		}
		DEBUG_TACPLUS("connected to %s", tac_ntop(server[tries]->ai_addr, server[tries]->ai_addrlen));

		/* set current tac_secret */
		tac_secret = key[tries];
		return(fd);
	}

	/* all attempts failed */
	return(-1);
} /* tac_connect */
Exemple #7
0
int _pam_parse (int argc, const char **argv) {
    int ctrl = 0;
    const char *current_secret = NULL;

    /* otherwise the list will grow with each call */
    memset(tac_srv, 0, sizeof(tacplus_server_t) * TAC_PLUS_MAXSERVERS);
    tac_srv_no = 0;

    tac_service[0] = 0;
    tac_protocol[0] = 0;
    tac_prompt[0] = 0;
    tac_login[0] = 0;

    for (ctrl = 0; argc-- > 0; ++argv) {
        if (!strcmp (*argv, "debug")) { /* all */
            ctrl |= PAM_TAC_DEBUG;
        } else if (!strcmp (*argv, "use_first_pass")) {
            ctrl |= PAM_TAC_USE_FIRST_PASS;
        } else if (!strcmp (*argv, "try_first_pass")) { 
            ctrl |= PAM_TAC_TRY_FIRST_PASS;
        } else if (!strncmp (*argv, "service=", 8)) { /* author & acct */
            xstrcpy (tac_service, *argv + 8, sizeof(tac_service));
        } else if (!strncmp (*argv, "protocol=", 9)) { /* author & acct */
            xstrcpy (tac_protocol, *argv + 9, sizeof(tac_protocol));
        } else if (!strncmp (*argv, "prompt=", 7)) { /* authentication */
            xstrcpy (tac_prompt, *argv + 7, sizeof(tac_prompt));
            /* Replace _ with space */
            int chr;
            for (chr = 0; chr < strlen(tac_prompt); chr++) {
                if (tac_prompt[chr] == '_') {
                    tac_prompt[chr] = ' ';
                }
            }
        } else if (!strncmp (*argv, "login="******"acct_all")) {
            ctrl |= PAM_TAC_ACCT;
        } else if (!strncmp (*argv, "server=", 7)) { /* authen & acct */
            if(tac_srv_no < TAC_PLUS_MAXSERVERS) { 
                struct addrinfo hints, *servers, *server;
                int rv;
                char *port, server_buf[256];

                memset(&hints, 0, sizeof hints);
                hints.ai_family = AF_UNSPEC;  /* use IPv4 or IPv6, whichever */
                hints.ai_socktype = SOCK_STREAM;

                if (strlen(*argv + 7) >= sizeof(server_buf)) {
                    _pam_log(LOG_ERR, "server address too long, sorry");
                    continue;
                }
                strcpy(server_buf, *argv + 7);

                port = strchr(server_buf, ':');
                if (port != NULL) {
                    *port = '\0';
					port++;
                }
                if ((rv = getaddrinfo(server_buf, (port == NULL) ? "49" : port, &hints, &servers)) == 0) {
                    for(server = servers; server != NULL && tac_srv_no < TAC_PLUS_MAXSERVERS; server = server->ai_next) {
                        tac_srv[tac_srv_no].addr = server;
                        tac_srv[tac_srv_no].key = current_secret;
                        tac_srv_no++;
                    }
                } else {
                    _pam_log (LOG_ERR,
                        "skip invalid server: %s (getaddrinfo: %s)",
                        server_buf, gai_strerror(rv));
                }
            } else {
                _pam_log(LOG_ERR, "maximum number of servers (%d) exceeded, skipping",
                    TAC_PLUS_MAXSERVERS);
            }
        } else if (!strncmp (*argv, "secret=", 7)) {
            int i;

            current_secret = *argv + 7;     /* points right into argv (which is const) */

            /* if 'secret=' was given after a 'server=' parameter, fill in the current secret */
            for(i = tac_srv_no-1; i >= 0; i--) {
                if (tac_srv[i].key != NULL)
                    break;

                tac_srv[i].key = current_secret;
            }
        } else if (!strncmp (*argv, "timeout=", 8)) {
            /* FIXME atoi() doesn't handle invalid numeric strings well */
            tac_timeout = atoi(*argv + 8);

            if (tac_timeout < 0)
                tac_timeout = 0;
        } else {
            _pam_log (LOG_WARNING, "unrecognized option: %s", *argv);
        }
    }

    if (ctrl & PAM_TAC_DEBUG) {
        int n;

        _pam_log(LOG_DEBUG, "%d servers defined", tac_srv_no);

        for(n = 0; n < tac_srv_no; n++) {
            _pam_log(LOG_DEBUG, "server[%d] { addr=%s, key='%s' }", n, tac_ntop(tac_srv[n].addr->ai_addr), tac_srv[n].key);
        }

        _pam_log(LOG_DEBUG, "tac_service='%s'", tac_service);
        _pam_log(LOG_DEBUG, "tac_protocol='%s'", tac_protocol);
        _pam_log(LOG_DEBUG, "tac_prompt='%s'", tac_prompt);
        _pam_log(LOG_DEBUG, "tac_login='******'", tac_login);
    }

    return ctrl;
}    /* _pam_parse */