(root)/
Linux-PAM-1.5.3/
modules/
pam_issue/
pam_issue.c
       1  /*
       2   * pam_issue module - a simple /etc/issue parser to set PAM_USER_PROMPT
       3   *
       4   * Copyright 1999 by Ben Collins <bcollins@debian.org>
       5   *
       6   * Needs to be called before any other auth modules so we can setup the
       7   * user prompt before it's first used. Allows one argument option, which
       8   * is the full path to a file to be used for issue (uses /etc/issue as a
       9   * default) such as "issue=/etc/issue.telnet".
      10   *
      11   * We can also parse escapes within the the issue file (enabled by
      12   * default, but can be disabled with the "noesc" option). It's the exact
      13   * same parsing as util-linux's agetty program performs.
      14   *
      15   * Released under the GNU LGPL version 2 or later
      16   */
      17  
      18  #include "config.h"
      19  
      20  #include <string.h>
      21  #include <stdio.h>
      22  #include <stdlib.h>
      23  #include <sys/types.h>
      24  #include <sys/stat.h>
      25  #include <fcntl.h>
      26  #include <unistd.h>
      27  #include <sys/utsname.h>
      28  #include <time.h>
      29  #include <syslog.h>
      30  
      31  #ifdef USE_LOGIND
      32  #include <systemd/sd-login.h>
      33  #else
      34  #include <utmp.h>
      35  #endif
      36  
      37  #include <security/_pam_macros.h>
      38  #include <security/pam_modules.h>
      39  #include <security/pam_ext.h>
      40  #include "pam_inline.h"
      41  
      42  static int _user_prompt_set = 0;
      43  
      44  static int
      45  read_issue_raw(pam_handle_t *pamh, FILE *fp, char **prompt)
      46  {
      47      char *issue;
      48      struct stat st;
      49  
      50      *prompt = NULL;
      51  
      52      if (fstat(fileno(fp), &st) < 0) {
      53  	pam_syslog(pamh, LOG_ERR, "stat error: %m");
      54  	return PAM_SERVICE_ERR;
      55      }
      56  
      57      if ((issue = malloc(st.st_size + 1)) == NULL) {
      58  	pam_syslog(pamh, LOG_CRIT, "out of memory");
      59  	return PAM_BUF_ERR;
      60      }
      61  
      62      if ((off_t)fread(issue, 1, st.st_size, fp) != st.st_size) {
      63  	pam_syslog(pamh, LOG_ERR, "read error: %m");
      64  	_pam_drop(issue);
      65  	return PAM_SERVICE_ERR;
      66      }
      67  
      68      issue[st.st_size] = '\0';
      69      *prompt = issue;
      70      return PAM_SUCCESS;
      71  }
      72  
      73  static int
      74  read_issue_quoted(pam_handle_t *pamh, FILE *fp, char **prompt)
      75  {
      76      int c;
      77      size_t size = 1024;
      78      size_t issue_len = 0;
      79      char *issue;
      80      struct utsname uts;
      81  
      82      *prompt = NULL;
      83  
      84      if ((issue = malloc(size)) == NULL) {
      85  	pam_syslog(pamh, LOG_CRIT, "out of memory");
      86  	return PAM_BUF_ERR;
      87      }
      88  
      89      (void) uname(&uts);
      90  
      91      while ((c = getc(fp)) != EOF) {
      92  	const char *src = NULL;
      93  	size_t len = 0;
      94  	char buf[1024] = "";
      95  
      96  	if (c == '\\') {
      97  	    if ((c = getc(fp)) == EOF)
      98  		break;
      99  	    switch (c) {
     100  	      case 's':
     101  		src = uts.sysname;
     102  		len = strnlen(uts.sysname, sizeof(uts.sysname));
     103  		break;
     104  	      case 'n':
     105  		src = uts.nodename;
     106  		len = strnlen(uts.nodename, sizeof(uts.nodename));
     107  		break;
     108  	      case 'r':
     109  		src = uts.release;
     110  		len = strnlen(uts.release, sizeof(uts.release));
     111  		break;
     112  	      case 'v':
     113  		src = uts.version;
     114  		len = strnlen(uts.version, sizeof(uts.version));
     115  		break;
     116  	      case 'm':
     117  		src = uts.machine;
     118  		len = strnlen(uts.machine, sizeof(uts.machine));
     119  		break;
     120  	      case 'o':
     121  #ifdef HAVE_GETDOMAINNAME
     122  		if (getdomainname(buf, sizeof(buf)) >= 0)
     123  		    buf[sizeof(buf) - 1] = '\0';
     124  #endif
     125  		break;
     126  	      case 'd':
     127  	      case 't':
     128  		{
     129  		    const char *weekday[] = {
     130  			"Sun", "Mon", "Tue", "Wed", "Thu",
     131  			"Fri", "Sat" };
     132  		    const char *month[] = {
     133  			"Jan", "Feb", "Mar", "Apr", "May",
     134  			"Jun", "Jul", "Aug", "Sep", "Oct",
     135  			"Nov", "Dec" };
     136  		    time_t now;
     137  		    struct tm *tm;
     138  
     139  		    (void) time (&now);
     140  		    tm = localtime(&now);
     141  
     142  		    if (c == 'd')
     143  			snprintf (buf, sizeof buf, "%s %s %d  %d",
     144  				weekday[tm->tm_wday], month[tm->tm_mon],
     145  				tm->tm_mday, tm->tm_year + 1900);
     146  		    else
     147  			snprintf (buf, sizeof buf, "%02d:%02d:%02d",
     148  				tm->tm_hour, tm->tm_min, tm->tm_sec);
     149  		}
     150  		break;
     151  	      case 'l':
     152  		{
     153  		    const char *ttyn = ttyname(1);
     154  		    if (ttyn) {
     155  			const char *str = pam_str_skip_prefix(ttyn, "/dev/");
     156  			if (str != NULL)
     157  			    ttyn = str;
     158  			src = ttyn;
     159  			len = strlen(ttyn);
     160  		    }
     161  		}
     162  		break;
     163  	      case 'u':
     164  	      case 'U':
     165  		{
     166  		    unsigned int users = 0;
     167  #ifdef USE_LOGIND
     168  		    int sessions = sd_get_sessions(NULL);
     169  
     170  		    if (sessions < 0) {
     171  		      pam_syslog(pamh, LOG_ERR, "logind error: %s",
     172  				 strerror(-sessions));
     173  		      _pam_drop(issue);
     174  		      return PAM_SERVICE_ERR;
     175  		    } else {
     176  		      users = sessions;
     177  		    }
     178  #else
     179  		    struct utmp *ut;
     180  		    setutent();
     181  		    while ((ut = getutent())) {
     182  			if (ut->ut_type == USER_PROCESS)
     183  			    ++users;
     184  		    }
     185  		    endutent();
     186  #endif
     187  		    if (c == 'U')
     188  			snprintf (buf, sizeof buf, "%u %s", users,
     189  			          (users == 1) ? "user" : "users");
     190  		    else
     191  			snprintf (buf, sizeof buf, "%u", users);
     192  		    break;
     193  		}
     194  	      default:
     195  		buf[0] = c; buf[1] = '\0';
     196  	    }
     197  	} else {
     198  	    buf[0] = c; buf[1] = '\0';
     199  	}
     200  
     201  	if (src == NULL) {
     202  	    src = buf;
     203  	    len = strlen(buf);
     204  	}
     205  	if (issue_len + len + 1 > size) {
     206  	    char *new_issue;
     207  
     208  	    size += len + 1;
     209  	    new_issue = realloc (issue, size);
     210  	    if (new_issue == NULL) {
     211  		_pam_drop(issue);
     212  		return PAM_BUF_ERR;
     213  	    }
     214  	    issue = new_issue;
     215  	}
     216  	memcpy(issue + issue_len, src, len);
     217  	issue_len += len;
     218      }
     219  
     220      issue[issue_len] = '\0';
     221  
     222      if (ferror(fp)) {
     223  	pam_syslog(pamh, LOG_ERR, "read error: %m");
     224  	_pam_drop(issue);
     225  	return PAM_SERVICE_ERR;
     226      }
     227  
     228      *prompt = issue;
     229      return PAM_SUCCESS;
     230  }
     231  
     232  /* --- authentication management functions (only) --- */
     233  
     234  int
     235  pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
     236  		    int argc, const char **argv)
     237  {
     238      int retval = PAM_SERVICE_ERR;
     239      FILE *fp;
     240      const char *issue_file = NULL;
     241      int parse_esc = 1;
     242      const void *item = NULL;
     243      const char *cur_prompt;
     244      char *issue_prompt = NULL;
     245  
     246     /* If we've already set the prompt, don't set it again */
     247      if(_user_prompt_set)
     248  	return PAM_IGNORE;
     249  
     250      /* We set this here so if we fail below, we won't get further
     251         than this next time around (only one real failure) */
     252      _user_prompt_set = 1;
     253  
     254      for ( ; argc-- > 0 ; ++argv ) {
     255  	const char *str;
     256  
     257  	if ((str = pam_str_skip_prefix(*argv, "issue=")) != NULL) {
     258  	    issue_file = str;
     259  	    D(("set issue_file to: %s", issue_file));
     260  	} else if (!strcmp(*argv,"noesc")) {
     261  	    parse_esc = 0;
     262  	    D(("turning off escape parsing by request"));
     263  	} else
     264  	    D(("unknown option passed: %s", *argv));
     265      }
     266  
     267      if (issue_file == NULL)
     268  	issue_file = "/etc/issue";
     269  
     270      if ((fp = fopen(issue_file, "r")) == NULL) {
     271  	pam_syslog(pamh, LOG_ERR, "error opening %s: %m", issue_file);
     272  	return PAM_SERVICE_ERR;
     273      }
     274  
     275      if ((retval = pam_get_item(pamh, PAM_USER_PROMPT, &item)) != PAM_SUCCESS) {
     276  	fclose(fp);
     277  	return retval;
     278      }
     279  
     280      cur_prompt = item;
     281      if (cur_prompt == NULL)
     282  	cur_prompt = "";
     283  
     284      if (parse_esc)
     285  	retval = read_issue_quoted(pamh, fp, &issue_prompt);
     286      else
     287  	retval = read_issue_raw(pamh, fp, &issue_prompt);
     288  
     289      fclose(fp);
     290  
     291      if (retval != PAM_SUCCESS)
     292  	goto out;
     293  
     294      {
     295  	size_t size = strlen(issue_prompt) + strlen(cur_prompt) + 1;
     296  	char *new_prompt = realloc(issue_prompt, size);
     297  
     298  	if (new_prompt == NULL) {
     299  	    pam_syslog(pamh, LOG_CRIT, "out of memory");
     300  	    retval = PAM_BUF_ERR;
     301  	    goto out;
     302  	}
     303  	issue_prompt = new_prompt;
     304      }
     305  
     306      strcat(issue_prompt, cur_prompt);
     307      retval = pam_set_item(pamh, PAM_USER_PROMPT,
     308  			      (const void *) issue_prompt);
     309    out:
     310      _pam_drop(issue_prompt);
     311      return (retval == PAM_SUCCESS) ? PAM_IGNORE : retval;
     312  }
     313  
     314  int
     315  pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
     316  	       int argc UNUSED, const char **argv UNUSED)
     317  {
     318       return PAM_IGNORE;
     319  }