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 }