(root)/
Linux-PAM-1.5.3/
modules/
pam_group/
pam_group.c
       1  /*
       2   * pam_group module
       3   *
       4   * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/7/6
       5   * Field parsing rewritten by Tomas Mraz <tm@t8m.info>
       6   */
       7  
       8  #include "config.h"
       9  
      10  #include <sys/file.h>
      11  #include <stdio.h>
      12  #include <stdlib.h>
      13  #include <ctype.h>
      14  #include <unistd.h>
      15  #include <stdarg.h>
      16  #include <time.h>
      17  #include <syslog.h>
      18  #include <string.h>
      19  #include <errno.h>
      20  
      21  #include <grp.h>
      22  #include <sys/types.h>
      23  #include <sys/stat.h>
      24  #include <fcntl.h>
      25  #include <netdb.h>
      26  
      27  #define PAM_GROUP_CONF		SCONFIGDIR "/group.conf"
      28  #ifdef VENDOR_SCONFIGDIR
      29  # define VENDOR_PAM_GROUP_CONF	VENDOR_SCONFIGDIR "/group.conf"
      30  #endif
      31  #define PAM_GROUP_BUFLEN        1000
      32  #define FIELD_SEPARATOR         ';'   /* this is new as of .02 */
      33  
      34  #ifndef TRUE
      35  # define TRUE 1
      36  #endif
      37  #ifndef FALSE
      38  # define FALSE 0
      39  #endif
      40  
      41  typedef enum { AND, OR } operator;
      42  
      43  #include <security/pam_modules.h>
      44  #include <security/_pam_macros.h>
      45  #include <security/pam_modutil.h>
      46  #include <security/pam_ext.h>
      47  #include "pam_inline.h"
      48  
      49  /* --- static functions for checking whether the user should be let in --- */
      50  
      51  static char *
      52  shift_buf(char *mem, int from)
      53  {
      54      char *start = mem;
      55      while ((*mem = mem[from]) != '\0')
      56  	++mem;
      57      pam_overwrite_n(mem, PAM_GROUP_BUFLEN - (mem - start));
      58      return mem;
      59  }
      60  
      61  static void
      62  trim_spaces(char *buf, char *from)
      63  {
      64      while (from > buf) {
      65  	--from;
      66  	if (*from == ' ')
      67  	    *from = '\0';
      68  	else
      69  	    break;
      70      }
      71  }
      72  
      73  #define STATE_NL       0 /* new line starting */
      74  #define STATE_COMMENT  1 /* inside comment */
      75  #define STATE_FIELD    2 /* field following */
      76  #define STATE_EOF      3 /* end of file or error */
      77  
      78  static int
      79  read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state,
      80  	   const char *conf_filename)
      81  {
      82      char *to;
      83      char *src;
      84      int i;
      85      char c;
      86      int onspace;
      87  
      88      /* is buf set ? */
      89      if (! *buf) {
      90  	*buf = (char *) calloc(1, PAM_GROUP_BUFLEN+1);
      91  	if (! *buf) {
      92  	    pam_syslog(pamh, LOG_CRIT, "out of memory");
      93  	    D(("no memory"));
      94  	    *state = STATE_EOF;
      95  	    return -1;
      96  	}
      97  	*from = 0;
      98          *state = STATE_NL;
      99  	fd = open(conf_filename, O_RDONLY);
     100  	if (fd < 0) {
     101  	    pam_syslog(pamh, LOG_ERR, "error opening %s: %m", conf_filename);
     102  	    _pam_drop(*buf);
     103  	    *state = STATE_EOF;
     104  	    return -1;
     105  	}
     106      }
     107  
     108      if (*from > 0)
     109  	to = shift_buf(*buf, *from);
     110      else
     111  	to = *buf;
     112  
     113      while (fd != -1 && to - *buf < PAM_GROUP_BUFLEN) {
     114  	i = pam_modutil_read(fd, to, PAM_GROUP_BUFLEN - (to - *buf));
     115  	if (i < 0) {
     116  	    pam_syslog(pamh, LOG_ERR, "error reading %s: %m", conf_filename);
     117  	    close(fd);
     118  	    pam_overwrite_n(*buf, PAM_GROUP_BUFLEN);
     119  	    _pam_drop(*buf);
     120  	    *state = STATE_EOF;
     121  	    return -1;
     122  	} else if (!i) {
     123  	    close(fd);
     124  	    fd = -1;          /* end of file reached */
     125  	}
     126  
     127  	to += i;
     128      }
     129  
     130      if (to == *buf) {
     131  	/* nothing previously in buf, nothing read */
     132  	_pam_drop(*buf);
     133  	*state = STATE_EOF;
     134  	return -1;
     135      }
     136  
     137      pam_overwrite_n(to, PAM_GROUP_BUFLEN - (to - *buf));
     138  
     139      to = *buf;
     140      onspace = 1; /* delete any leading spaces */
     141  
     142      for (src = to; (c=*src) != '\0'; ++src) {
     143  	if (*state == STATE_COMMENT && c != '\n') {
     144  		continue;
     145  	}
     146  
     147  	switch (c) {
     148  	    case '\n':
     149  		*state = STATE_NL;
     150                  *to = '\0';
     151  		*from = (src - *buf) + 1;
     152  		trim_spaces(*buf, to);
     153  		return fd;
     154  
     155  	    case '\t':
     156              case ' ':
     157  		if (!onspace) {
     158  		    onspace = 1;
     159  		    *to++ = ' ';
     160  		}
     161  		break;
     162  
     163              case '!':
     164  		onspace = 1; /* ignore following spaces */
     165  		*to++ = '!';
     166  		break;
     167  
     168  	    case '#':
     169  		*state = STATE_COMMENT;
     170  		break;
     171  
     172  	    case FIELD_SEPARATOR:
     173  		*state = STATE_FIELD;
     174                  *to = '\0';
     175  		*from = (src - *buf) + 1;
     176  		trim_spaces(*buf, to);
     177  		return fd;
     178  
     179  	    case '\\':
     180  		if (src[1] == '\n') {
     181  		    ++src; /* skip it */
     182  		    break;
     183  		}
     184  	    /* fallthrough */
     185  	    default:
     186  		*to++ = c;
     187  		onspace = 0;
     188  	}
     189  	if (src > to)
     190  	    *src = '\0'; /* clearing */
     191      }
     192  
     193      if (*state != STATE_COMMENT) {
     194  	*state = STATE_COMMENT;
     195  	pam_syslog(pamh, LOG_ERR, "field too long - ignored");
     196  	**buf = '\0';
     197      } else {
     198  	*to = '\0';
     199  	trim_spaces(*buf, to);
     200      }
     201  
     202      *from = 0;
     203      return fd;
     204  }
     205  
     206  /* read a member from a field */
     207  
     208  static int logic_member(const char *string, int *at)
     209  {
     210       int c,to;
     211       int done=0;
     212       int token=0;
     213  
     214       to=*at;
     215       do {
     216  	  c = string[to++];
     217  
     218  	  switch (c) {
     219  
     220  	  case '\0':
     221  	       --to;
     222  	       done = 1;
     223  	       break;
     224  
     225  	  case '&':
     226  	  case '|':
     227  	  case '!':
     228  	       if (token) {
     229  		    --to;
     230  	       }
     231  	       done = 1;
     232  	       break;
     233  
     234  	  default:
     235  	       if (isalpha(c) || c == '*' || isdigit(c) || c == '_'
     236  		    || c == '-' || c == '.' || c == '/' || c == ':') {
     237  		    token = 1;
     238  	       } else if (token) {
     239  		    --to;
     240  		    done = 1;
     241  	       } else {
     242  		    ++*at;
     243  	       }
     244  	  }
     245       } while (!done);
     246  
     247       return to - *at;
     248  }
     249  
     250  typedef enum { VAL, OP } expect;
     251  
     252  static int
     253  logic_field (const pam_handle_t *pamh, const void *me,
     254               const char *x, int rule,
     255               int (*agrees)(const pam_handle_t *pamh, const void *,
     256                                 const char *, int, int))
     257  {
     258       int left=FALSE, right, not=FALSE;
     259       operator oper=OR;
     260       int at=0, l;
     261       expect next=VAL;
     262  
     263       while ((l = logic_member(x,&at))) {
     264  	  int c = x[at];
     265  
     266  	  if (next == VAL) {
     267  	       if (c == '!')
     268  		    not = !not;
     269  	       else if (isalpha(c) || c == '*' || isdigit(c) || c == '_'
     270                      || c == '-' || c == '.' || c == '/' || c == ':') {
     271  		    right = not ^ agrees(pamh, me, x+at, l, rule);
     272  		    if (oper == AND)
     273  			 left &= right;
     274  		    else
     275  			 left |= right;
     276  		    next = OP;
     277  	       } else {
     278  		    pam_syslog(pamh, LOG_ERR,
     279  			       "garbled syntax; expected name (rule #%d)",
     280  			       rule);
     281  		    return FALSE;
     282  	       }
     283  	  } else {   /* OP */
     284  	       switch (c) {
     285  	       case '&':
     286  		    oper = AND;
     287  		    break;
     288  	       case '|':
     289  		    oper = OR;
     290  		    break;
     291  	       default:
     292  		    pam_syslog(pamh, LOG_ERR,
     293  			       "garbled syntax; expected & or | (rule #%d)",
     294  			       rule);
     295  		    D(("%c at %d",c,at));
     296  		    return FALSE;
     297  	       }
     298  	       next = VAL;
     299  	       not = FALSE;
     300  	  }
     301  	  at += l;
     302       }
     303  
     304       return left;
     305  }
     306  
     307  static int
     308  is_same (const pam_handle_t *pamh UNUSED,
     309           const void *A, const char *b, int len, int rule UNUSED)
     310  {
     311       int i;
     312       const char *a;
     313  
     314       a = A;
     315       for (i=0; len > 0; ++i, --len) {
     316  	  if (b[i] != a[i]) {
     317  	       if (b[i++] == '*') {
     318  		    return (!--len || !strncmp(b+i,a+strlen(a)-len,len));
     319  	       } else
     320  		    return FALSE;
     321  	  }
     322       }
     323  
     324       /* Ok, we know that b is a substring from A and does not contain
     325          wildcards, but now the length of both strings must be the same,
     326          too. In this case it means, a[i] has to be the end of the string. */
     327       if (a[i] != '\0')
     328            return FALSE;
     329  
     330       return ( !len );
     331  }
     332  
     333  typedef struct {
     334       int day;             /* array of 7 bits, one set for today */
     335       int minute;            /* integer, hour*100+minute for now */
     336  } TIME;
     337  
     338  static struct day {
     339       const char *d;
     340       int bit;
     341  } const days[11] = {
     342       { "su", 01 },
     343       { "mo", 02 },
     344       { "tu", 04 },
     345       { "we", 010 },
     346       { "th", 020 },
     347       { "fr", 040 },
     348       { "sa", 0100 },
     349       { "wk", 076 },
     350       { "wd", 0101 },
     351       { "al", 0177 },
     352       { NULL, 0 }
     353  };
     354  
     355  static TIME time_now(void)
     356  {
     357       struct tm *local;
     358       time_t the_time;
     359       TIME this;
     360  
     361       the_time = time((time_t *)0);                /* get the current time */
     362       local = localtime(&the_time);
     363       this.day = days[local->tm_wday].bit;
     364       this.minute = local->tm_hour*100 + local->tm_min;
     365  
     366       D(("day: 0%o, time: %.4d", this.day, this.minute));
     367       return this;
     368  }
     369  
     370  /* take the current date and see if the range "date" passes it */
     371  static int
     372  check_time (const pam_handle_t *pamh, const void *AT,
     373              const char *times, int len, int rule)
     374  {
     375       int not,pass;
     376       int marked_day, time_start, time_end;
     377       const TIME *at;
     378       int i,j=0;
     379  
     380       at = AT;
     381       D(("checking: 0%o/%.4d vs. %s", at->day, at->minute, times));
     382  
     383       if (times == NULL) {
     384  	  /* this should not happen */
     385  	  pam_syslog(pamh, LOG_CRIT, "internal error in file %s at line %d",
     386  		     __FILE__, __LINE__);
     387  	  return FALSE;
     388       }
     389  
     390       if (times[j] == '!') {
     391  	  ++j;
     392  	  not = TRUE;
     393       } else {
     394  	  not = FALSE;
     395       }
     396  
     397       for (marked_day = 0; len > 0 && isalpha(times[j]); --len) {
     398  	  int this_day=-1;
     399  
     400  	  D(("%c%c ?", times[j], times[j+1]));
     401  	  for (i=0; days[i].d != NULL; ++i) {
     402  	       if (tolower(times[j]) == days[i].d[0]
     403  		   && tolower(times[j+1]) == days[i].d[1] ) {
     404  		    this_day = days[i].bit;
     405  		    break;
     406  	       }
     407  	  }
     408  	  j += 2;
     409  	  if (this_day == -1) {
     410  	       pam_syslog(pamh, LOG_ERR, "bad day specified (rule #%d)", rule);
     411  	       return FALSE;
     412  	  }
     413  	  marked_day ^= this_day;
     414       }
     415       if (marked_day == 0) {
     416  	  pam_syslog(pamh, LOG_ERR, "no day specified");
     417  	  return FALSE;
     418       }
     419       D(("day range = 0%o", marked_day));
     420  
     421       time_start = 0;
     422       for (i=0; len > 0 && i < 4 && isdigit(times[i+j]); ++i, --len) {
     423  	  time_start *= 10;
     424  	  time_start += times[i+j]-'0';       /* is this portable? */
     425       }
     426       j += i;
     427  
     428       if (times[j] == '-') {
     429  	  time_end = 0;
     430  	  for (i=1; len > 0 && i < 5 && isdigit(times[i+j]); ++i, --len) {
     431  	       time_end *= 10;
     432  	       time_end += times[i+j]-'0';    /* is this portable? */
     433  	  }
     434  	  j += i;
     435       } else
     436  	  time_end = -1;
     437  
     438       D(("i=%d, time_end=%d, times[j]='%c'", i, time_end, times[j]));
     439       if (i != 5 || time_end == -1) {
     440  	  pam_syslog(pamh, LOG_ERR, "no/bad times specified (rule #%d)", rule);
     441  	  return TRUE;
     442       }
     443       D(("times(%d to %d)", time_start,time_end));
     444       D(("marked_day = 0%o", marked_day));
     445  
     446       /* compare with the actual time now */
     447  
     448       pass = FALSE;
     449       if (time_start < time_end) {    /* start < end ? --> same day */
     450  	  if ((at->day & marked_day) && (at->minute >= time_start)
     451  	      && (at->minute < time_end)) {
     452  	       D(("time is listed"));
     453  	       pass = TRUE;
     454  	  }
     455       } else {                                    /* spans two days */
     456  	  if ((at->day & marked_day) && (at->minute >= time_start)) {
     457  	       D(("caught on first day"));
     458  	       pass = TRUE;
     459  	  } else {
     460  	       marked_day <<= 1;
     461  	       marked_day |= (marked_day & 0200) ? 1:0;
     462  	       D(("next day = 0%o", marked_day));
     463  	       if ((at->day & marked_day) && (at->minute <= time_end)) {
     464  		    D(("caught on second day"));
     465  		    pass = TRUE;
     466  	       }
     467  	  }
     468       }
     469  
     470       return (not ^ pass);
     471  }
     472  
     473  static int find_member(const char *string, int *at)
     474  {
     475       int c,to;
     476       int done=0;
     477       int token=0;
     478  
     479       to=*at;
     480       do {
     481            c = string[to++];
     482  
     483            switch (c) {
     484  
     485            case '\0':
     486                 --to;
     487                 done = 1;
     488                 break;
     489  
     490            case '&':
     491            case '|':
     492  	  case '!':
     493                 if (token) {
     494                      --to;
     495                 }
     496                 done = 1;
     497                 break;
     498  
     499            default:
     500                 if (isalpha(c) || isdigit(c) || c == '_' || c == '*'
     501                      || c == '-') {
     502                      token = 1;
     503                 } else if (token) {
     504                      --to;
     505                      done = 1;
     506                 } else {
     507                      ++*at;
     508                 }
     509            }
     510       } while (!done);
     511  
     512       return to - *at;
     513  }
     514  
     515  #define GROUP_BLK 10
     516  #define blk_size(len) (((len-1 + GROUP_BLK)/GROUP_BLK)*GROUP_BLK)
     517  
     518  static int mkgrplist(pam_handle_t *pamh, char *buf, gid_t **list, int len)
     519  {
     520       int l,at=0;
     521       int blks;
     522  
     523       blks = blk_size(len);
     524       D(("cf. blks=%d and len=%d", blks,len));
     525  
     526       while ((l = find_member(buf,&at))) {
     527  	  int edge;
     528  
     529  	  if (len >= blks) {
     530  	       gid_t *tmp;
     531  
     532  	       D(("allocating new block"));
     533  	       tmp = (gid_t *) realloc((*list)
     534  				       , sizeof(gid_t) * (blks += GROUP_BLK));
     535  	       if (tmp != NULL) {
     536  		    (*list) = tmp;
     537  	       } else {
     538  		    pam_syslog(pamh, LOG_ERR, "out of memory for group list");
     539  		    free(*list);
     540  		    (*list) = NULL;
     541  		    return -1;
     542  	       }
     543  	  }
     544  
     545  	  /* '\0' terminate the entry */
     546  
     547  	  edge = (buf[at+l]) ? 1:0;
     548  	  buf[at+l] = '\0';
     549  	  D(("found group: %s",buf+at));
     550  
     551  	  /* this is where we convert a group name to a gid_t */
     552  	  {
     553  	      const struct group *grp;
     554  
     555  	      grp = pam_modutil_getgrnam(pamh, buf+at);
     556  	      if (grp == NULL) {
     557  		  pam_syslog(pamh, LOG_ERR, "bad group: %s", buf+at);
     558  	      } else {
     559  		  D(("group %s exists", buf+at));
     560  		  (*list)[len++] = grp->gr_gid;
     561  	      }
     562  	  }
     563  
     564  	  /* next entry along */
     565  
     566  	  at += l + edge;
     567       }
     568       D(("returning with [%p/len=%d]->%p",list,len,*list));
     569       return len;
     570  }
     571  
     572  
     573  static int check_account(pam_handle_t *pamh, const char *service,
     574  			 const char *tty, const char *user)
     575  {
     576      int from=0, state=STATE_NL, fd=-1;
     577      char *buffer=NULL;
     578      int count=0;
     579      TIME here_and_now;
     580      int retval=PAM_SUCCESS;
     581      gid_t *grps;
     582      int no_grps;
     583      const char *conf_filename = PAM_GROUP_CONF;
     584  
     585  #ifdef VENDOR_PAM_GROUP_CONF
     586      /*
     587       * Check whether PAM_GROUP_CONF file is available.
     588       * If it does not exist, fall back to VENDOR_PAM_GROUP_CONF file.
     589       */
     590      struct stat stat_buffer;
     591      if (stat(conf_filename, &stat_buffer) != 0 && errno == ENOENT) {
     592  	conf_filename = VENDOR_PAM_GROUP_CONF;
     593      }
     594  #endif
     595  
     596      /*
     597       * first we get the current list of groups - the application
     598       * will have previously done an initgroups(), or equivalent.
     599       */
     600  
     601      D(("counting supplementary groups"));
     602      no_grps = getgroups(0, NULL);      /* find the current number of groups */
     603      if (no_grps > 0) {
     604  	grps = calloc( blk_size(no_grps) , sizeof(gid_t) );
     605  	D(("copying current list into grps [%d big]",blk_size(no_grps)));
     606  	if (getgroups(no_grps, grps) < 0) {
     607  	    D(("getgroups call failed"));
     608  	    no_grps = 0;
     609  	    _pam_drop(grps);
     610  	}
     611  #ifdef PAM_DEBUG
     612  	{
     613  	    int z;
     614  	    for (z=0; z<no_grps; ++z) {
     615  		D(("gid[%d]=%d", z, grps[z]));
     616  	    }
     617  	}
     618  #endif
     619      } else {
     620  	D(("no supplementary groups known"));
     621  	no_grps = 0;
     622  	grps = NULL;
     623      }
     624  
     625      here_and_now = time_now();                         /* find current time */
     626  
     627      /* parse the rules in the configuration file */
     628      do {
     629  	int good=TRUE;
     630  
     631  	/* here we get the service name field */
     632  
     633  	fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
     634  	if (!buffer || !buffer[0]) {
     635  	    /* empty line .. ? */
     636  	    continue;
     637  	}
     638  	++count;
     639  	D(("working on rule #%d",count));
     640  
     641  	if (state != STATE_FIELD) {
     642  	    pam_syslog(pamh, LOG_ERR,
     643  		       "%s: malformed rule #%d", conf_filename, count);
     644  	    continue;
     645  	}
     646  
     647  	good = logic_field(pamh,service, buffer, count, is_same);
     648  	D(("with service: %s", good ? "passes":"fails" ));
     649  
     650  	/* here we get the terminal name field */
     651  
     652  	fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
     653  	if (state != STATE_FIELD) {
     654  	    pam_syslog(pamh, LOG_ERR,
     655  		       "%s: malformed rule #%d", conf_filename, count);
     656  	    continue;
     657  	}
     658  	good &= logic_field(pamh,tty, buffer, count, is_same);
     659  	D(("with tty: %s", good ? "passes":"fails" ));
     660  
     661  	/* here we get the username field */
     662  
     663  	fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
     664  	if (state != STATE_FIELD) {
     665  	    pam_syslog(pamh, LOG_ERR,
     666  		       "%s: malformed rule #%d", conf_filename, count);
     667  	    continue;
     668  	}
     669  	/* If buffer starts with @, we are using netgroups */
     670  	if (buffer[0] == '@')
     671  #ifdef HAVE_INNETGR
     672  	  good &= innetgr (&buffer[1], NULL, user, NULL);
     673  #else
     674  	  pam_syslog (pamh, LOG_ERR, "pam_group does not have netgroup support");
     675  #endif
     676  	/* otherwise, if the buffer starts with %, it's a UNIX group */
     677  	else if (buffer[0] == '%')
     678            good &= pam_modutil_user_in_group_nam_nam(pamh, user, &buffer[1]);
     679  	else
     680  	  good &= logic_field(pamh,user, buffer, count, is_same);
     681  	D(("with user: %s", good ? "passes":"fails" ));
     682  
     683  	/* here we get the time field */
     684  
     685  	fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
     686  	if (state != STATE_FIELD) {
     687  	    pam_syslog(pamh, LOG_ERR,
     688  		       "%s: malformed rule #%d", conf_filename, count);
     689  	    continue;
     690  	}
     691  
     692  	good &= logic_field(pamh,&here_and_now, buffer, count, check_time);
     693  	D(("with time: %s", good ? "passes":"fails" ));
     694  
     695  	fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
     696  	if (state == STATE_FIELD) {
     697  	    pam_syslog(pamh, LOG_ERR,
     698  		       "%s: poorly terminated rule #%d", conf_filename, count);
     699  	    continue;
     700  	}
     701  
     702  	/*
     703  	 * so we have a list of groups, we need to turn it into
     704  	 * something to send to setgroups(2)
     705  	 */
     706  
     707  	if (good) {
     708  	    D(("adding %s to gid list", buffer));
     709  	    good = mkgrplist(pamh, buffer, &grps, no_grps);
     710  	    if (good < 0) {
     711  		no_grps = 0;
     712  	    } else {
     713  		no_grps = good;
     714  	    }
     715  	}
     716  
     717  	if (good > 0) {
     718  	    D(("rule #%d passed, added %d groups", count, good));
     719  	} else if (good < 0) {
     720  	    retval = PAM_BUF_ERR;
     721  	} else {
     722  	    D(("rule #%d failed", count));
     723  	}
     724  
     725      } while (state != STATE_EOF);
     726  
     727      /* now set the groups for the user */
     728  
     729      if (no_grps > 0) {
     730  #ifdef PAM_DEBUG
     731  	int err;
     732  #endif
     733  	D(("trying to set %d groups", no_grps));
     734  #ifdef PAM_DEBUG
     735  	for (err=0; err<no_grps; ++err) {
     736  	    D(("gid[%d]=%d", err, grps[err]));
     737  	}
     738  #endif
     739  	if (setgroups(no_grps, grps) != 0) {
     740  	    D(("but couldn't set groups %m"));
     741  	    pam_syslog(pamh, LOG_ERR,
     742  		       "unable to set the group membership for user: %m");
     743  	    retval = PAM_CRED_ERR;
     744  	}
     745      }
     746  
     747      if (grps) {                                          /* tidy up */
     748  	pam_overwrite_n(grps, sizeof(gid_t) * blk_size(no_grps));
     749  	_pam_drop(grps);
     750  	no_grps = 0;
     751      }
     752  
     753      return retval;
     754  }
     755  
     756  /* --- public authentication management functions --- */
     757  
     758  int
     759  pam_sm_authenticate (pam_handle_t *pamh UNUSED, int flags UNUSED,
     760  		     int argc UNUSED, const char **argv UNUSED)
     761  {
     762      return PAM_IGNORE;
     763  }
     764  
     765  int
     766  pam_sm_setcred (pam_handle_t *pamh, int flags,
     767  		int argc UNUSED, const char **argv UNUSED)
     768  {
     769      const void *service=NULL, *void_tty=NULL;
     770      const char *user=NULL;
     771      const char *tty;
     772      int retval;
     773      unsigned setting;
     774  
     775      /* only interested in establishing credentials */
     776  
     777      setting = flags;
     778      if (!(setting & (PAM_ESTABLISH_CRED | PAM_REINITIALIZE_CRED))) {
     779  	D(("ignoring call - not for establishing credentials"));
     780  	return PAM_SUCCESS;            /* don't fail because of this */
     781      }
     782  
     783      /* set service name */
     784  
     785      if (pam_get_item(pamh, PAM_SERVICE, &service)
     786  	!= PAM_SUCCESS || service == NULL) {
     787  	pam_syslog(pamh, LOG_ERR, "cannot find the current service name");
     788  	return PAM_ABORT;
     789      }
     790  
     791      /* set username */
     792  
     793      if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || *user == '\0') {
     794  	pam_syslog(pamh, LOG_NOTICE, "cannot determine user name");
     795  	return PAM_USER_UNKNOWN;
     796      }
     797  
     798      /* set tty name */
     799  
     800      if (pam_get_item(pamh, PAM_TTY, &void_tty) != PAM_SUCCESS
     801  	|| void_tty == NULL) {
     802  	D(("PAM_TTY not set, probing stdin"));
     803  	tty = ttyname(STDIN_FILENO);
     804  	if (tty == NULL) {
     805  	    tty = "";
     806  	}
     807  	if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS) {
     808  	    pam_syslog(pamh, LOG_ERR, "couldn't set tty name");
     809  	    return PAM_ABORT;
     810  	}
     811      }
     812      else
     813        tty = (const char *) void_tty;
     814  
     815      if (tty[0] == '/') {   /* full path */
     816  	const char *t;
     817          tty++;
     818          if ((t = strchr(tty, '/')) != NULL) {
     819  	    tty = t + 1;
     820  	}
     821      }
     822  
     823      /* good, now we have the service name, the user and the terminal name */
     824  
     825      D(("service=%s", service));
     826      D(("user=%s", user));
     827      D(("tty=%s", tty));
     828  
     829      retval = check_account(pamh,service,tty,user);          /* get groups */
     830  
     831      return retval;
     832  }
     833  
     834  /* end of module definition */