/* 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; }
/* * return value: * 0 : success * < 0 : error status code, see LIBTAC_STATUS_... * LIBTAC_STATUS_WRITE_ERR * LIBTAC_STATUS_WRITE_TIMEOUT (pending impl) * LIBTAC_STATUS_ASSEMBLY_ERR (pending impl) */ int tac_acct_send(int fd, int type, const char *user, char *tty, char *r_addr, struct tac_attrib *attr) { HDR *th; struct acct tb; u_char user_len, port_len, r_addr_len; struct tac_attrib *a; int i = 0; /* arg count */ int pkt_len = 0; int pktl = 0; int w; /* write count */ u_char *pkt=NULL; /* u_char *pktp; */ /* obsolute */ int ret = 0; th = _tac_req_header(TAC_PLUS_ACCT, 0); /* set header options */ th->version=TAC_PLUS_VER_0; th->encryption=tac_encryption ? TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG; TACDEBUG(LOG_DEBUG, "%s: user '%s', tty '%s', rem_addr '%s', encrypt: %s, type: %s", \ __FUNCTION__, user, tty, r_addr, \ (tac_encryption) ? "yes" : "no", \ tac_acct_flag2str(type)); user_len=(u_char) strlen(user); port_len=(u_char) strlen(tty); r_addr_len=(u_char) strlen(r_addr); tb.flags=(u_char) type; tb.authen_method=tac_authen_method; tb.priv_lvl=tac_priv_lvl; if (!*tac_login) { /* default to PAP */ tb.authen_type = TAC_PLUS_AUTHEN_TYPE_PAP; } else { if (strcmp(tac_login,"chap") == 0) { tb.authen_type=TAC_PLUS_AUTHEN_TYPE_CHAP; } else if(strcmp(tac_login,"login") == 0) { tb.authen_type=TAC_PLUS_AUTHEN_TYPE_ASCII; } else { tb.authen_type=TAC_PLUS_AUTHEN_TYPE_PAP; } } tb.authen_service=tac_authen_service; tb.user_len=user_len; tb.port_len=port_len; tb.r_addr_len=r_addr_len; /* allocate packet */ pkt=(u_char *) xcalloc(1, TAC_ACCT_REQ_FIXED_FIELDS_SIZE); pkt_len=sizeof(tb); /* fill attribute length fields */ a = attr; while (a) { pktl = pkt_len; pkt_len += sizeof(a->attr_len); pkt = (u_char*) xrealloc(pkt, pkt_len); /* see comments in author_s.c pktp=pkt + pkt_len; pkt_len += sizeof(a->attr_len); pkt = xrealloc(pkt, pkt_len); */ bcopy(&a->attr_len, pkt + pktl, sizeof(a->attr_len)); i++; a = a->next; } /* fill the arg count field and add the fixed fields to packet */ tb.arg_cnt = i; bcopy(&tb, pkt, TAC_ACCT_REQ_FIXED_FIELDS_SIZE); /* #define PUTATTR(data, len) \ pktp = pkt + pkt_len; \ pkt_len += len; \ pkt = xrealloc(pkt, pkt_len); \ bcopy(data, pktp, len); */ #define PUTATTR(data, len) \ pktl = pkt_len; \ pkt_len += len; \ pkt = (u_char*) xrealloc(pkt, pkt_len); \ bcopy(data, pkt + pktl, len); /* fill user and port fields */ PUTATTR(user, user_len) PUTATTR(tty, port_len) PUTATTR(r_addr, r_addr_len) /* fill attributes */ a = attr; while(a) { PUTATTR(a->attr, a->attr_len) a = a->next; } /* finished building packet, fill len_from_header in header */ th->datalength = htonl(pkt_len); /* write header */ w = write(fd, th, TAC_PLUS_HDR_SIZE); if(w < TAC_PLUS_HDR_SIZE) { TACSYSLOG(LOG_ERR, "%s: short write on header, wrote %d of %d: %m",\ __FUNCTION__, w, TAC_PLUS_HDR_SIZE); free(pkt); free(th); return LIBTAC_STATUS_WRITE_ERR; } /* encrypt packet body */ _tac_crypt(pkt, th); /* write body */ w=write(fd, pkt, pkt_len); if(w < pkt_len) { TACSYSLOG(LOG_ERR, "%s: short write on body, wrote %d of %d: %m",\ __FUNCTION__, w, pkt_len); ret = LIBTAC_STATUS_WRITE_ERR; } free(pkt); free(th); TACDEBUG(LOG_DEBUG, "%s: exit status=%d", __FUNCTION__, ret); return ret; }
int _pam_account(pam_handle_t *pamh, int argc, const char **argv, int type, char *cmd) { int retval; static int ctrl; char *user = NULL; char *tty = NULL; char *r_addr = NULL; char *typemsg; int status = PAM_SESSION_ERR; int srv_i, tac_fd; typemsg = tac_acct_flag2str(type); ctrl = _pam_parse (argc, argv); if (ctrl & PAM_TAC_DEBUG) { syslog (LOG_DEBUG, "%s: [%s] called (pam_tacplus v%u.%u.%u)", __FUNCTION__, typemsg, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); syslog(LOG_DEBUG, "%s: tac_srv_no=%d", __FUNCTION__, tac_srv_no); } if ((user = _pam_get_user(pamh)) == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: username [%s] obtained", __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 [%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); /* checks for specific data required by TACACS+, which should be supplied in command line */ if(*tac_service == '\0') { _pam_log (LOG_ERR, "ACC: TACACS+ service type not configured"); return PAM_AUTH_ERR; } if(*tac_protocol == '\0') { _pam_log (LOG_ERR, "ACC: TACACS+ protocol type not configured (IGNORED)"); } /* when this module is called from within pppd or other application dealing with serial lines, it is likely that we will get hit with signal caused by modem hangup; this is important only for STOP packets, it's relatively rare that modem hangs up on accounting start */ if(type == TAC_PLUS_ACCT_FLAG_STOP) { signal(SIGALRM, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); } status = PAM_SESSION_ERR; for(srv_i = 0; srv_i < tac_srv_no; srv_i++) { tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, NULL, tac_timeout); if (tac_fd < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (fd)", __FUNCTION__, typemsg); continue; } if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: connected with fd=%d (srv %d)", __FUNCTION__, tac_fd, srv_i); retval = _pam_send_account(tac_fd, type, user, tty, r_addr, cmd); if (retval < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (acct)", __FUNCTION__, typemsg); } else { status = PAM_SUCCESS; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: [%s] for [%s] sent", __FUNCTION__, typemsg, user); } close(tac_fd); if ((status == PAM_SUCCESS) && !(ctrl & PAM_TAC_ACCT)) { /* do not send acct start/stop packets to _all_ servers */ break; } } if (type == TAC_PLUS_ACCT_FLAG_STOP) { signal(SIGALRM, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); } return status; }
/* * Send an accounting record to the TACACS+ server. * We send the start/stop accounting records even if the user is not known * to the TACACS+ server. This seems non-intuitive, but it's the way * this code is written to work. */ int _pam_account(pam_handle_t *pamh, int argc, const char **argv, int type, char *cmd) { int retval; int ctrl; char *user = NULL; char *tty = NULL; char *r_addr = NULL; char *typemsg; int status = PAM_SESSION_ERR; int srv_i, tac_fd; typemsg = tac_acct_flag2str(type); ctrl = _pam_parse (argc, argv); if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: [%s] called (pam_tacplus v%u.%u.%u)", __func__, typemsg, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); _pam_get_user(pamh, &user); if (user == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: username [%s] obtained", __func__, user); if (!task_id) #if defined(HAVE_OPENSSL_RAND_H) && defined(HAVE_LIBCRYPTO) RAND_pseudo_bytes((unsigned char *) &task_id, sizeof(task_id)); #else task_id = (short unsigned int) tac_magic(); #endif _pam_get_terminal(pamh, &tty); if(!strncmp(tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: tty [%s] obtained", __func__, tty); _pam_get_rhost(pamh, &r_addr); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: rhost [%s] obtained", __func__, r_addr); /* checks for specific data required by TACACS+, which should be supplied in command line */ if(tac_protocol[0] == '\0') { _pam_log (LOG_ERR, "ACC: TACACS+ protocol type not configured"); return PAM_AUTH_ERR; } /* when this module is called from within pppd or other application dealing with serial lines, it is likely that we will get hit with signal caused by modem hangup; this is important only for STOP packets, it's relatively rare that modem hangs up on accounting start */ if(type == TAC_PLUS_ACCT_FLAG_STOP) { signal(SIGALRM, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); } /* * If PAM_SESSION_ERR is used, then the pam config can't * ignore server failures, so use PAM_AUTHINFO_UNAVAIL. * * We have to make a new connection each time, because libtac is single * threaded (doesn't support multiple connects at the same time due to * use of globals)), and doesn't have support for persistent connections. * That's fixable, but not worth the effort at this point. * * TODO: this should be converted to use do_tac_connect eventually. */ status = PAM_AUTHINFO_UNAVAIL; for(srv_i = 0; srv_i < tac_srv_no; srv_i++) { tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, NULL, __vrfname); if (tac_fd < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (fd)", __func__, typemsg); continue; } if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: connected with fd=%d to srv[%d] %s", __func__, tac_fd, srv_i, tac_srv[srv_i].addr ? tac_ntop(tac_srv[srv_i].addr->ai_addr) : "not set"); retval = _pam_send_account(tac_fd, type, user, tty, r_addr, cmd); if (retval < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (acct)", __func__, typemsg); } else { status = PAM_SUCCESS; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: [%s] for [%s] sent", __func__, typemsg, user); } if ((status == PAM_SUCCESS) && !(ctrl & PAM_TAC_ACCT)) { /* do not send acct start/stop packets to _all_ servers */ break; } } if (type == TAC_PLUS_ACCT_FLAG_STOP) { signal(SIGALRM, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); } return status; }