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