(root)/
Linux-PAM-1.5.3/
libpamc/
pamc_converse.c
       1  /*
       2   * $Id$
       3   *
       4   * Copyright (c) Andrew G. Morgan <morgan@ftp.kernel.org>
       5   *
       6   * pamc_converse
       7   */
       8  
       9  #include "libpamc.h"
      10  #include "pam_inline.h"
      11  
      12  /*
      13   * select agent
      14   */
      15  
      16  static int __pamc_select_agent(pamc_handle_t pch, char *agent_id)
      17  {
      18      pamc_agent_t *agent;
      19  
      20      for (agent = pch->chain; agent; agent = agent->next) {
      21  	if (!strcmp(agent->id, agent_id)) {
      22  	    pch->current = agent;
      23  	    return PAM_BPC_TRUE;
      24  	}
      25      }
      26  
      27      D(("failed to locate agent"));
      28      pch->current = NULL;
      29      return PAM_BPC_FALSE;
      30  }
      31  
      32  /*
      33   * pass a binary prompt to the active agent and wait for a reply prompt
      34   */
      35  
      36  int pamc_converse(pamc_handle_t pch, pamc_bp_t *prompt_p)
      37  {
      38      uint32_t size, offset=0;
      39      uint8_t control, raw[PAM_BP_MIN_SIZE];
      40  
      41      D(("called"));
      42  
      43      if (pch == NULL) {
      44  	D(("null pch"));
      45  	goto pamc_converse_failure;
      46      }
      47  
      48      if (prompt_p == NULL) {
      49  	D(("null prompt_p"));
      50  	goto pamc_converse_failure;
      51      }
      52  
      53      if (*prompt_p == NULL) {
      54  	D(("null *prompt_p"));
      55  	goto pamc_converse_failure;
      56      }
      57  
      58      /* from here on, failures are interoperability problems.. */
      59  
      60      size = PAM_BP_SIZE(*prompt_p);
      61      if (size < PAM_BP_MIN_SIZE) {
      62  	D(("problem with size being too short (%u)", size));
      63  	goto pamc_unknown_prompt;
      64      }
      65  
      66      if (PAM_BPC_FOR_CLIENT(*prompt_p) != PAM_BPC_TRUE) {
      67  	D(("*prompt_p is not legal for the client to use"));
      68  	goto pamc_unknown_prompt;
      69      }
      70  
      71      /* do we need to select the agent? */
      72      if ((*prompt_p)->control == PAM_BPC_SELECT) {
      73  	char *rawh;
      74  	size_t i;
      75  	int retval;
      76  
      77  	D(("selecting a specified agent"));
      78  
      79  	rawh = (char *) *prompt_p;
      80  	for (i = PAM_BP_MIN_SIZE; i<size; ++i) {
      81  	    if (rawh[i] == '/') {
      82  		break;
      83  	    }
      84  	}
      85  
      86  	if ( (i >= size)
      87  	     || !__pamc_valid_agent_id(i-PAM_BP_MIN_SIZE,
      88  				       rawh + PAM_BP_MIN_SIZE) ) {
      89  	    goto pamc_unknown_prompt;
      90  	}
      91  
      92  	rawh[i] = '\0';
      93  	retval = pamc_load(pch, PAM_BP_MIN_SIZE + rawh);
      94  	if (retval == PAM_BPC_TRUE) {
      95  	    retval = __pamc_select_agent(pch, PAM_BP_MIN_SIZE + rawh);
      96  	}
      97  	rawh[i] = '/';
      98  
      99  	if (retval != PAM_BPC_TRUE) {
     100  	    goto pamc_unknown_prompt;
     101  	}
     102  
     103  	D(("agent is loaded"));
     104      }
     105  
     106      if (pch->current == NULL) {
     107  	D(("unable to address agent"));
     108  	goto pamc_unknown_prompt;
     109      }
     110  
     111      /* pump all of the prompt into the agent */
     112      do {
     113  	int rval = write(pch->current->writer,
     114  			 offset + (const uint8_t *) (*prompt_p),
     115  			 size - offset);
     116  	if (rval == -1) {
     117  	    switch (errno) {
     118  	    case EINTR:
     119  		break;
     120  	    default:
     121  		D(("problem writing to agent: %m"));
     122  		goto pamc_unknown_prompt;
     123  	    }
     124  	} else {
     125  	    offset += rval;
     126  	}
     127      } while (offset < size);
     128  
     129      D(("whole prompt sent to agent"));
     130  
     131      /* read size and control for response prompt */
     132  
     133      offset = 0;
     134      memset(raw, 0, sizeof(raw));
     135      do {
     136  	int rval;
     137  
     138  	rval = read(pch->current->reader, raw + offset,
     139  		    PAM_BP_MIN_SIZE - offset);
     140  
     141  	if (rval == -1) {
     142  	    switch (errno) {
     143  	    case EINTR:
     144  		break;
     145  	    default:
     146  		D(("problem reading from agent: %m"));
     147  		goto pamc_unknown_prompt;
     148  	    }
     149  	} else if (rval) {
     150  	    offset += rval;
     151  	} else {
     152  	    D(("agent has closed its output pipe - nothing more to read"));
     153  	    goto pamc_converse_failure;
     154  	}
     155      } while (offset < PAM_BP_MIN_SIZE);
     156  
     157      /* construct the whole reply prompt */
     158  
     159      size = PAM_BP_SIZE(raw);
     160      control = PAM_BP_RCONTROL(raw);
     161      pam_overwrite_array(raw);
     162  
     163      D(("agent replied with prompt of size %d and control %u",
     164         size, control));
     165  
     166      PAM_BP_RENEW(prompt_p, control, size - PAM_BP_MIN_SIZE);
     167      if (*prompt_p == NULL) {
     168  	D(("problem making a new prompt for reply"));
     169  	goto pamc_unknown_prompt;
     170      }
     171  
     172      /* read the rest of the reply prompt -- note offset has the correct
     173         value from the previous loop */
     174  
     175      while (offset < size) {
     176  	int rval = read(pch->current->reader, offset + (uint8_t *) *prompt_p,
     177  			size-offset);
     178  
     179  	if (rval == -1) {
     180  	    switch (errno) {
     181  	    case EINTR:
     182  		break;
     183  	    default:
     184  		D(("problem reading from agent: %m"));
     185  		goto pamc_unknown_prompt;
     186  	    }
     187  	} else if (rval) {
     188  	    offset += rval;
     189  	} else {
     190  	    D(("problem reading prompt (%d) with %d to go",
     191  	       size, size-offset));
     192  	    goto pamc_converse_failure;
     193  	}
     194      }
     195  
     196      D(("returning success"));
     197  
     198      return PAM_BPC_TRUE;
     199  
     200  pamc_converse_failure:
     201  
     202      D(("conversation failure"));
     203      PAM_BP_RENEW(prompt_p, 0, 0);
     204      return PAM_BPC_FALSE;
     205  
     206  pamc_unknown_prompt:
     207  
     208      /* the server is trying something that the client does not support */
     209      D(("unknown prompt"));
     210      PAM_BP_RENEW(prompt_p, PAM_BPC_FAIL, 0);
     211      return PAM_BPC_TRUE;
     212  }