コード例 #1
0
ファイル: pam_tacplus.c プロジェクト: Plexxi/pam_tacplus
/* 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 */
コード例 #2
0
ファイル: pam_tacplus.c プロジェクト: daveolson53/pam_tacplus
/*
 * 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;
}
コード例 #3
0
ファイル: tacacs.c プロジェクト: Bytewerk/uClinux-ipcam
static int
tacacs_auth(char *t_user, char *t_passwd, char**t_msgp,
			struct wordlist **t_paddrs, struct wordlist **t_popts)
{
    int  tac_fd;
    char *msg;
    struct areply   arep;
    struct tac_attrib *attr;
    struct tac_attrib *attrentry;
    struct wordlist **pnextaddr;
    struct wordlist *addr;
    int addrlen;
    int ret;

    if (prev_pap_auth_hook) {
	ret = prev_pap_auth_hook(t_user, t_passwd, t_msgp, t_paddrs, t_popts);
	if (ret >= 0) {
	    return ret;
	}
    }
    
    if (!use_tacacs) return -1;

    *t_msgp = "TACACS+ server failed";
    *t_popts = NULL;

    /* start authentication */

    if (tac_server == -1)
	return 0;
    
    tac_fd = tac_connect(&tac_server, 1);
    if (tac_fd < 0)
	return 0;

    if (tac_authen_pap_send(tac_fd, t_user, t_passwd, tty) < 0)
	return 0;

    msg = tac_authen_pap_read(tac_fd);
    if (msg != NULL) {
	*t_msgp = msg;
	return 0;
    }

    close(tac_fd);

    /* user/password is valid, now check authorization */
    if (use_authorize) {
	tac_fd = tac_connect(&tac_server, 1);
    	if (tac_fd < 0)
	    return 0;

	attr = NULL;
	tac_add_attrib(&attr, "service", "ppp");
	tac_add_attrib(&attr, "protocol", "ip");

	if (tac_author_send(tac_fd, t_user, tty, attr) < 0)
	    return 0;

	tac_author_read(tac_fd, &arep);
	if (arep.status != AUTHOR_STATUS_PASS_ADD
	        && arep.status != AUTHOR_STATUS_PASS_REPL) {
	    *t_msgp = arep.msg;
    	    return 0;
	}

	tac_free_attrib(&attr);
	close(tac_fd);

	/* Build up list of allowable addresses */
	*t_paddrs = NULL; /* Default to allow all */
	pnextaddr = t_paddrs;
	for (attrentry=arep.attr; attrentry!=NULL; attrentry=attrentry->next) {
	    if (strncmp(attrentry->attr, "addr=", 5) == 0) {
		addrlen = attrentry->attr_len - 5;

		/* Allocate a buffer for both the structure and the address */
		addr = (struct wordlist*)malloc(sizeof(struct wordlist)
						+ addrlen + 1);
		if (addr == NULL)
		    novm("TACACS+ address");

		addr->word = (char*)(addr+1);
		strncpy(addr->word, attrentry->attr+5, addrlen);
		addr->word[addrlen] = '\0';

		addr->next = NULL;
		*pnextaddr = addr;
		pnextaddr = &addr->next;
	    }
	}

	tac_free_attrib(&arep.attr);
    }
    
    *t_msgp = "Login succeeded";
    syslog(LOG_INFO,"TACACS+ login succeeded for %s", t_user);

    authorized = 1;

    return 1;
}
コード例 #4
0
ファイル: pam_tacplus.c プロジェクト: WEMS/pam_tacplus
/* 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 */
コード例 #5
0
ファイル: nss_tacplus.c プロジェクト: friofry/nss_tacplus
/**
 * ... document me ...
 */
enum nss_status _nss_tacplus_getpwnam_r(const char *name, struct passwd *pw,
                                        char *buffer, size_t buflen,
                                        int *errnop)
{
    enum nss_status status = NSS_STATUS_NOTFOUND;
    int tac_fd = -1;
    struct addrinfo *server = NULL;
    time_t now = -1;
    uint32_t cycle = 0;

    (void)pthread_once(&G_tacplus_initialized, &_initalize_tacplus);
    now = time(NULL);

    // check to see if we should re-read our configuration
    // once per 32 second cycle
    cycle = (now - G_tacplus_started) >> 5;
    if (   NSS_STATUS_SUCCESS != G_tacplus_conf.status
        || cycle > G_tacplus_cycles)
    {
        G_tacplus_cycles = cycle;
        _check_config(cycle);
    }
    else
    {
        G_tacplus_cycles = cycle;
    }

    if (NSS_STATUS_SUCCESS != G_tacplus_conf.status)
    {
        status = G_tacplus_conf.status;
        *errnop = G_tacplus_conf.errnum;

        assert(NULL == G_tacplus_conf.servers);
    }

    // Iterate through our servers linked list, stop when we get an answer
    // that isn't NOTFOUND. Since we're twisting TACACS+ authorization
    // functionality to provide this facility, we treat AUTHOR_STATUS_FAIL,
    // AUTHOR_STATUS_ERROR, AUTHOR_STATUS_FAIL as "soft" fails, and just
    // move on to the next server. Doing otherwise would result in potentially
    // unexpected behaviors if the user is provisioned on server->ai_next, but
    // not on server.
    for (server = G_tacplus_conf.servers;
         NSS_STATUS_NOTFOUND == status && NULL != server;
         server = server->ai_next)
    {
        void *sin_addr = NULL;

        // the first member of sockaddr_in and sockaddr_in6 are the same, so
        // this should always work.
        uint16_t port = ntohs(((struct sockaddr_in *)server->ai_addr)->sin_port);

        // this is ugly, but we need to differentiate IPv6 vs. IPv4 addresses
        // (in practice, this may not be necessary, as I'm not certain if the
        // remainder of this code is capable of handling IPv6, yet.)
        sin_addr = (  AF_INET6 == server->ai_family
                    ? (void*)&((struct sockaddr_in6 *)server->ai_addr)->sin6_addr
                    : (void*)&((struct sockaddr_in *)server->ai_addr)->sin_addr);
        inet_ntop(server->ai_family, sin_addr, buffer, buflen);

        syslog(LOG_INFO, "%s: begin lookup: user=`%s', server=`%s:%d'",
               __FILE__, name, buffer, port);

        // connect to the current server
        errno = 0;
        tac_fd = tac_connect_single(server, G_tacplus_conf.secret, NULL, 15);

        if (0 > tac_fd)
        {
            char errtext[256];
            int errnum = errno;

            strerror_r(errnum, errtext, sizeof(errtext));
            syslog(LOG_WARNING,
                   "%s: Connection to TACACS+ server failed: server=`%s:%d', "
                   "errno=%d, errtext=`%s'",
                   __FILE__, buffer, port, errnum, errtext);

             // upon failure, simply move on to the next server in the list
            continue;
        }
        else
        {
            int rv = -1;
            struct tac_attrib *attr = NULL;

            tac_add_attrib(&attr, "service", G_tacplus_conf.service);
            tac_add_attrib(&attr, "protocol", G_tacplus_conf.protocol);

            rv = tac_author_send(tac_fd, name, G_tacplus_conf.protocol,
                                 "unknown", attr);

            tac_free_attrib(&attr);

            if (0 > rv)
            {
                status = NSS_STATUS_TRYAGAIN;
            }
            else
            {
                struct areply reply;

                memset(&reply, '\0', sizeof(reply));
                tac_author_read(tac_fd, &reply);

                if (   (AUTHOR_STATUS_PASS_ADD == reply.status)
                    || (AUTHOR_STATUS_PASS_REPL == reply.status))
                {
                    syslog(LOG_INFO,
                           "%s: found match: user=`%s', server=`%s:%d', "
                           "status=%d, attributes? %s",
                           __FILE__, name, buffer, port, reply.status,
                           (NULL == reply.attr ? "no" : "yes"));
                    status = _passwd_from_reply(&reply, name, pw, buffer,
                                                buflen, errnop);
                }
                else
                {
                    syslog(LOG_INFO,
                           "%s: lookup failed: user=`%s', server=`%s:%d', "
                           "status=%d, msg=%s", __FILE__, name, buffer, port,
                           reply.status, reply.msg);
                }
                if (NULL != reply.attr)
                {
                    tac_free_attrib(&reply.attr);
                }
                free(reply.msg);
            }
        }

        // XXX: there is a potential efficiency to be gained by not
        //      reopening our sockets all the time, but I'm not
        //      convinced it's worth it, especially since we require
        //      nscd to operate, anyhow.
        close(tac_fd);
    }

    return status;
}
コード例 #6
0
ファイル: tacc.c プロジェクト: jeroennijhof/pam_tacplus
int main(int argc, char **argv) {
    char *pass = NULL;
    char *tty = NULL;
    char *command = NULL;
    char *remote_addr = NULL;
    char *service = NULL;
    char *protocol = NULL;
    struct addrinfo *tac_server;
    char *tac_server_name = NULL;
    char *tac_secret = NULL;
    int tac_fd;
    short int task_id = 0;
    char buf[40];
    int ret;
#ifndef USE_SYSTEM
    pid_t pid;
#endif
    struct areply arep;

    /* options */
    flag log_wtmp = 1;
    flag do_author = 0;
    flag do_authen = 0;
    flag do_account = 0;
    flag login_mode = 0;

    /* check argc */
    if (argc < 2) {
        showusage(argv[0]);
        exit(EXIT_ERR);
    }

    /* check for login mode */
    if (argc == 2 && isalpha(*argv[1])) {
        g_user = argv[1];
        do_author = do_authen = do_account = 1;
        command = DEFAULT_COMMAND;
        login_mode = 1;
    } else {
        int c;
        int opt_index;

        while ((c = getopt_long(argc, argv, opt_string, long_options,
                                &opt_index)) != EOF) {
            switch (c) {
                case 'T':
                    do_authen = 1;
                    break;
                case 'R':
                    do_author = 1;
                    break;
                case 'A':
                    do_account = 1;
                    break;
                case 'V':
                    showversion(argv[0]);
                    /*NOTREACHED*/
                    break;
                case 'h':
                    showusage(argv[0]);
                    /*NOTREACHED*/
                    break;
                case 'u':
                    g_user = optarg;
                    break;
                case 'r':
                    remote_addr = optarg;
                    break;
                case 'L':
                    // tac_login is a global variable initialized in libtac
                    xstrcpy(tac_login, optarg, sizeof(tac_login));
                    break;
                case 'p':
                    pass = optarg;
                    break;
                case 's':
                    tac_server_name = optarg;
                    break;
                case 'k':
                    tac_secret = optarg;
                    break;
                case 'c':
                    command = optarg;
                    break;
                case 'S':
                    service = optarg;
                    break;
                case 'P':
                    protocol = optarg;
                    break;
                case 'q':
                    quiet = 1;
                    break;
                case 'w':
                    log_wtmp = 0;
                    break;
                case 'n':
                    tac_encryption = 0;
                    break;
                case 'y':
                    tty = optarg;
                    break;
            }
        }
    }

    /* check available information and set to defaults if needed */
    if (do_authen + do_author + do_account == 0) {
        printf("error: one of -TRAVh options is required\n");
        exit(EXIT_ERR);
    }

    if (g_user == NULL) {
        printf("error: username is required.\n");
        exit(EXIT_ERR);
    }

    if (remote_addr == NULL) {
        printf("error: remote address is required.\n");
        exit(EXIT_ERR);
    }

    if (service == NULL) {
        printf("error: service is required.\n");
        exit(EXIT_ERR);
    }

    if (protocol == NULL) {
        printf("error: protocol is required.\n");
        exit(EXIT_ERR);
    }

    if (tac_server_name == NULL) {
        printf("error: server name is required.\n");
        exit(EXIT_ERR);
    }

    struct addrinfo hints;
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    ret = getaddrinfo(tac_server_name, "tacacs", &hints, &tac_server);
    if (ret != 0) {
        printf("error: resolving name %s: %s", tac_server_name,
               gai_strerror(ret));
        exit(EXIT_ERR);
    }

    if (tac_secret == NULL) {
        printf("error: server secret is required.\n");
        exit(EXIT_ERR);
    }

    if (pass == NULL) {
        signal(SIGALRM, timeout_handler);
        alarm(GETPASS_TIMEOUT);
        pass = getpass(PASSWORD_PROMPT);
        alarm(0);
        signal(SIGALRM, SIG_DFL);
        if (!strlen(pass))
            exit(EXIT_ERR);
    }

    if (tty == NULL) {
        printf("error: tty name is required.\n");
        exit(EXIT_ERR);
    }

    /* open syslog before any TACACS+ calls */
    openlog("tacc", LOG_CONS | LOG_PID, LOG_AUTHPRIV);

    if (do_authen)
        authenticate(tac_server, tac_secret, g_user, pass, tty, remote_addr);

    if (do_author) {
        /* authorize user */
        struct tac_attrib *attr = NULL;
        tac_add_attrib(&attr, "service", service);
        tac_add_attrib(&attr, "protocol", protocol);

        tac_fd = tac_connect_single(tac_server, tac_secret, NULL, 60);
        if (tac_fd < 0) {
            if (!quiet)
                printf("Error connecting to TACACS+ server: %m\n");
            exit(EXIT_ERR);
        }

        tac_author_send(tac_fd, g_user, tty, remote_addr, attr);

        tac_author_read(tac_fd, &arep);
        if (arep.status != AUTHOR_STATUS_PASS_ADD
            && arep.status != AUTHOR_STATUS_PASS_REPL) {
            if (!quiet)
                printf("Authorization FAILED: %s\n", arep.msg);
            exit(EXIT_FAIL);
        } else {
            if (!quiet)
                printf("Authorization OK: %s\n", arep.msg);
        }

        tac_free_attrib(&attr);
    }

    /* we no longer need the password in our address space */
    bzero(pass, strlen(pass));
    pass = NULL;

    if (do_account) {
        /* start accounting */
        struct tac_attrib *attr = NULL;

        sprintf(buf, "%lu", time(0));
        tac_add_attrib(&attr, "start_time", buf);

        // this is not crypto but merely an identifier
        long rnd_id = random();
        memcpy(&task_id, &rnd_id, sizeof(task_id));

        sprintf(buf, "%hu", task_id);
        tac_add_attrib(&attr, "task_id", buf);
        tac_add_attrib(&attr, "service", service);
        tac_add_attrib(&attr, "protocol", protocol);

        tac_fd = tac_connect_single(tac_server, tac_secret, NULL, 60);
        if (tac_fd < 0) {
            if (!quiet)
                printf("Error connecting to TACACS+ server: %m\n");
            exit(EXIT_ERR);
        }

        tac_acct_send(tac_fd, TAC_PLUS_ACCT_FLAG_START, g_user, tty, remote_addr,
                      attr);

        ret = tac_acct_read(tac_fd, &arep);
        if (ret == 0) {
            if (!quiet)
                printf("Accounting: START failed: %s\n", arep.msg);
            syslog(LOG_INFO, "TACACS+ accounting start failed: %s", arep.msg);
        } else if (!login_mode && !quiet)
            printf("Accounting: START OK\n");

        close(tac_fd);

        tac_free_attrib(&attr);

    }

    /* log in local utmp */
    if (log_wtmp) {
#if defined(HAVE_PUTUTXLINE)
        struct timeval tv;

        gettimeofday(&tv, NULL);

        memset(&utmpx, 0, sizeof(utmpx));
        utmpx.ut_type = USER_PROCESS;
        utmpx.ut_pid = getpid();
        xstrcpy(utmpx.ut_line, tty, sizeof(utmpx.ut_line));
        strncpy(utmpx.ut_id, tty + C_STRLEN("tty"), sizeof(utmpx.ut_id));
        xstrcpy(utmpx.ut_host, "dialup", sizeof(utmpx.ut_host));
        utmpx.ut_tv.tv_sec = tv.tv_sec;
        utmpx.ut_tv.tv_usec = tv.tv_usec;
        xstrcpy(utmpx.ut_user, g_user, sizeof(utmpx.ut_user));
        /* ut_addr unused ... */
        setutxent();
        pututxline(&utmpx);
#elif defined(HAVE_LOGWTMP)
        logwtmp(tty, g_user, "dialup");
#endif
    }

    if (command != NULL) {
        int ret;

        syslog(LOG_DEBUG, "starting %s for %s", command, g_user);

        signal(SIGHUP, SIG_IGN);
        signal(SIGTERM, SIG_IGN);
        signal(SIGINT, SIG_IGN);
        signal(SIGCHLD, SIG_IGN);

#ifdef COMMAND_MESSAGE
        printf(COMMAND_MESSAGE);
#endif

#if USE_SYSTEM
        ret = system(command);
        if (ret < 0)
            syslog(LOG_WARNING, "command failed: %m");
        else
            syslog(LOG_NOTICE, "command exit code %u", ret);
#else
        pid=fork();

        if(pid == 0) {
            /* child */

            execl(DEFAULT_COMMAND, DEFAULT_COMMAND, ARGS, NULL);
            syslog(LOG_ERR, "execl() failed: %m");
            _exit(EXIT_FAIL);
        }

        if(pid < 0) {
            /* error */
            syslog(LOG_ERR, "fork failed: %m");
            exit(EXIT_FAIL);
        }

        if(pid > 0) {
            /* parent */
            int st, r;

            r=wait(&st);
        }
#endif
    }

    if (do_account) {
        /* stop accounting */
        struct tac_attrib *attr = NULL;
        sprintf(buf, "%lu", time(0));
        tac_add_attrib(&attr, "stop_time", buf);
        sprintf(buf, "%hu", task_id);
        tac_add_attrib(&attr, "task_id", buf);

        tac_fd = tac_connect_single(tac_server, tac_secret, NULL, 60);
        if (tac_fd < 0) {
            if (!quiet)
                printf("Error connecting to TACACS+ server: %m\n");
            exit(EXIT_ERR);
        }

        tac_acct_send(tac_fd, TAC_PLUS_ACCT_FLAG_STOP, g_user, tty, remote_addr,
                      attr);
        ret = tac_acct_read(tac_fd, &arep);
        if (ret == 0) {
            if (!quiet)
                printf("Accounting: STOP failed: %s", arep.msg);
            syslog(LOG_INFO, "TACACS+ accounting stop failed: %s\n", arep.msg);
        } else if (!login_mode && !quiet)
            printf("Accounting: STOP OK\n");

        close(tac_fd);

        tac_free_attrib(&attr);
    }

    /* logout from utmp */
    if (log_wtmp) {
#if defined(HAVE_PUTUTXLINE)
        utmpx.ut_type = DEAD_PROCESS;
        memset(utmpx.ut_line, 0, sizeof(utmpx.ut_line));
        memset(utmpx.ut_user, 0, sizeof(utmpx.ut_user));
        memset(utmpx.ut_host, 0, sizeof(utmpx.ut_host));
        utmpx.ut_tv.tv_sec = utmpx.ut_tv.tv_usec = 0;
        setutxent();
        pututxline(&utmpx);
#elif defined(HAVE_LOGWTMP)
        logwtmp(tty, "", "");
#endif
    }

    exit(EXIT_OK);
}