pamc_converse.c 4.4 KB

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