/* 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; }
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); }