static int get_tally( tally_t *tally, uid_t uid, const char *filename, FILE **TALLY, struct fail_s *fsp) { struct stat fileinfo; int lstat_ret = lstat(filename,&fileinfo); if ( lstat_ret && *tally!=TALLY_HI ) { int oldmask = umask(077); *TALLY=fopen(filename, "a"); /* Create file, or append-open in pathological case. */ umask(oldmask); if ( !*TALLY ) { _pam_log(LOG_ALERT, "Couldn't create %s",filename); return PAM_AUTH_ERR; } lstat_ret = fstat(fileno(*TALLY),&fileinfo); fclose(*TALLY); } if ( lstat_ret ) { _pam_log(LOG_ALERT, "Couldn't stat %s",filename); return PAM_AUTH_ERR; } if((fileinfo.st_mode & S_IWOTH) || !S_ISREG(fileinfo.st_mode)) { /* If the file is world writable or is not a normal file, return error */ _pam_log(LOG_ALERT, "%s is either world writable or not a normal file", filename); return PAM_AUTH_ERR; } if ( ! ( *TALLY = fopen(filename,(*tally!=TALLY_HI)?"r+":"r") ) ) { _pam_log(LOG_ALERT, "Error opening %s for update", filename); /* Discovering why account service fails: e/uid are target user. * * perror(MODULE_NAME); * fprintf(stderr,"uid %d euid %d\n",getuid(), geteuid()); */ return PAM_AUTH_ERR; } if ( fseek( *TALLY, uid * sizeof(struct faillog), SEEK_SET ) ) { _pam_log(LOG_ALERT, "fseek failed %s", filename); return PAM_AUTH_ERR; } if ( fileinfo.st_size <= uid * sizeof(struct faillog) ) { memset(fsp, 0, sizeof(struct faillog)); *tally=0; fsp->fs_faillog.fail_time = time(NULL); } else if (( fread((char *) &fsp->fs_faillog, sizeof(struct faillog), 1, *TALLY) )==0 ) { *tally=0; /* Assuming a gappy filesystem */ } else { *tally = fsp->fs_faillog.fail_cnt; } return PAM_SUCCESS; }
static int tally_bump (int inc, pam_handle_t *pamh, int flags, int argc, const char **argv) { uid_t uid; int fail_on_error = FALSE; tally_t tally = 0; /* !TALLY_HI --> Log opened for update */ char no_magic_root = FALSE; char filename[ FILENAME_MAX ] = DEFAULT_LOGFILE; /* Should probably decode the parameters before anything else. */ { for ( ; argc-- > 0; ++argv ) { /* generic options.. um, ignored. :] */ if ( ! strcmp( *argv, "no_magic_root" ) ) { no_magic_root = TRUE; } else if ( ! strncmp( *argv, "file=", 5 ) ) { char const *from = (*argv)+5; char *to = filename; if ( *from!='/' || strlen(from)>FILENAME_MAX-1 ) { _pam_log(LOG_ERR, MODULE_NAME ": filename not /rooted or too long; ", *argv); RETURN_ERROR( PAM_AUTH_ERR ); } while ( ( *to++ = *from++ ) ); } else if ( ! strcmp( *argv, "onerr=fail" ) ) { fail_on_error=TRUE; } else if ( ! strcmp( *argv, "onerr=succeed" ) ) { fail_on_error=FALSE; } else { _pam_log(LOG_ERR, MODULE_NAME ": unknown option; %s",*argv); } } /* for() */ } { FILE *TALLY = NULL; const char *user = NULL, *remote_host = NULL, *cur_tty = NULL; struct fail_s fs, *fsp = &fs; int i=pam_get_uid(pamh, &uid, &user); if ( i != PAM_SUCCESS ) RETURN_ERROR( i ); i=get_tally( &tally, uid, filename, &TALLY, fsp ); /* to remember old fail time (for locktime) */ fsp->fs_fail_time = fsp->fs_faillog.fail_time; fsp->fs_faillog.fail_time = time(NULL); (void) pam_get_item(pamh, PAM_RHOST, (const void **)&remote_host); if (!remote_host) { (void) pam_get_item(pamh, PAM_TTY, (const void **)&cur_tty); if (!cur_tty) { strncpy(fsp->fs_faillog.fail_line, "unknown", sizeof(fsp->fs_faillog.fail_line) - 1); fsp->fs_faillog.fail_line[sizeof(fsp->fs_faillog.fail_line)-1] = 0; } else { strncpy(fsp->fs_faillog.fail_line, cur_tty, sizeof(fsp->fs_faillog.fail_line)-1); fsp->fs_faillog.fail_line[sizeof(fsp->fs_faillog.fail_line)-1] = 0; } } else { strncpy(fsp->fs_faillog.fail_line, remote_host, (size_t)sizeof(fsp->fs_faillog.fail_line)); fsp->fs_faillog.fail_line[sizeof(fsp->fs_faillog.fail_line)-1] = 0; } if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); } if ( no_magic_root || getuid() ) { /* no_magic_root kills uid test */ tally+=inc; if ( tally==TALLY_HI ) { /* Overflow *and* underflow. :) */ tally-=inc; _pam_log(LOG_ALERT,"Tally %sflowed for user %s", (inc<0)?"under":"over",user); } } i=set_tally( tally, uid, filename, &TALLY, fsp ); if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); } } return PAM_SUCCESS; }
int _pam_parse (int argc, const char **argv) { int ctrl = 0; const char *current_secret = NULL; /* otherwise the list will grow with each call */ memset(tac_srv, 0, sizeof(tacplus_server_t) * TAC_PLUS_MAXSERVERS); tac_srv_no = 0; tac_service[0] = 0; tac_protocol[0] = 0; tac_prompt[0] = 0; tac_login[0] = 0; for (ctrl = 0; argc-- > 0; ++argv) { if (!strcmp (*argv, "debug")) { /* all */ ctrl |= PAM_TAC_DEBUG; } else if (!strcmp (*argv, "use_first_pass")) { ctrl |= PAM_TAC_USE_FIRST_PASS; } else if (!strcmp (*argv, "try_first_pass")) { ctrl |= PAM_TAC_TRY_FIRST_PASS; } else if (!strncmp (*argv, "service=", 8)) { /* author & acct */ xstrcpy (tac_service, *argv + 8, sizeof(tac_service)); } else if (!strncmp (*argv, "protocol=", 9)) { /* author & acct */ xstrcpy (tac_protocol, *argv + 9, sizeof(tac_protocol)); } else if (!strncmp (*argv, "prompt=", 7)) { /* authentication */ xstrcpy (tac_prompt, *argv + 7, sizeof(tac_prompt)); /* Replace _ with space */ int chr; for (chr = 0; chr < strlen(tac_prompt); chr++) { if (tac_prompt[chr] == '_') { tac_prompt[chr] = ' '; } } } else if (!strncmp (*argv, "login="******"acct_all")) { ctrl |= PAM_TAC_ACCT; } else if (!strncmp (*argv, "server=", 7)) { /* authen & acct */ if(tac_srv_no < TAC_PLUS_MAXSERVERS) { struct addrinfo hints, *servers, *server; int rv; char *port, server_buf[256]; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; /* use IPv4 or IPv6, whichever */ hints.ai_socktype = SOCK_STREAM; if (strlen(*argv + 7) >= sizeof(server_buf)) { _pam_log(LOG_ERR, "server address too long, sorry"); continue; } strcpy(server_buf, *argv + 7); port = strchr(server_buf, ':'); if (port != NULL) { *port = '\0'; port++; } if ((rv = getaddrinfo(server_buf, (port == NULL) ? "49" : port, &hints, &servers)) == 0) { for(server = servers; server != NULL && tac_srv_no < TAC_PLUS_MAXSERVERS; server = server->ai_next) { tac_srv[tac_srv_no].addr = server; tac_srv[tac_srv_no].key = current_secret; tac_srv_no++; } } else { _pam_log (LOG_ERR, "skip invalid server: %s (getaddrinfo: %s)", server_buf, gai_strerror(rv)); } } else { _pam_log(LOG_ERR, "maximum number of servers (%d) exceeded, skipping", TAC_PLUS_MAXSERVERS); } } else if (!strncmp (*argv, "secret=", 7)) { int i; current_secret = *argv + 7; /* points right into argv (which is const) */ /* if 'secret=' was given after a 'server=' parameter, fill in the current secret */ for(i = tac_srv_no-1; i >= 0; i--) { if (tac_srv[i].key != NULL) break; tac_srv[i].key = current_secret; } } else if (!strncmp (*argv, "timeout=", 8)) { /* FIXME atoi() doesn't handle invalid numeric strings well */ tac_timeout = atoi(*argv + 8); if (tac_timeout < 0) tac_timeout = 0; } else { _pam_log (LOG_WARNING, "unrecognized option: %s", *argv); } } if (ctrl & PAM_TAC_DEBUG) { int n; _pam_log(LOG_DEBUG, "%d servers defined", tac_srv_no); for(n = 0; n < tac_srv_no; n++) { _pam_log(LOG_DEBUG, "server[%d] { addr=%s, key='%s' }", n, tac_ntop(tac_srv[n].addr->ai_addr), tac_srv[n].key); } _pam_log(LOG_DEBUG, "tac_service='%s'", tac_service); _pam_log(LOG_DEBUG, "tac_protocol='%s'", tac_protocol); _pam_log(LOG_DEBUG, "tac_prompt='%s'", tac_prompt); _pam_log(LOG_DEBUG, "tac_login='******'", tac_login); } return ctrl; } /* _pam_parse */
PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char **argv) { const char *user = NULL, *passwd = NULL; struct passwd *pwd; int rval, status; pid_t pid; // For checking mount paths: (mount from + target) char path[PATH_MAX]; char targetpath[PATH_MAX]; char encfs_options[USERNAME_MAX]; char fuse_options[USERNAME_MAX]; char *targetpath_store; strcpy(default_encfs_options, ""); strcpy(default_fuse_options, ""); // For execing: char *arg[USERNAME_MAX]; int arg_pos = 0; int i; int inpipe[2], outpipe[2]; rval = pam_get_user(pamh, &user, NULL); if ((rval != PAM_SUCCESS) || (!user)) { _pam_log(LOG_ERR, "can't get username: %s", pam_strerror(pamh, rval)); return PAM_AUTH_ERR; } rval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) (void *) &passwd); if (rval != PAM_SUCCESS) { _pam_log(LOG_ERR, "Could not retrieve user's password"); return PAM_AUTH_ERR; } if (!passwd) { rval = _set_auth_tok(pamh, flags, argc, argv); if (rval != PAM_SUCCESS) { return rval; } rval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) (void *) &passwd); if (rval != PAM_SUCCESS || passwd == NULL) { _pam_log(LOG_ERR, "Could not retrieve user's password"); return PAM_AUTH_ERR; } } if ((pwd = getpwnam(user)) == NULL) { _pam_log(LOG_ERR, "Could not getpwnam"); return PAM_AUTH_ERR; } // Read configfile if (!readconfig (pwd, pamh, pwd->pw_name, path, targetpath, encfs_options, fuse_options)) { // DEBUG _pam_log(LOG_ERR,"No entry for user found in log"); return PAM_IGNORE; } //DEBUG _pam_log(LOG_ERR,"Username : %s, Encpath : %s, Targetmount : %s",pwd->pw_name,path,targetpath); //Store targetpath targetpath_store = strdup(targetpath); if ((i = pam_set_data(pamh, "encfs_targetpath", targetpath_store, targetpath_cleanup)) != PAM_SUCCESS) { _pam_log(LOG_ERR, "Storing targetpath FAIL"); free(targetpath_store); return i; } // Check if we're mounted already. if (checkmnt(targetpath)) { //DEBUG _pam_log(LOG_ERR,"Already mounted"); return PAM_IGNORE; } /* _pam_log(LOG_ERR,"Config output for %s:",user); _pam_log(LOG_ERR," path : %s",path); _pam_log(LOG_ERR," targetpath : %s",targetpath); _pam_log(LOG_ERR," encfs : %s %s",default_encfs_options,encfs_options); _pam_log(LOG_ERR," fuse : %s %s",default_fuse_options,fuse_options); */ arg_pos += buildCmd(arg, arg_pos, "encfs"); arg_pos += buildCmd(arg, arg_pos, "-S"); arg_pos += buildCmd(arg, arg_pos, default_encfs_options); arg_pos += buildCmd(arg, arg_pos, encfs_options); arg_pos += buildCmd(arg, arg_pos, path); arg_pos += buildCmd(arg, arg_pos, targetpath); if (strlen(default_fuse_options) > 0 && strlen(fuse_options) > 0) strcat(fuse_options, ","); strcat(fuse_options,default_fuse_options); if (strlen(fuse_options) > 0) { arg_pos += buildCmd(arg, arg_pos, "--"); arg_pos += buildCmd(arg, arg_pos, "-o"); arg_pos += buildCmd(arg, arg_pos, fuse_options); } arg[arg_pos] = NULL; /* printf("Arguments : "); for (i = 0; i < arg_pos+1;i++) { _pam_log(LOG_ERR,"Data : %s",arg[i]); } _pam_log(LOG_ERR,"Number of arguments : %d",arg_pos); */ /* arg[0] = cmd; arg[1] = params; // arg[2] = params2; arg[2] = params3; arg[3] = path; arg[4] = targetpath; arg[5] = fuseparams; arg[6] = fuseparams2; arg[7] = NULL; */ if (pipe(inpipe) || pipe(outpipe)) { _pam_log(LOG_ERR, "Failed to create pipe"); return PAM_IGNORE; } // Execute switch (pid = fork()) { case -1: _pam_log(LOG_ERR, "Fork failed"); return PAM_SERVICE_ERR; case 0: if (drop_permissions == 1) if ((initgroups(pwd->pw_name, pwd->pw_gid) == -1) || (setgid(pwd->pw_gid) == -1) || (setuid(pwd->pw_uid) == -1)) { _pam_log(LOG_ERR, "Dropping permissions failed"); return PAM_SERVICE_ERR; } close(outpipe[WRITE_END]); dup2(outpipe[READ_END], fileno(stdin)); close(outpipe[READ_END]); close(inpipe[READ_END]); dup2(inpipe[WRITE_END], fileno(stdout)); close(inpipe[WRITE_END]); // For some reason the current directory has to be set to targetpath (or path?) before exec'ing encfs through gdm chdir(targetpath); execvp("encfs", arg); char errstr[128]; snprintf(errstr, 127, "%d - %s", errno, strerror(errno)); _pam_log(LOG_ERR, "Exec failed - %s", errstr); exit(127); } int len; close(inpipe[WRITE_END]); close(outpipe[READ_END]); if (waitpid(pid, &status, WNOHANG) == 0) { len = write(outpipe[WRITE_END], passwd, (size_t) strlen(passwd)); if ((len != (size_t) strlen(passwd)) || (write(outpipe[WRITE_END], "\n", 1) != 1)) _pam_log(LOG_ERR, "Did not send password to pipe (%d sent)", len); close(outpipe[WRITE_END]); } if (waitpid_timeout(pid, &status, 0)) { _pam_log(LOG_ERR, "Timed out waiting for encfs, killing\n"); kill(pid, SIGKILL); } int exitstatus = WEXITSTATUS(status); char buff[512]; len = read(inpipe[READ_END], &buff, 511); close(inpipe[READ_END]); buff[len] = 0; if (!checkmnt(targetpath) && (len > 0 || exitstatus > 0)) { _pam_log(LOG_ERR, "exitcode : %d, errorstring : %s", exitstatus, buff); return PAM_AUTH_ERR; } else { return PAM_IGNORE; } return PAM_AUTH_ERR; }
PAM_EXTERN int pam_sm_chauthtok (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; int status = PAM_TRY_AGAIN; int seq = 0; user = pass = tty = r_addr = NULL; 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); /* Preliminary call to check readiness of our module */ if (flags & PAM_PRELIM_CHECK) { /* Can we connect to TACACS? */ for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i ); tac_fd = tac_connect_single(tac_srv[srv_i], tac_srv_key[srv_i]); if (tac_fd < 0) { _pam_log (LOG_ERR, "connection failed srv %d: %m", srv_i); if (srv_i == tac_srv_no-1) { _pam_log (LOG_ERR, "no more servers to connect"); return PAM_TRY_AGAIN; } continue; } } close(tac_fd); return PAM_SUCCESS; } if ((user = _pam_get_user(pamh)) == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: user [%s] obtained", __FUNCTION__, user); /* CHPASS does not send a password in data field, * user is prompted by a GETDATA reply packet */ pass = malloc(5); strcpy(pass,"null"); tty = _pam_get_terminal(pamh); if (!strncmp (tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tty [%s] obtained", __FUNCTION__, tty); r_addr = _pam_get_rhost(pamh); if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: rhost [%s] obtained", __FUNCTION__, r_addr); /* Attempt server connect */ for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { status = TAC_PLUS_AUTHEN_STATUS_FAIL; if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i ); tac_fd = tac_connect_single(tac_srv[srv_i], tac_srv_key[srv_i]); if (tac_fd < 0) { _pam_log (LOG_ERR, "connection failed srv %d: %m", srv_i); if (srv_i == tac_srv_no-1) { _pam_log (LOG_ERR, "no more servers to connect"); return PAM_AUTHINFO_UNAVAIL; } continue; } /* Send AUTHEN/START */ if (tac_authen_send(tac_fd, user, pass, tty, r_addr, TAC_PLUS_AUTHEN_CHPASS, ctrl) < 0) { _pam_log (LOG_ERR, "error sending auth req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } else { /* Read AUTHEN/REPLY and act on status */ struct msg_status *msgstatus = malloc(sizeof(msg_status)); do { tac_authen_read(msgstatus, tac_fd, ctrl, &seq); status = msgstatus->status; switch (status) { case TAC_PLUS_AUTHEN_STATUS_GETPASS: /* AUTHEN/CONT with password */ if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, pass, ctrl, seq+1) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } break; case TAC_PLUS_AUTHEN_STATUS_GETDATA: { /* Get data from user with pam conversation */ struct pam_message msg; struct pam_response *resp = NULL; int retval; char *user_data = NULL; /* set up conversation call */ msg.msg_style = PAM_PROMPT_ECHO_OFF; msg.msg = malloc(100); strcpy((char *)msg.msg,msgstatus->server_msg); if ((retval = converse (pamh, 1, &msg, &resp)) != PAM_SUCCESS) { status = PAM_AUTHINFO_UNAVAIL; } else { if (resp != NULL) { if (resp->resp == NULL && (ctrl & PAM_TAC_DEBUG)) _pam_log (LOG_DEBUG, "pam_sm_authenticate: NULL given by user for GETDATA request"); user_data = resp->resp; resp->resp = NULL; } else { if (ctrl & PAM_TAC_DEBUG) { _pam_log (LOG_DEBUG, "pam_sm_authenticate: no error reported"); _pam_log (LOG_DEBUG, "getting data from user - NULL returned!?"); } return PAM_CONV_ERR; } /* AUTHEN/CONT with data */ if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, user_data, ctrl, seq+1) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } } free(msg.msg); } break; case TAC_PLUS_AUTHEN_STATUS_GETUSER: /* AUTHEN/CONT with username */ if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, user, ctrl, seq+1) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } break; } } while ( (status == TAC_PLUS_AUTHEN_STATUS_GETDATA) || (status == TAC_PLUS_AUTHEN_STATUS_GETPASS) || (status == TAC_PLUS_AUTHEN_STATUS_GETUSER) ); if (status != TAC_PLUS_AUTHEN_STATUS_PASS) { _pam_log (LOG_ERR, "auth failed: %d", status); status = PAM_AUTHTOK_ERR; } else { /* OK, we got authenticated; save the server that accepted us for pam_sm_acct_mgmt and exit the loop */ status = PAM_SUCCESS; active_server = tac_srv[srv_i]; active_key = tac_srv_key[srv_i]; close(tac_fd); break; } } close(tac_fd); } if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: exit with pam status: %i", __FUNCTION__, status); bzero (pass, strlen (pass)); free(pass); pass = NULL; return status; } /* pam_sm_chauthtok */
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { const char *username; const char *password; const char *member = NULL; int retval = PAM_AUTH_ERR; int i; /* parse arguments */ int ctrl = _pam_parse(argc, argv); /* Get the username */ retval = pam_get_user(pamh, &username, NULL); if ((retval != PAM_SUCCESS) || (!username)) { if (ctrl & WINBIND_DEBUG_ARG) _pam_log(LOG_DEBUG,"can not get the username"); return PAM_SERVICE_ERR; } retval = _winbind_read_password(pamh, ctrl, NULL, "Password: "******"Could not retrieve user's password"); return PAM_AUTHTOK_ERR; } if (ctrl & WINBIND_DEBUG_ARG) { /* Let's not give too much away in the log file */ #ifdef DEBUG_PASSWORD _pam_log(LOG_INFO, "Verify user `%s' with password `%s'", username, password); #else _pam_log(LOG_INFO, "Verify user `%s'", username); #endif } /* Retrieve membership-string here */ for ( i=0; i<argc; i++ ) { if ((strncmp(argv[i], "require_membership_of", strlen("require_membership_of")) == 0) || (strncmp(argv[i], "require-membership-of", strlen("require-membership-of")) == 0)) { char *p; char *parm = strdup(argv[i]); if ( (p = strchr( parm, '=' )) == NULL) { _pam_log(LOG_INFO, "no \"=\" delimiter for \"require_membership_of\" found\n"); break; } member = strdup(p+1); } } /* Now use the username to look up password */ return winbind_auth_request(username, password, member, ctrl); }
static int set_filter(pam_handle_t *pamh, int flags, int ctrl , const char **evp, const char *filtername) { int status=-1; char terminal[TERMINAL_LEN]; struct termio stored_mode; /* initial terminal mode settings */ int fd[2], child=0, child2=0, aterminal; if (filtername == NULL || *filtername != '/') { _pam_log(LOG_ALERT, "filtername not permitted; require full path"); return PAM_ABORT; } if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) { aterminal = 0; } else { aterminal = 1; } if (aterminal) { /* open the master pseudo terminal */ fd[0] = master(terminal); if (fd[0] < 0) { _pam_log(LOG_CRIT,"no master terminal"); return PAM_AUTH_ERR; } /* set terminal into raw mode.. remember old mode so that we can revert to it after the child has quit. */ /* this is termio terminal handling... */ if (ioctl(STDIN_FILENO, TCGETA, (char *) &stored_mode ) < 0) { /* in trouble, so close down */ close(fd[0]); _pam_log(LOG_CRIT, "couldn't copy terminal mode"); return PAM_ABORT; } else { struct termio t_mode = stored_mode; t_mode.c_iflag = 0; /* no input control */ t_mode.c_oflag &= ~OPOST; /* no ouput post processing */ /* no signals, canonical input, echoing, upper/lower output */ t_mode.c_lflag &= ~(ISIG|ICANON|ECHO|XCASE); t_mode.c_cflag &= ~(CSIZE|PARENB); /* no parity */ t_mode.c_cflag |= CS8; /* 8 bit chars */ t_mode.c_cc[VMIN] = 1; /* number of chars to satisfy a read */ t_mode.c_cc[VTIME] = 0; /* 0/10th second for chars */ if (ioctl(STDIN_FILENO, TCSETA, (char *) &t_mode) < 0) { close(fd[0]); _pam_log(LOG_WARNING, "couldn't put terminal in RAW mode"); return PAM_ABORT; } /* * NOTE: Unlike the stream socket case here the child * opens the slave terminal as fd[1] *after* the fork... */ } } else { /* * not a terminal line so just open a stream socket fd[0-1] * both set... */ if ( socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0 ) { _pam_log(LOG_CRIT,"couldn't open a stream pipe"); return PAM_ABORT; } } /* start child process */ if ( (child = fork()) < 0 ) { _pam_log(LOG_WARNING,"first fork failed"); if (aterminal) { (void) ioctl(STDIN_FILENO, TCSETA, (char *) &stored_mode); } return PAM_AUTH_ERR; } if ( child == 0 ) { /* child process *is* application */ if (aterminal) { /* close the controlling tty */ #if defined(__hpux) && defined(O_NOCTTY) int t = open("/dev/tty", O_RDWR|O_NOCTTY); #else int t = open("/dev/tty",O_RDWR); if (t > 0) { (void) ioctl(t, TIOCNOTTY, NULL); close(t); } #endif /* defined(__hpux) && defined(O_NOCTTY) */ /* make this process it's own process leader */ if (setsid() == -1) { _pam_log(LOG_WARNING,"child cannot become new session"); return PAM_ABORT; } /* find slave's name */ terminal[5] = 't'; /* want to open slave terminal */ fd[1] = open(terminal, O_RDWR); close(fd[0]); /* process is the child -- uses line fd[1] */ if (fd[1] < 0) { _pam_log(LOG_WARNING,"cannot open slave terminal; %s" ,terminal); return PAM_ABORT; } /* initialize the child's terminal to be the way the parent's was before we set it into RAW mode */ if (ioctl(fd[1], TCSETA, (char *) &stored_mode) < 0) { _pam_log(LOG_WARNING,"cannot set slave terminal mode; %s" ,terminal); close(fd[1]); return PAM_ABORT; } } else { /* nothing to do for a simple stream socket */ } /* re-assign the stdin/out to fd[1] <- (talks to filter). */ if ( dup2(fd[1],STDIN_FILENO) != STDIN_FILENO || dup2(fd[1],STDOUT_FILENO) != STDOUT_FILENO || dup2(fd[1],STDERR_FILENO) != STDERR_FILENO ) { _pam_log(LOG_WARNING ,"unable to re-assign STDIN/OUT/ERR...'s"); close(fd[1]); return PAM_ABORT; } /* make sure that file descriptors survive 'exec's */ if ( fcntl(STDIN_FILENO, F_SETFD, 0) || fcntl(STDOUT_FILENO,F_SETFD, 0) || fcntl(STDERR_FILENO,F_SETFD, 0) ) { _pam_log(LOG_WARNING ,"unable to re-assign STDIN/OUT/ERR...'s"); return PAM_ABORT; } /* now the user input is read from the parent/filter: forget fd */ close(fd[1]); /* the current process is now aparently working with filtered stdio/stdout/stderr --- success! */ return PAM_SUCCESS; } /* * process is the parent here. So we can close the application's * input/output */ close(fd[1]); /* Clear out passwords... there is a security problem here in * that this process never executes pam_end. Consequently, any * other sensitive data in this process is *not* explicitly * overwritten, before the process terminates */ (void) pam_set_item(pamh, PAM_AUTHTOK, NULL); (void) pam_set_item(pamh, PAM_OLDAUTHTOK, NULL); /* fork a copy of process to run the actual filter executable */ if ( (child2 = fork()) < 0 ) { _pam_log(LOG_WARNING,"filter fork failed"); child2 = 0; } else if ( child2 == 0 ) { /* exec the child filter */ if ( dup2(fd[0],APPIN_FILENO) != APPIN_FILENO || dup2(fd[0],APPOUT_FILENO) != APPOUT_FILENO || dup2(fd[0],APPERR_FILENO) != APPERR_FILENO ) { _pam_log(LOG_WARNING ,"unable to re-assign APPIN/OUT/ERR...'s"); close(fd[0]); exit(1); } /* make sure that file descriptors survive 'exec's */ if ( fcntl(APPIN_FILENO, F_SETFD, 0) == -1 || fcntl(APPOUT_FILENO,F_SETFD, 0) == -1 || fcntl(APPERR_FILENO,F_SETFD, 0) == -1 ) { _pam_log(LOG_WARNING ,"unable to retain APPIN/OUT/ERR...'s"); close(APPIN_FILENO); close(APPOUT_FILENO); close(APPERR_FILENO); exit(1); } /* now the user input is read from the parent through filter */ execle(filtername, "<pam_filter>", NULL, evp); /* getting to here is an error */ _pam_log(LOG_ALERT, "filter: %s, not executable", filtername); } else { /* wait for either of the two children to exit */ while (child && child2) { /* loop if there are two children */ int lstatus=0; int chid; chid = wait(&lstatus); if (chid == child) { if (WIFEXITED(lstatus)) { /* exited ? */ status = WEXITSTATUS(lstatus); } else if (WIFSIGNALED(lstatus)) { /* killed ? */ status = -1; } else continue; /* just stopped etc.. */ child = 0; /* the child has exited */ } else if (chid == child2) { /* * if the filter has exited. Let the child die * naturally below */ if (WIFEXITED(lstatus) || WIFSIGNALED(lstatus)) child2 = 0; } else { _pam_log(LOG_ALERT ,"programming error <chid=%d,lstatus=%x>: " __FILE__ " line %d" , lstatus, __LINE__ ); child = child2 = 0; status = -1; } } } close(fd[0]); /* if there is something running, wait for it to exit */ while (child || child2) { int lstatus=0; int chid; chid = wait(&lstatus); if (child && chid == child) { if (WIFEXITED(lstatus)) { /* exited ? */ status = WEXITSTATUS(lstatus); } else if (WIFSIGNALED(lstatus)) { /* killed ? */ status = -1; } else continue; /* just stopped etc.. */ child = 0; /* the child has exited */ } else if (child2 && chid == child2) { if (WIFEXITED(lstatus) || WIFSIGNALED(lstatus)) child2 = 0; } else { _pam_log(LOG_ALERT ,"programming error <chid=%d,lstatus=%x>: " __FILE__ " line %d" , lstatus, __LINE__ ); child = child2 = 0; status = -1; } } if (aterminal) { /* reset to initial terminal mode */ (void) ioctl(STDIN_FILENO, TCSETA, (char *) &stored_mode); } if (ctrl & FILTER_DEBUG) { _pam_log(LOG_DEBUG,"parent process exited"); /* clock off */ } /* quit the parent process, returning the child's exit status */ exit(status); }
/* 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; int status = PAM_AUTH_ERR; int seq = 0; user = pass = tty = r_addr = NULL; 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: user [%s] obtained", __FUNCTION__, user); /* uwzgledniac PAM_DISALLOW_NULL_AUTHTOK */ retval = tacacs_get_password (pamh, flags, ctrl, &pass); if (retval != PAM_SUCCESS || pass == NULL || *pass == '\0') { _pam_log (LOG_ERR, "unable to obtain password"); return PAM_CRED_INSUFFICIENT; } retval = pam_set_item (pamh, PAM_AUTHTOK, pass); if (retval != PAM_SUCCESS) { _pam_log (LOG_ERR, "unable to set password"); return PAM_CRED_INSUFFICIENT; } if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: password obtained", __FUNCTION__); tty = _pam_get_terminal(pamh); if (!strncmp (tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tty [%s] obtained", __FUNCTION__, tty); r_addr = _pam_get_rhost(pamh); if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: rhost [%s] obtained", __FUNCTION__, r_addr); /* Attempt server connect */ for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { status = TAC_PLUS_AUTHEN_STATUS_FAIL; if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i ); tac_fd = tac_connect_single(tac_srv[srv_i], tac_srv_key[srv_i]); if (tac_fd < 0) { _pam_log (LOG_ERR, "connection failed srv %d: %m", srv_i); if (srv_i == tac_srv_no-1) { _pam_log (LOG_ERR, "no more servers to connect"); return PAM_AUTHINFO_UNAVAIL; } continue; } /* Send AUTHEN/START */ if (tac_authen_send(tac_fd, user, pass, tty, r_addr, TAC_PLUS_AUTHEN_LOGIN, ctrl) < 0) { _pam_log (LOG_ERR, "error sending auth req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } else { /* Read AUTHEN/REPLY and act on status */ struct msg_status *msgstatus = malloc(sizeof(msg_status)); do { tac_authen_read(msgstatus, tac_fd, ctrl, &seq); status = msgstatus->status; switch (status) { case TAC_PLUS_AUTHEN_STATUS_GETPASS: /* AUTHEN/CONT with password */ if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, pass, ctrl, seq+1) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_MAXTRIES; } break; case TAC_PLUS_AUTHEN_STATUS_GETDATA: { /* The only GETDATA request should be if the user's password * has expired and ACS is requesting a new password * * Check if a conversation function has been set and either * return a PAM_AUTHTOK_EXPIRED or start a conversation * with the user for to change the password */ if (!strcmp(tty,"http")) { status = PAM_NEW_AUTHTOK_REQD; if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: expired", __FUNCTION__); } else { /* Get data from user with pam conversation */ struct pam_message msg; struct pam_response *resp = NULL; int retval; char *user_data = NULL; /* set up conversation call */ msg.msg_style = PAM_PROMPT_ECHO_OFF; msg.msg = malloc(100); strcpy((char *)msg.msg,msgstatus->server_msg); if ((retval = converse (pamh, 1, &msg, &resp)) != PAM_SUCCESS) { status = PAM_AUTHINFO_UNAVAIL; } else { if (resp != NULL) { if (resp->resp == NULL && (ctrl & PAM_TAC_DEBUG)) _pam_log (LOG_DEBUG, "pam_sm_authenticate: NULL given by user for GETDATA request"); user_data = resp->resp; resp->resp = NULL; } else { if (ctrl & PAM_TAC_DEBUG) { _pam_log (LOG_DEBUG, "pam_sm_authenticate: no error reported"); _pam_log (LOG_DEBUG, "getting data from user - NULL returned!?"); } return PAM_CONV_ERR; } /* AUTHEN/CONT with data */ if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, user_data, ctrl, seq+1) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } } free(msg.msg); } } break; case TAC_PLUS_AUTHEN_STATUS_GETUSER: /* AUTHEN/CONT with username */ if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, user, ctrl, seq+1) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } break; } } while ( (status == TAC_PLUS_AUTHEN_STATUS_GETDATA) || (status == TAC_PLUS_AUTHEN_STATUS_GETPASS) || (status == TAC_PLUS_AUTHEN_STATUS_GETUSER) ); if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: out of while loop status=%d", __FUNCTION__,status); if (status == TAC_PLUS_AUTHEN_STATUS_PASS) { /* OK, we got authenticated; save the server that accepted us for pam_sm_acct_mgmt and exit the loop */ status = PAM_SUCCESS; active_server = tac_srv[srv_i]; active_key = tac_srv_key[srv_i]; } else if (status != PAM_NEW_AUTHTOK_REQD) { _pam_log (LOG_ERR, "auth failed: %d", status); status = PAM_AUTH_ERR; } } close(tac_fd); /* TODO: Allow time for tac server to reply * TODO: Check if reply received before connecting to next server */ } if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: exit with pam status: %i", __FUNCTION__, status); bzero (pass, strlen (pass)); free(pass); pass = NULL; return status; } /* pam_sm_authenticate */
static int pam_iruserok(pam_handle_t *pamh, struct _options *opts, U32 raddr, int superuser, const char *ruser, const char *luser, const char *rhost) { const char *cp; struct stat sbuf; struct passwd *pwd; FILE *hostf; uid_t uid; int answer; char pbuf[MAXPATHLEN]; /* potential buffer overrun */ if ((!superuser||opts->opt_hosts_equiv_rootok) && !opts->opt_no_hosts_equiv ) { /* try to open system hosts.equiv file */ hostf = fopen (_PATH_HEQUIV, "r"); if (hostf) { answer = __ivaliduser(pamh, opts, hostf, raddr, luser , ruser, rhost); (void) fclose(hostf); if (answer == 0) return 0; /* remote host is equivalent to localhost */ } /* else { No hosts.equiv file on system. } */ } if ( opts->opt_no_rhosts ) return 1; /* * Identify user's local .rhosts file */ pwd = _pammodutil_getpwnam(pamh, luser); if (pwd == NULL) { /* * luser is assumed to be valid because of an earlier check for uid = 0 * we don't log this error twice. However, this shouldn't happen ! * --cristiang */ return(1); } /* check for buffer overrun */ if (strlen(pwd->pw_dir) + sizeof(USER_RHOSTS_FILE) + 2 >= MAXPATHLEN) { if (opts->opt_debug) _pam_log(LOG_DEBUG,"home directory for `%s' is too long", luser); return 1; /* to dangerous to try */ } (void) strcpy(pbuf, pwd->pw_dir); (void) strcat(pbuf, USER_RHOSTS_FILE); /* * Change effective uid while _reading_ .rhosts. (not just * opening). If root and reading an NFS mounted file system, * can't read files that are 0600 as .rhosts files should be. */ /* We are root, this will not fail */ #ifdef linux /* If we are on linux the better way is setfsuid */ uid = setfsuid(pwd->pw_uid); hostf = fopen(pbuf, "r"); #else uid = geteuid(); (void) seteuid(pwd->pw_uid); hostf = fopen(pbuf, "r"); #endif if (hostf == NULL) { if (opts->opt_debug) _pam_log(LOG_DEBUG,"Could not open %s file",pbuf); answer = 1; goto exit_function; } /* * If not a regular file, or is owned by someone other than * user or root or if writeable by anyone but the owner, quit. */ cp = NULL; if (lstat(pbuf, &sbuf) < 0 || !S_ISREG(sbuf.st_mode)) cp = ".rhosts not regular file"; else if (fstat(fileno(hostf), &sbuf) < 0) cp = ".rhosts fstat failed"; else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) cp = "bad .rhosts owner"; else if (sbuf.st_mode & S_IWOTH) cp = ".rhosts writable by other!"; else if (sbuf.st_mode & S_IWGRP) { /* private group caveat */ if (opts->opt_private_group) { struct group *grp = getgrgid(sbuf.st_gid); if (NULL == grp || NULL == grp->gr_name || strcmp(luser,grp->gr_name)) { cp = ".rhosts writable by public group"; } else if (grp->gr_mem) { int gcount; /* require at most one member (luser) of this group */ for (gcount=0; grp->gr_mem[gcount]; ++gcount) { if (strcmp(grp->gr_mem[gcount], luser)) { gcount = -1; break; } } if (gcount < 0) { cp = ".rhosts writable by other members of group"; } } } else { cp = ".rhosts writable by group"; } } /* It is _NOT_ safe to append an else here... Do so prior to * S_IWGRP check */ /* If there were any problems, quit. */ if (cp) { opts->last_error = cp; answer = 1; goto exit_function; } answer = __ivaliduser (pamh, opts, hostf, raddr, luser, ruser, rhost); exit_function: /* * Go here to exit after the fsuid/euid has been adjusted so that * they are reset before we exit. */ #ifdef linux setfsuid(uid); #else (void)seteuid(uid); #endif if (hostf != NULL) (void) fclose(hostf); return answer; }
static int _pam_auth_rhosts (pam_handle_t *pamh, int flags, int argc, const char **argv) { int retval; const char *luser = NULL; const char *ruser = NULL, *rhost = NULL; struct _options opts; int as_root = 0; /* * Look at the options and set the flags accordingly. */ memset (&opts, 0, sizeof (opts)); set_parameters (&opts, flags, argc, argv); /* * Obtain the parameters for the various items */ for (;;) { /* abuse loop to avoid goto */ /* get the remotehost */ D(("getting rhost")); retval = pam_get_rhost(pamh, &rhost, NULL); (void) pam_set_item(pamh, PAM_RHOST, rhost); if (retval != PAM_SUCCESS) { if (opts.opt_debug) { _pam_log(LOG_DEBUG, "could not get the remote host name"); } break; } /* get the remote user */ D(("getting ruser")); retval = pam_get_ruser(pamh, &ruser, NULL); (void) pam_set_item(pamh, PAM_RUSER, ruser); if (retval != PAM_SUCCESS) { if (opts.opt_debug) _pam_log(LOG_DEBUG, "could not get the remote username"); break; } /* get the local user */ D(("getting user")); retval = pam_get_user(pamh, &luser, NULL); if (retval != PAM_SUCCESS) { if (opts.opt_debug) _pam_log(LOG_DEBUG, "could not determine name of local user"); break; } if (opts.superuser && !strcmp(opts.superuser, luser)) { as_root = 1; } /* check if the luser uid == 0... --cristiang */ if (! opts.opt_no_uid_check) { struct passwd *luser_pwd; luser_pwd = _pammodutil_getpwnam(pamh, luser); if (luser_pwd == NULL) { if (opts.opt_debug) _pam_log(LOG_DEBUG, "user '%s' unknown to this system", luser); retval = PAM_AUTH_ERR; break; } if (luser_pwd->pw_uid == 0) as_root = 1; luser_pwd = NULL; /* forget */ } /* * Validate the account information. */ if (pam_ruserok (pamh, &opts, rhost, as_root, ruser, luser) != 0) { if ( !opts.opt_suppress ) { _pam_log(LOG_WARNING, "denied to %s@%s as %s: %s", ruser, rhost, luser, (opts.last_error==NULL) ? "access not allowed":opts.last_error); } retval = PAM_AUTH_ERR; } else { _pam_log(LOG_NOTICE, "allowed to %s@%s as %s", ruser, rhost, luser); } break; } return retval; }
/* no-op function for future use */ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, const char **argv) { int ctrl, retval; char *user; char *pass; char *tty; char *r_addr; const void *pam_pass = NULL; 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); syslog(LOG_DEBUG, "%s(flags=%d, argc=%d)", __func__, flags, argc); if ( (pam_get_item(pamh, PAM_OLDAUTHTOK, &pam_pass) == PAM_SUCCESS) && (pam_pass != NULL) ) { if ((pass = strdup(pam_pass)) == NULL) return PAM_BUF_ERR; } else { pass = strdup(""); } 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); 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); if (PAM_SILENT == (flags & PAM_SILENT)) { status = PAM_AUTHTOK_ERR; goto finish; } status = PAM_TRY_AGAIN; 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 (PAM_PRELIM_CHECK == (flags & PAM_PRELIM_CHECK)) { if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG)) syslog(LOG_DEBUG, "%s: finishing PAM_PRELIM_CHECK with srv %d", __FUNCTION__, srv_i); close(tac_fd); status = PAM_SUCCESS; goto finish; } if (tac_authen_send(tac_fd, user, "", tty, r_addr, TAC_PLUS_AUTHEN_CHPASS) < 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; active_server.addr = tac_srv[srv_i].addr; active_server.key = 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_AUTHTOK_ERR; communicating = 0; _pam_log(LOG_ERR, "chauthtok 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"); communicating = 0; } } else { _pam_log(LOG_WARNING, "%s: error sending msg=\"%s\", retval=%d", __FUNCTION__, conv_msg.msg, retval); communicating = 0; } } else { syslog(LOG_ERR, "GETDATA response with no message, returning PAM_TRY_AGAIN"); 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: calling tac_cont_send", __FUNCTION__); if (tac_cont_send(tac_fd, pass) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); communicating = 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 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) { free(resp->resp); free(resp); } free(re.msg); } /* end while(communicating) */ close(tac_fd); if (status == PAM_SUCCESS || status == PAM_AUTHTOK_ERR) break; } finish: if (status != PAM_SUCCESS && status != PAM_AUTHTOK_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)); free(pass); pass = NULL; } return status; } /* pam_sm_chauthtok */ #endif #ifdef PAM_STATIC struct pam_module _pam_tacplus_modstruct { "pam_tacplus", pam_sm_authenticate, pam_sm_setcred, pam_sm_acct_mgmt, pam_sm_open_session, pam_sm_close_session, #ifdef PAM_SM_PASSWORD pam_sm_chauthtok #else NULL #endif };
/* 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); retval = tacacs_get_password (pamh, flags, ctrl, &pass); if (retval != PAM_SUCCESS || pass == NULL || *pass == '\0') { _pam_log(LOG_ERR, "unable to obtain password"); return PAM_CRED_INSUFFICIENT; } retval = pam_set_item (pamh, PAM_AUTHTOK, pass); if (retval != PAM_SUCCESS) { _pam_log(LOG_ERR, "unable to set password"); 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; active_server.addr = tac_srv[srv_i].addr; active_server.key = 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) { free(resp->resp); free(resp); } free(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)); free(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; /* 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); 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 == NULL || !*tac_protocol) { _pam_log (LOG_ERR, "SM: TACACS+ protocol type not configured (IGNORED)"); } tac_add_attrib(&attr, "service", tac_service); if(tac_protocol != NULL && 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) 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) 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) free (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) free (arep.msg); close(tac_fd); return status; } /* pam_sm_acct_mgmt */
static int exec_file(pam_handle_t *pamh, char **argv, const char *logfile) { pid_t pid, rc; int p[2]; char buf[1024]; long ttl; time_t start; int i, status, intr; fd_set rd; struct timeval tv; size_t total = 0; if (pipe(p)) { _pam_log(LOG_ERR, "pipe: %s", strerror(errno)); return PAM_SYSTEM_ERR; } pid = fork(); if (pid == -1) { close(p[0]); close(p[1]); _pam_log(LOG_ERR, "fork: %s", strerror(errno)); return PAM_SYSTEM_ERR; } if (pid == 0) { /* child */ if (dup2(p[1], 1) == -1) { _pam_log(LOG_ERR, "dup2: %s", strerror(errno)); _exit(127); } for (i = sysconf(_SC_OPEN_MAX); i >= 0; i--) { if (i != 1) close(i); } open("/dev/null", O_RDONLY); if (logfile) { if (open(logfile, O_CREAT|O_APPEND|O_WRONLY, 0644) == -1) { _pam_log(LOG_ERR, "open(%s): %s", logfile, strerror(errno)); _exit(127); } } else dup2(1, 2); execv(argv[0], argv); _exit(127); } /* master */ close(p[1]); start = time(NULL); intr = 0; rc = 0; status = 0; for (i = 0; total < max_output_size;) { FD_ZERO(&rd); FD_SET(p[0], &rd); if (intr) { rc = waitpid(pid, &status, WNOHANG); if (rc == pid) break; if (rc == (pid_t)-1) { _pam_log(LOG_ERR, "waitpid: %s", strerror(errno)); break; } intr = 0; } ttl = timeout_option - (time(NULL) - start); if (ttl <= 0) { _pam_log(LOG_ERR, "timed out reading from %s", argv[0]); break; } tv.tv_sec = ttl; tv.tv_usec = 0; rc = select(p[0] + 1, &rd, NULL, NULL, &tv); if (rc < 0) { if (errno == EINTR || errno == EAGAIN) { intr = 1; continue; } _pam_log(LOG_ERR, "select: %s", strerror(errno)); } if (i == sizeof(buf) - 1) { char *p; buf[i] = 0; p = strrchr(buf, '\n'); if (p) *p++ = 0; pam_info(pamh, "%s", buf); if (p && *p) { i = strlen(p); memmove(buf, p, i); } } if (FD_ISSET(p[0], &rd)) { char c; rc = read(p[0], &c, 1); if (rc == 1) { buf[i++] = c; total++; } else if (rc == 0 || errno == EINTR || errno == EAGAIN) { intr = 1; continue; } else { _pam_log(LOG_ERR, "read: %s", strerror(errno)); break; } } } if (i) { buf[i] = 0; pam_info(pamh, "%s", buf); } close(p[0]); if (rc != pid) { _pam_log(LOG_NOTICE, "killing %s (pid %lu)", argv[0], (unsigned long) pid); kill(pid, SIGKILL); while ((rc = waitpid(pid, &status, 0)) == -1 && errno == EINTR); if (rc == (pid_t)-1) { _pam_log(LOG_ERR, "waitpid: %s", strerror(errno)); return PAM_SYSTEM_ERR; } } else if (WIFEXITED(status)) { status = WEXITSTATUS(status); if (status) { _pam_log(LOG_ERR, "%s exited with status %d", argv[0], status); return PAM_SYSTEM_ERR; } } else if (WIFSIGNALED(status)) { status = WTERMSIG(status); _pam_log(LOG_ERR, "%s got signal %d", argv[0], status); return PAM_SYSTEM_ERR; } else if (status) { _pam_log(LOG_ERR, "%s failed: unknown status 0x%x", argv[0], status); return PAM_SYSTEM_ERR; } return PAM_SUCCESS; }
/* 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); /* uwzgledniac PAM_DISALLOW_NULL_AUTHTOK */ retval = tacacs_get_password (pamh, flags, ctrl, &pass); if (retval != PAM_SUCCESS || pass == NULL || *pass == '\0') { _pam_log(LOG_ERR, "unable to obtain password"); return PAM_CRED_INSUFFICIENT; } retval = pam_set_item (pamh, PAM_AUTHTOK, pass); if (retval != PAM_SUCCESS) { _pam_log(LOG_ERR, "unable to set password"); 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); 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) < 0) { close(tac_fd); _pam_log(LOG_ERR, "error sending auth req to TACACS+ server"); continue; } communicating = 1; while (communicating) { msg = tac_authen_read(tac_fd); /* 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"); status = PAM_SUCCESS; communicating = 0; active_server.addr = tac_srv[srv_i].addr; active_server.key = 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: /* forget it */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_FAIL"); status = PAM_AUTH_ERR; communicating = 0; _pam_log(LOG_ERR, "auth failed: %d", msg); break; case TAC_PLUS_AUTHEN_STATUS_GETDATA: /* not implemented */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETDATA"); 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; 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 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); } } /* 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); bzero(pass, strlen (pass)); free(pass); pass = NULL; return status; } /* pam_sm_authenticate */
static int process_args(pam_handle_t *pamh , int argc, const char **argv, const char *type , char ***evp, const char **filtername) { int ctrl=0; while (argc-- > 0) { if (strcmp("debug",*argv) == 0) { ctrl |= FILTER_DEBUG; } else if (strcmp("new_term",*argv) == 0) { ctrl |= NEW_TERM; } else if (strcmp("non_term",*argv) == 0) { ctrl |= NON_TERM; } else if (strcmp("run1",*argv) == 0) { ctrl |= FILTER_RUN1; if (argc <= 0) { _pam_log(LOG_ALERT,"no run filter supplied"); } else break; } else if (strcmp("run2",*argv) == 0) { ctrl |= FILTER_RUN2; if (argc <= 0) { _pam_log(LOG_ALERT,"no run filter supplied"); } else break; } else { _pam_log(LOG_ERR, "unrecognized option: %s (ignored)", *argv); } ++argv; /* step along list */ } if (argc < 0) { /* there was no reference to a filter */ *filtername = NULL; *evp = NULL; } else { char **levp; const char *tmp; int i,size; *filtername = *++argv; if (ctrl & FILTER_DEBUG) { _pam_log(LOG_DEBUG,"will run filter %s\n", *filtername); } levp = (char **) malloc(5*sizeof(char *)); if (levp == NULL) { _pam_log(LOG_CRIT,"no memory for environment of filter"); return -1; } for (size=i=0; i<argc; ++i) { size += strlen(argv[i])+1; } /* the "ARGS" variable */ #define ARGS_OFFSET 5 /* sizeof("ARGS="); */ #define ARGS_NAME "ARGS=" size += ARGS_OFFSET; levp[0] = (char *) malloc(size); if (levp[0] == NULL) { _pam_log(LOG_CRIT,"no memory for filter arguments"); if (levp) { free(levp); } return -1; } strncpy(levp[0],ARGS_NAME,ARGS_OFFSET); for (i=0,size=ARGS_OFFSET; i<argc; ++i) { strcpy(levp[0]+size, argv[i]); size += strlen(argv[i]); levp[0][size++] = ' '; } levp[0][--size] = '\0'; /* <NUL> terminate */ /* the "SERVICE" variable */ #define SERVICE_OFFSET 8 /* sizeof("SERVICE="); */ #define SERVICE_NAME "SERVICE=" pam_get_item(pamh, PAM_SERVICE, (const void **)&tmp); size = SERVICE_OFFSET+strlen(tmp); levp[1] = (char *) malloc(size+1); if (levp[1] == NULL) { _pam_log(LOG_CRIT,"no memory for service name"); if (levp) { free(levp[0]); free(levp); } return -1; } strncpy(levp[1],SERVICE_NAME,SERVICE_OFFSET); strcpy(levp[1]+SERVICE_OFFSET, tmp); levp[1][size] = '\0'; /* <NUL> terminate */ /* the "USER" variable */ #define USER_OFFSET 5 /* sizeof("USER="******"USER="******"<unknown>"; } size = USER_OFFSET+strlen(tmp); levp[2] = (char *) malloc(size+1); if (levp[2] == NULL) { _pam_log(LOG_CRIT,"no memory for user's name"); if (levp) { free(levp[1]); free(levp[0]); free(levp); } return -1; } strncpy(levp[2],USER_NAME,USER_OFFSET); strcpy(levp[2]+USER_OFFSET, tmp); levp[2][size] = '\0'; /* <NUL> terminate */ /* the "USER" variable */ #define TYPE_OFFSET 5 /* sizeof("TYPE="); */ #define TYPE_NAME "TYPE=" size = TYPE_OFFSET+strlen(type); levp[3] = (char *) malloc(size+1); if (levp[3] == NULL) { _pam_log(LOG_CRIT,"no memory for type"); if (levp) { free(levp[2]); free(levp[1]); free(levp[0]); free(levp); } return -1; } strncpy(levp[3],TYPE_NAME,TYPE_OFFSET); strcpy(levp[3]+TYPE_OFFSET, type); levp[3][size] = '\0'; /* <NUL> terminate */ levp[4] = NULL; /* end list */ *evp = levp; } if ((ctrl & FILTER_DEBUG) && *filtername) { char **e; _pam_log(LOG_DEBUG,"filter[%s]: %s",type,*filtername); _pam_log(LOG_DEBUG,"environment:"); for (e=*evp; e && *e; ++e) { _pam_log(LOG_DEBUG," %s",*e); } } return ctrl; }
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; typemsg = tac_acct_flag2str(type); ctrl = _pam_parse (argc, argv); if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: [%s] called (pam_tacplus v%u.%u.%u)" , __FUNCTION__, typemsg, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); if (ctrl & PAM_TAC_DEBUG) _pam_log(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) _pam_log(LOG_DEBUG, "%s: username [%s] obtained", __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 [%s] obtained", __FUNCTION__, tty); r_addr = _pam_get_rhost(pamh); if (ctrl & PAM_TAC_DEBUG) _pam_log(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 == 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; } /* 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(!(ctrl & PAM_TAC_ACCT)) { /* normal mode, send packet to the first available server */ int srv_i = 0; status = PAM_SESSION_ERR; while ((status == PAM_SESSION_ERR) && (srv_i < tac_srv_no)) { int tac_fd; tac_fd = tac_connect_single(tac_srv[srv_i], tac_srv_key[srv_i]); if(tac_fd < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (fd)", __FUNCTION__, typemsg); srv_i++; continue; } if (ctrl & PAM_TAC_DEBUG) _pam_log(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); /* return code from function in this mode is status of the last server we tried to send packet to */ if(retval < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (acct)", __FUNCTION__, typemsg); } else { status = PAM_SUCCESS; if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: [%s] for [%s] sent", __FUNCTION__, typemsg,user); } close(tac_fd); srv_i++; } } else { /* send packet to all servers specified */ int srv_i; status = PAM_SESSION_ERR; for(srv_i = 0; srv_i < tac_srv_no; srv_i++) { int tac_fd; tac_fd = tac_connect_single(tac_srv[srv_i], tac_srv_key[srv_i]); if(tac_fd < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (fd)", __FUNCTION__, typemsg); continue; } if (ctrl & PAM_TAC_DEBUG) _pam_log(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); /* return code from function in this mode is status of the last server we tried to send packet to */ if(retval < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (acct)", __FUNCTION__, typemsg); } else { status = PAM_SUCCESS; if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: [%s] for [%s] sent", __FUNCTION__, typemsg,user); } close(tac_fd); } } /* acct mode */ if(type == TAC_PLUS_ACCT_FLAG_STOP) { signal(SIGALRM, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); } return status; }
/* 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; int status = PAM_AUTH_ERR; 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); /* uwzgledniac PAM_DISALLOW_NULL_AUTHTOK */ retval = tacacs_get_password (pamh, flags, ctrl, &pass); if (retval != PAM_SUCCESS || pass == NULL || *pass == '\0') { _pam_log (LOG_ERR, "unable to obtain password"); return PAM_CRED_INSUFFICIENT; } retval = pam_set_item (pamh, PAM_AUTHTOK, pass); if (retval != PAM_SUCCESS) { _pam_log (LOG_ERR, "unable to set password"); 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); for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { int msg = TAC_PLUS_AUTHEN_STATUS_FAIL; if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i ); tac_fd = tac_connect_single(tac_srv[srv_i], tac_srv_key[srv_i]); if (tac_fd < 0) { _pam_log (LOG_ERR, "connection failed srv %d: %m", srv_i); if (srv_i == tac_srv_no-1) { _pam_log (LOG_ERR, "no more servers to connect"); return PAM_AUTHINFO_UNAVAIL; } continue; } if (tac_authen_send(tac_fd, user, pass, tty, r_addr) < 0) { _pam_log (LOG_ERR, "error sending auth req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } else { msg = tac_authen_read(tac_fd); if (msg == 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"); status = PAM_AUTHINFO_UNAVAIL; } else { msg = tac_authen_read(tac_fd); if (msg != TAC_PLUS_AUTHEN_STATUS_PASS) { _pam_log (LOG_ERR, "auth failed: %d", msg); status = PAM_AUTH_ERR; } else { /* OK, we got authenticated; save the server that accepted us for pam_sm_acct_mgmt and exit the loop */ status = PAM_SUCCESS; active_server = tac_srv[srv_i]; active_key = tac_srv_key[srv_i]; close(tac_fd); break; } } } else if (msg != TAC_PLUS_AUTHEN_STATUS_PASS) { _pam_log (LOG_ERR, "auth failed: %d", msg); status = PAM_AUTH_ERR; } else { /* OK, we got authenticated; save the server that accepted us for pam_sm_acct_mgmt and exit the loop */ status = PAM_SUCCESS; active_server = tac_srv[srv_i]; active_key = tac_srv_key[srv_i]; close(tac_fd); break; } } close(tac_fd); } if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: exit with pam status: %i", __FUNCTION__, status); bzero (pass, strlen (pass)); free(pass); pass = NULL; return status; } /* pam_sm_authenticate */
/* 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 */
/* expected hook for auth service */ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { int retval, pcnt, pwdfilename_found; const char *name; char *password; char pwdfilename[PWDFN_LEN]; char salt[12], stored_crypted_password[CRYPTED_BCPWD_LEN+1]; char *crypted_password; FILE *pwdfile; int use_flock = 0; int use_delay = 1; int temp_result = 0; /* we require the pwdfile switch and argument to be present, else we don't work */ /* pcnt is the parameter counter variable for iterating through argv */ pcnt = pwdfilename_found = 0; do { /* see if the current parameter looks like "pwdfile" */ if (strcmp(argv[pcnt],PWDF_PARAM)==0) { /* if argv is long enough, grab the subsequent parameter */ if (pcnt+1 < argc) { /* make sure we can't overflow */ strncpy(pwdfilename,argv[++pcnt],PWDFN_LEN); /* indicate that we've found it */ pwdfilename_found = 1; } /* also check for "pwdfile=blah" */ } else if (strncmp(argv[pcnt],PWDF_PARAM "=",sizeof(PWDF_PARAM "=")-1)==0) { /* make sure we can't overflow */ strncpy(pwdfilename,argv[pcnt]+sizeof(PWDF_PARAM),PWDFN_LEN); /* indicate that we've found it */ pwdfilename_found = 1; } else if (strcmp(argv[pcnt],FLOCK_PARAM)==0) { /* we have a "flock" parameter */ use_flock = 1; } else if (strcmp(argv[pcnt],"no" FLOCK_PARAM)==0) { /* or a "noflock" parameter */ use_flock = 0; } else if (strcmp(argv[pcnt],NODELAY_PARAM)==0) { /* no delay on authentication failure */ use_delay = 0; } } while (++pcnt < argc); #ifdef HAVE_PAM_FAIL_DELAY if (use_delay) { D(("setting delay")); (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */ } #endif /* for some or other reason, the password file wasn't specified */ if (!pwdfilename_found) { _pam_log(LOG_ERR,"password file name not specified"); return PAM_AUTHINFO_UNAVAIL; } /* DEBUG */ D(_pam_log(LOG_ERR, "password filename extracted")); /* now try to open the password file */ if ((pwdfile=fopen(pwdfilename,"r"))==NULL) { _pam_log(LOG_ERR,"couldn't open password file %s",pwdfilename); return PAM_AUTHINFO_UNAVAIL; } /* set a lock on the password file */ if (use_flock && lock_fd(fileno(pwdfile)) == -1) { _pam_log(LOG_ERR,"couldn't lock password file %s",pwdfilename); return PAM_AUTHINFO_UNAVAIL; } /* get user name */ if ((retval = pam_get_user(pamh,&name,"login: "******"username not found"); fclose(pwdfile); return retval; } /* DEBUG */ D(_pam_log(LOG_ERR,"username is %s", name)); /* get password - code from pam_unix_auth.c */ pam_get_item(pamh, PAM_AUTHTOK, (void *)&password); if (!password) { retval = _set_auth_tok(pamh, flags, argc, argv); if (retval!=PAM_SUCCESS) { fclose(pwdfile); return retval; } } pam_get_item(pamh, PAM_AUTHTOK, (void *)&password); if ((retval = pam_get_item(pamh, PAM_AUTHTOK, (void *)&password)) != PAM_SUCCESS) { _pam_log(LOG_ERR, "auth token not found"); fclose(pwdfile); return retval; } /* DEBUG */ D(_pam_log(LOG_ERR,"got password from user", password)); /* now crypt password and compare to the user entry in the password file */ /* first make sure password is long enough -- may I do this? */ if (strlen(password)<2 || password==NULL) { _pam_log(LOG_ERR,"password too short or NULL"); fclose(pwdfile); return PAM_AUTH_ERR; } /* get the crypted password corresponding to this user */ if (!fgetpwnam(pwdfile, name, stored_crypted_password)) { _pam_log(LOG_ERR,"user not found in password database"); fclose(pwdfile); return PAM_AUTHINFO_UNAVAIL; } /* DEBUG */ D(_pam_log(LOG_ERR,"got crypted password == '%s'", stored_crypted_password)); temp_result = 0; /* Extract the salt and set the passwd length, depending on MD5 or DES */ if (strncmp(stored_crypted_password, "$1$", 3) == 0) { D(_pam_log(LOG_ERR,"password hash type is 'md5'")); /* get out the salt into "salt" */ strncpy(salt, stored_crypted_password, 11); salt[11] = '\0'; stored_crypted_password[CRYPTED_MD5PWD_LEN] = '\0'; /* try both md5 crypts */ crypted_password = Goodcrypt_md5(password, salt); if (strcmp(crypted_password, stored_crypted_password) == 0) { temp_result = 1; } else { crypted_password = Brokencrypt_md5(password, salt); if (strcmp(crypted_password, stored_crypted_password) == 0) { temp_result = 1; } } } else { /* get the salt out into "salt" */ strncpy(salt, stored_crypted_password, 2); salt[2] = '\0'; stored_crypted_password[CRYPTED_BCPWD_LEN] = '\0'; if (strlen(stored_crypted_password) <= CRYPTED_DESPWD_LEN) { D(_pam_log(LOG_ERR,"password hash type is 'crypt'")); crypted_password = crypt(password, salt); } else { D(_pam_log(LOG_ERR,"password hash type is 'bigcrypt'")); crypted_password = bigcrypt(password, salt); } if (strcmp(crypted_password, stored_crypted_password) == 0) { temp_result = 1; } } /* DEBUG */ D(_pam_log(LOG_ERR,"user password crypted is '%s'", crypted_password)); /* if things don't match up, complain */ if (!temp_result) { _pam_log(LOG_ERR,"wrong password for user %s",name); fclose(pwdfile); return PAM_AUTH_ERR; } /* DEBUG */ D(_pam_log(LOG_ERR,"passwords match")); /* we've gotten here, i.e. authentication was sucessful! */ fclose(pwdfile); return PAM_SUCCESS; }
static int _winbind_read_password(pam_handle_t * pamh ,unsigned int ctrl ,const char *comment ,const char *prompt1 ,const char *prompt2 ,const char **pass) { int authtok_flag; int retval; const char *item; char *token; /* * make sure nothing inappropriate gets returned */ *pass = token = NULL; /* * which authentication token are we getting? */ authtok_flag = on(WINBIND__OLD_PASSWORD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK; /* * should we obtain the password from a PAM item ? */ if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) { retval = pam_get_item(pamh, authtok_flag, (const void **) &item); if (retval != PAM_SUCCESS) { /* very strange. */ _pam_log(LOG_ALERT, "pam_get_item returned error to unix-read-password" ); return retval; } else if (item != NULL) { /* we have a password! */ *pass = item; item = NULL; return PAM_SUCCESS; } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) { return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */ } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl) && off(WINBIND__OLD_PASSWORD, ctrl)) { return PAM_AUTHTOK_RECOVER_ERR; } } /* * getting here implies we will have to get the password from the * user directly. */ { struct pam_message msg[3], *pmsg[3]; struct pam_response *resp; int i, replies; /* prepare to converse */ if (comment != NULL) { pmsg[0] = &msg[0]; msg[0].msg_style = PAM_TEXT_INFO; msg[0].msg = comment; i = 1; } else { i = 0; } pmsg[i] = &msg[i]; msg[i].msg_style = PAM_PROMPT_ECHO_OFF; msg[i++].msg = prompt1; replies = 1; if (prompt2 != NULL) { pmsg[i] = &msg[i]; msg[i].msg_style = PAM_PROMPT_ECHO_OFF; msg[i++].msg = prompt2; ++replies; } /* so call the conversation expecting i responses */ resp = NULL; retval = converse(pamh, i, pmsg, &resp); if (resp != NULL) { /* interpret the response */ if (retval == PAM_SUCCESS) { /* a good conversation */ token = x_strdup(resp[i - replies].resp); if (token != NULL) { if (replies == 2) { /* verify that password entered correctly */ if (!resp[i - 1].resp || strcmp(token, resp[i - 1].resp)) { _pam_delete(token); /* mistyped */ retval = PAM_AUTHTOK_RECOVER_ERR; _make_remark(pamh ,PAM_ERROR_MSG, MISTYPED_PASS); } } } else { _pam_log(LOG_NOTICE ,"could not recover authentication token"); } } /* * tidy up the conversation (resp_retcode) is ignored * -- what is it for anyway? AGM */ _pam_drop_reply(resp, i); } else { retval = (retval == PAM_SUCCESS) ? PAM_AUTHTOK_RECOVER_ERR : retval; } } if (retval != PAM_SUCCESS) { if (on(WINBIND_DEBUG_ARG, ctrl)) _pam_log(LOG_DEBUG, "unable to obtain a password"); return retval; } /* 'token' is the entered password */ /* we store this password as an item */ retval = pam_set_item(pamh, authtok_flag, token); _pam_delete(token); /* clean it up */ if (retval != PAM_SUCCESS || (retval = pam_get_item(pamh, authtok_flag ,(const void **) &item)) != PAM_SUCCESS) { _pam_log(LOG_CRIT, "error manipulating password"); return retval; } *pass = item; item = NULL; /* break link to password */ return PAM_SUCCESS; }
int readconfig(struct passwd *pwd, pam_handle_t * pamh, const char *user, char *path, char *targetpath, char *encfs_options, char *fuse_options) { FILE *conffile; char line[BUFSIZE]; char username[USERNAME_MAX]; int parsed; const char *tmp; // Return 1 = error, 2 = silent error (ie already mounted) if ((conffile = fopen(CONFIGFILE, "r")) == NULL) { _pam_log(LOG_ERR, "Failed to open conffile %s", CONFIGFILE); return 0; } while (fgets(line, BUFSIZE, conffile) != NULL) { if (line[0] == '#') continue; parsed = sscanf(line, "%s%s%s%s%s", username, path, targetpath, encfs_options, fuse_options); if (parsed == -1) continue; if (strcmp("drop_permissions", username) == 0) { drop_permissions = 1; continue; } if (strcmp("encfs_default", username) == 0) { if (parsed == 2 && !strcmp("-",path) == 0) strcpy(default_encfs_options, path); continue; } if (strcmp("fuse_default", username) == 0) { if (parsed == 2 && !strcmp("-",path) == 0) strcpy(default_fuse_options, path); continue; } if (parsed == 5) { // Parsing user: if (strcmp("-", encfs_options) == 0) strcpy(encfs_options, ""); if (strcmp("-", fuse_options) == 0) strcpy(fuse_options, ""); searchAndReplace(default_encfs_options); searchAndReplace(encfs_options); // Check if this is the right user / default user. if ((strcmp("-",username) != 0) && (strcmp(user,username) != 0) && (strcmp("*",username) !=0)) continue; if (strcmp("-",username) == 0) { strcat(path, "/"); strcat(path, user); // Todo check if dir exists and give better error msg. } // If username is '*', paths are relative to $HOME if (strcmp("*", username) == 0 && strcmp("-", targetpath) != 0) { if ((tmp = getHome(pwd, pamh))) { char home[PATH_MAX]; strcpy(home, tmp); strcat(home, "/"); strcat(home, path); strcpy(path, home); strcpy(home, tmp); strcat(home, "/"); strcat(home, targetpath); strcpy(targetpath, home); } } if (strcmp("-", targetpath) == 0) { // We do not have targetpath, construct one. strcpy(targetpath, ""); if ((tmp = getHome(pwd, pamh))) { strcpy(targetpath, tmp); } } // Done, check targetpath and return. if (!targetpath || *targetpath == '\0') { _pam_log(LOG_ERR, "Can't get to HOME dir for user %s", user); fclose(conffile); return 0; } // Check if path exists, if we're "-" then we dont care, if not we give error. if (is_dir(path)) { // We may fail to stat this directory (EPERM) if it's mounted because of fuse's funky permission system. if (!is_dir(targetpath)) { if (checkmnt(targetpath)) { // Doublecheck if we're mounted, for some reason we can't stat the dir even when root if it's mounted. // we are mounted, but we return 1 anyway so we can store targetpath fclose(conffile); return 1; } _pam_log(LOG_ERR, "TargetPath for %s does not exist (%s)", user, targetpath); fclose(conffile); return 0; } fclose(conffile); return 1; } // Path does not exist, if we're a specified user give error, if not keep looking. if ((strcmp("-", username) != 0) && (strcmp("*", username) != 0)) { _pam_log(LOG_ERR, "Path for %s does not exist (%s)", user, path); fclose(conffile); return 0; } continue; } continue; } fclose(conffile); return 0; }
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, const char **argv) { unsigned int lctrl; int retval; unsigned int ctrl = _pam_parse(argc, argv); /* <DO NOT free() THESE> */ const char *user; const char *member = NULL; char *pass_old, *pass_new; /* </DO NOT free() THESE> */ char *Announce; int retry = 0; /* * First get the name of a user */ retval = pam_get_user(pamh, &user, "Username: "******"username was NULL!"); return PAM_USER_UNKNOWN; } if (retval == PAM_SUCCESS && on(WINBIND_DEBUG_ARG, ctrl)) _pam_log(LOG_DEBUG, "username [%s] obtained", user); } else { if (on(WINBIND_DEBUG_ARG, ctrl)) _pam_log(LOG_DEBUG, "password - could not identify user"); return retval; } /* * obtain and verify the current password (OLDAUTHTOK) for * the user. */ if (flags & PAM_PRELIM_CHECK) { /* instruct user what is happening */ #define greeting "Changing password for " Announce = (char *) malloc(sizeof(greeting) + strlen(user)); if (Announce == NULL) { _pam_log(LOG_CRIT, "password - out of memory"); return PAM_BUF_ERR; } (void) strcpy(Announce, greeting); (void) strcpy(Announce + sizeof(greeting) - 1, user); #undef greeting lctrl = ctrl | WINBIND__OLD_PASSWORD; retval = _winbind_read_password(pamh, lctrl ,Announce ,"(current) NT password: "******"password - (old) token not obtained"); return retval; } /* verify that this is the password for this user */ retval = winbind_auth_request(user, pass_old, member, ctrl); if (retval != PAM_ACCT_EXPIRED && retval != PAM_AUTHTOK_EXPIRED && retval != PAM_NEW_AUTHTOK_REQD && retval != PAM_SUCCESS) { pass_old = NULL; return retval; } retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old); pass_old = NULL; if (retval != PAM_SUCCESS) { _pam_log(LOG_CRIT, "failed to set PAM_OLDAUTHTOK"); } } else if (flags & PAM_UPDATE_AUTHTOK) { /* * obtain the proposed password */ /* * get the old token back. */ retval = pam_get_item(pamh, PAM_OLDAUTHTOK ,(const void **) &pass_old); if (retval != PAM_SUCCESS) { _pam_log(LOG_NOTICE, "user not authenticated"); return retval; } lctrl = ctrl; if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) { lctrl |= WINBIND_USE_FIRST_PASS_ARG; } retry = 0; retval = PAM_AUTHTOK_ERR; while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) { /* * use_authtok is to force the use of a previously entered * password -- needed for pluggable password strength checking */ retval = _winbind_read_password(pamh, lctrl ,NULL ,"Enter new NT password: "******"Retype new NT password: "******"password - new password not obtained"); } pass_old = NULL;/* tidy up */ return retval; } /* * At this point we know who the user is and what they * propose as their new password. Verify that the new * password is acceptable. */ if (pass_new[0] == '\0') {/* "\0" password = NULL */ pass_new = NULL; } } /* * By reaching here we have approved the passwords and must now * rebuild the password database file. */ retval = winbind_chauthtok_request(user, pass_old, pass_new, ctrl); _pam_overwrite(pass_new); _pam_overwrite(pass_old); pass_old = pass_new = NULL; } else { retval = PAM_SERVICE_ERR; } return retval; }
/* stolen from pam_stress */ int tacacs_get_password (pam_handle_t * pamh, int flags ,int ctrl, char **password) { const void *pam_pass; char *pass = NULL; if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: called", __FUNCTION__); if ( (ctrl & (PAM_TAC_TRY_FIRST_PASS | PAM_TAC_USE_FIRST_PASS)) && (pam_get_item(pamh, PAM_AUTHTOK, &pam_pass) == PAM_SUCCESS) && (pam_pass != NULL) ) { if ((pass = strdup(pam_pass)) == NULL) return PAM_BUF_ERR; } else if ((ctrl & PAM_TAC_USE_FIRST_PASS)) { _pam_log(LOG_WARNING, "no forwarded password"); return PAM_PERM_DENIED; } else { struct pam_message msg; struct pam_response *resp = NULL; int retval; /* set up conversation call */ msg.msg_style = PAM_PROMPT_ECHO_OFF; if (!tac_prompt[0]) { msg.msg = "Password: "******"pam_sm_authenticate: NULL authtok given"); pass = resp->resp; /* remember this! */ resp->resp = NULL; free(resp); resp = NULL; } else { if (ctrl & PAM_TAC_DEBUG) { _pam_log (LOG_DEBUG, "pam_sm_authenticate: no error reported"); _pam_log (LOG_DEBUG, "getting password, but NULL returned!?"); } return PAM_CONV_ERR; } } /* FIXME *password can still turn out as NULL and it can't be free()d when it's NULL */ *password = pass; /* this *MUST* be free()'d by this module */ if(ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: obtained password", __FUNCTION__); return PAM_SUCCESS; }