Esempio n. 1
0
/* Helper functions */
int _pam_send_account(int tac_fd, int type, const char *user, char *tty,
    char *r_addr, char *cmd) {

    char buf[64];
    struct tac_attrib *attr;
    int retval;

    attr=(struct tac_attrib *)xcalloc(1, sizeof(struct tac_attrib));

    sprintf(buf, "%lu", (unsigned long)time(NULL));

    if (type == TAC_PLUS_ACCT_FLAG_START) {
        tac_add_attrib(&attr, "start_time", buf);
    } else if (type == TAC_PLUS_ACCT_FLAG_STOP) {
        tac_add_attrib(&attr, "stop_time", buf);
    }
    sprintf(buf, "%hu", task_id);
    tac_add_attrib(&attr, "task_id", buf);
    tac_add_attrib(&attr, "service", tac_service);
    if(tac_protocol[0] != '\0')
      tac_add_attrib(&attr, "protocol", tac_protocol);
    if (cmd != NULL) {
        tac_add_attrib(&attr, "cmd", cmd);
    }

    retval = tac_acct_send(tac_fd, type, user, tty, r_addr, attr);

    /* this is no longer needed */
    tac_free_attrib(&attr);

    if(retval < 0) {
        _pam_log (LOG_WARNING, "%s: send %s accounting failed (task %hu)",
            __FUNCTION__,
            tac_acct_flag2str(type),
            task_id);
        close(tac_fd);
        return -1;
    }

    struct areply re;
    if( tac_acct_read(tac_fd, &re) != TAC_PLUS_ACCT_STATUS_SUCCESS ) {
        _pam_log (LOG_WARNING, "%s: accounting %s failed (task %hu)",
            __FUNCTION__,
            tac_acct_flag2str(type),
            task_id);

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

        close(tac_fd);
        return -1;
    }

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

    close(tac_fd);
    return 0;
}
Esempio n. 2
0
static void
accounting_start(void)
{
    int  tac_fd;
    char *phone;
    char *msg;
    struct tac_attrib   *attr;
    struct in_addr      peer_addr;
    char   buf[40];

    if (prev_ip_up_hook) {
	prev_ip_up_hook();
    }
    
    if (use_tacacs && use_account && authorized) {
	authorized = 0;
	logged_in = 1;

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

	/* start accounting */
	attr = NULL;

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

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

	phone = getenv("CALLER_ID");
	if (!phone)
	    phone = "Unknow";
	tac_add_attrib(&attr, "phone_#", phone);

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

	peer_addr.s_addr = ipcp_hisoptions[0].hisaddr;
	sprintf(buf, "%s", inet_ntoa(peer_addr));

	tac_account_send(tac_fd, TAC_PLUS_ACCT_FLAG_START, peer_authname, tty, buf, attr);

	msg = tac_account_read(tac_fd);
	if (msg != NULL)
	    syslog(LOG_ERR,"TACACS+ start accounting failed: %s", msg);

	close(tac_fd); 
	tac_free_attrib(&attr);
    }
}
Esempio n. 3
0
static void
accounting_stop(void)
{
    int  tac_fd;
    char *msg;
    struct tac_attrib *attr;
    struct in_addr      peer_addr;
    char   buf[40];

    if (prev_ip_down_hook) {
	prev_ip_down_hook();
    }
    
    if (use_tacacs && use_account && logged_in) {
	logged_in = 0;

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

	/* stop accounting */
	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);
	if (link_stats_valid) {
	    sprintf(buf, "%d", link_stats.bytes_out);
	    tac_add_attrib(&attr, "bytes_out", buf);
	    sprintf(buf, "%d", link_stats.bytes_in);
	    tac_add_attrib(&attr, "bytes_in", buf);
	    sprintf(buf, "%d", link_connect_time);
	    tac_add_attrib(&attr, "elapsed_time", buf);
	    peer_addr.s_addr = ipcp_hisoptions[0].hisaddr;
	    sprintf(buf, "%s", inet_ntoa(peer_addr));
	}
	
	tac_account_send(tac_fd, TAC_PLUS_ACCT_FLAG_STOP, peer_authname, tty, buf, attr);
	
	msg = tac_account_read(tac_fd);
	if (msg != NULL)
	    syslog(LOG_ERR,"TACACS+ stop accounting failed: %s\n", msg);
	
	close(tac_fd);
	tac_free_attrib(&attr);
    }
}
Esempio n. 4
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 */
Esempio n. 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 ctrl, status=PAM_AUTH_ERR;
    char *user;
    char *tty;
    char *r_addr;
    struct areply arep;
    struct tac_attrib *attr_s = NULL, *attr;
    int tac_fd = -1;

    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)",
            __func__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT);

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


    _pam_get_terminal(pamh, &tty);
    if(!strncmp(tty, "/dev/", 5))
        tty += 5;

    _pam_get_rhost(pamh, &r_addr);

    /* checks for specific data required by TACACS+, which should
       be supplied in pam module command line  */
    if(!*tac_service) {
        _pam_log (LOG_ERR, "TACACS+ service type not configured");
        return PAM_AUTH_ERR;
    }
    tac_add_attrib(&attr_s, "service", tac_service);

    if(tac_protocol != NULL && tac_protocol[0] != '\0')
          tac_add_attrib(&attr_s, "protocol", tac_protocol);
    else
          _pam_log (LOG_ERR, "TACACS+ protocol type not configured (IGNORED)");

    tac_add_attrib(&attr_s, "cmd", "");

    memset(&arep, 0, sizeof arep);

    /*
     * Check if user is authorized, independently of authentication.
     * Authentication may have happened via ssh public key, rather than
     * via TACACS+.  PAM should not normally get to this entry point if
     * user is not yet authenticated.
     * We only write the mapping entry (if needed) when authorization
     * is succesful.
     * attr is not used here, but having a non-NULL value is how
     * talk_tac_server() distinguishes that it is an acct call, vs auth
     * TODO: use a different mechanism
    */
    status = do_tac_connect(ctrl, &tac_fd, user, NULL, tty, r_addr, &attr_s,
        &arep, pamh);
    tac_free_attrib(&attr_s);
    if(active_server.addr == NULL || tac_fd < 0) {
        /* we need to return PAM_AUTHINFO_UNAVAIL here, rather than
         * PAM_AUTH_ERR, or we can't use "ignore" in the pam configuration
         */
        status = PAM_AUTHINFO_UNAVAIL;
        goto cleanup;
    }

    if(status) {
        if (ctrl & PAM_TAC_DEBUG)
            _pam_log(LOG_NOTICE, "No TACACS mapping for %s after auth failure",
                user);
        goto cleanup;
    }

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

    attr = arep.attr;
    while (attr != NULL)  {
        char attribute[attr->attr_len];
        char value[attr->attr_len];
        char attrenv[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",
                    __func__, attribute, value);

            if(strncmp(attribute, "PRIV", 4) == 0) {
                char *ok;

                /* because of the separation above, value will start with
                 * the separator, which we don't want.  */
                priv_level = (unsigned)strtoul(value+1, &ok, 0);
                /* if this fails, we leave priv_level at 0, which is
                 * least privileged, so that's OK, but at least report it
                 */
                if (ok == value)
                    _pam_log (LOG_WARNING,
                        "%s: non-numeric privilege for %s, got (%s)",
                        __func__, attribute, value+1);
            }

            /*
             * make returned attributes available for other PAM modules via PAM
             * environment. Since separator can be = or *, ensure it's = for
             * the env.
             */
            snprintf(attrenv, sizeof attribute, "%s=%s", attribute, value+1);
            if (pam_putenv(pamh, attrenv) != PAM_SUCCESS)
                _pam_log(LOG_WARNING, "%s: unable to set PAM environment (%s)",
                    __func__, attribute);

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

    update_mapped(pamh, user, priv_level, r_addr);


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

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

    if(tac_fd >= 0) {
        active_server.addr = NULL;
        close(tac_fd);
        tac_fd = -1;
    }

    return status;
}    /* pam_sm_acct_mgmt */
Esempio n. 6
0
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;
}
Esempio n. 7
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 */
Esempio n. 8
0
/**
 * ... 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;
}
Esempio n. 9
0
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);
}