4 * Copyright (c) 1999 Andrew G. Morgan <morgan@ftp.kernel.org>
11 static int __pamc_exec_agent(pamc_handle_t pch, pamc_agent_t *agent)
14 int found_agent, length, reset_length, to_agent[2], from_agent[2];
15 int return_code = PAM_BPC_FAIL;
17 if (agent->id[agent->id_length] != '\0') {
18 PAM_BP_ASSERT("libpamc: internal error agent_id not terminated");
21 for (length=0; (length < agent->id_length); ++length) {
22 switch (agent->id[length]) {
24 D(("ill formed agent id"));
29 /* enough memory for any path + this agent */
30 reset_length = 3 + pch->max_path + agent->id_length;
31 D(("reset_length = %d (3+%d+%d)",
32 reset_length, pch->max_path, agent->id_length));
33 full_path = malloc(reset_length);
34 if (full_path == NULL) {
35 D(("no memory for agent path"));
40 for (length=0; pch->agent_paths[length]; ++length) {
43 D(("path: [%s]", pch->agent_paths[length]));
44 D(("agent id: [%s]", agent->id));
46 sprintf(full_path, "%s/%s", pch->agent_paths[length], agent->id);
48 D(("looking for agent here: [%s]\n", full_path));
49 if (stat(full_path, &buf) == 0) {
57 D(("no agent was found"));
62 D(("failed to open pipe to agent"));
66 if (pipe(from_agent)) {
67 D(("failed to open pipe from agent"));
72 if (agent->pid == -1) {
74 D(("failed to fork for agent"));
75 goto close_both_pipes;
77 } else if (agent->pid == 0) {
81 dup2(from_agent[1], STDOUT_FILENO);
82 dup2(to_agent[0], STDIN_FILENO);
84 /* we close all of the files that have filedescriptors lower
85 and equal to twice the highest we have seen, The idea is
86 that we don't want to leak filedescriptors to agents from a
87 privileged client application.
89 XXX - this is a heuristic at this point. There is a growing
90 need for an extra 'set param' libpamc function, that could
91 be used to supply info like the highest fd to close etc..
94 if (from_agent[1] > pch->highest_fd_to_close) {
95 pch->highest_fd_to_close = 2*from_agent[1];
98 for (i=0; i <= pch->highest_fd_to_close; ++i) {
103 /* only these three remain open */
106 (void) close(i); /* don't care if its not open */
110 /* we make no attempt to drop other privileges - this library
111 has no idea how that would be done in the general case. It
112 is up to the client application (when calling
113 pamc_converse) to make sure no privilege will leak into an
114 (untrusted) agent. */
116 /* we propogate no environment - future versions of this
117 library may have the ability to audit all agent
120 D(("exec'ing agent %s", full_path));
121 execle(full_path, "pam-agent", NULL, NULL);
129 close(from_agent[1]);
131 agent->writer = to_agent[1];
132 agent->reader = from_agent[0];
134 return_code = PAM_BPC_TRUE;
135 goto free_and_return;
138 close(from_agent[0]);
139 close(from_agent[1]);
146 memset(full_path, 0, reset_length);
149 D(("returning %d", return_code));
155 * has the named agent been loaded?
158 static int __pamc_agent_is_enabled(pamc_handle_t pch, const char *agent_id)
162 for (agent = pch->chain; agent; agent = agent->next) {
163 if (!strcmp(agent->id, agent_id)) {
164 D(("agent already loaded"));
169 D(("agent is not loaded"));
170 return PAM_BPC_FALSE;
174 * has the named agent been disabled?
177 static int __pamc_agent_is_disabled(pamc_handle_t pch, const char *agent_id)
179 pamc_blocked_t *blocked;
181 for (blocked=pch->blocked_agents; blocked; blocked = blocked->next) {
182 if (!strcmp(agent_id, blocked->id)) {
183 D(("agent is disabled"));
188 D(("agent is not disabled"));
189 return PAM_BPC_FALSE;
196 int pamc_disable(pamc_handle_t pch, const char *agent_id)
198 pamc_blocked_t *block;
202 return PAM_BPC_FALSE;
205 if (agent_id == NULL) {
206 D(("agent_id is NULL"));
207 return PAM_BPC_FALSE;
210 if (__pamc_agent_is_enabled(pch, agent_id) != PAM_BPC_FALSE) {
211 D(("agent is already loaded"));
212 return PAM_BPC_FALSE;
215 if (__pamc_agent_is_disabled(pch, agent_id) != PAM_BPC_FALSE) {
216 D(("agent is already disabled"));
220 block = calloc(1, sizeof(pamc_blocked_t));
222 D(("no memory for new blocking structure"));
223 return PAM_BPC_FALSE;
226 block->id = malloc(1 + strlen(agent_id));
227 if (block->id == NULL) {
228 D(("no memory for agent id"));
230 return PAM_BPC_FALSE;
233 strcpy(block->id, agent_id);
234 block->next = pch->blocked_agents;
235 pch->blocked_agents = block;
241 * force the loading of a particular agent
244 int pamc_load(pamc_handle_t pch, const char *agent_id)
249 /* santity checking */
253 return PAM_BPC_FALSE;
256 if (agent_id == NULL) {
257 D(("agent_id is NULL"));
258 return PAM_BPC_FALSE;
261 if (__pamc_agent_is_disabled(pch, agent_id) != PAM_BPC_FALSE) {
262 D(("sorry agent is disabled"));
263 return PAM_BPC_FALSE;
266 length = strlen(agent_id);
268 /* scan list to see if agent is loaded */
270 if (__pamc_agent_is_enabled(pch, agent_id) == PAM_BPC_TRUE) {
271 D(("no need to load an already loaded agent (%s)", agent_id));
275 /* not in the list, so we need to load it and add it to the head
278 agent = calloc(1, sizeof(pamc_agent_t));
280 D(("no memory for new agent"));
281 return PAM_BPC_FALSE;
283 agent->id = calloc(1, 1+length);
284 if (agent->id == NULL) {
285 D(("no memory for new agent's id"));
286 goto fail_free_agent;
288 memcpy(agent->id, agent_id, length);
289 agent->id[length] = '\0';
290 agent->id_length = length;
292 if (__pamc_exec_agent(pch, agent) != PAM_BPC_TRUE) {
293 D(("unable to exec agent"));
294 goto fail_free_agent_id;
297 agent->next = pch->chain;
304 memset(agent->id, 0, agent->id_length);
307 memset(agent, 0, sizeof(*agent));
312 return PAM_BPC_FALSE;
316 * what's a valid agent name?
319 int __pamc_valid_agent_id(int id_length, const char *id)
323 for (i=post=0 ; i < id_length; ++i) {
326 if (isalpha(ch) || isdigit(ch) || (ch == '_')) {
328 } else if (post && (ch == '.')) {
330 } else if ((i > 1) && (!post) && (ch == '@')) {
333 D(("id=%s contains '%c' which is illegal", id, ch));
339 D(("length of id is 0"));
342 return 1; /* id is valid */
347 * building a tree of available agent names
350 static pamc_id_node_t *__pamc_add_node(pamc_id_node_t *root, const char *id,
357 if ((cmp = strcmp(id, root->agent_id))) {
359 root->right = __pamc_add_node(root->right, id,
360 &(root->child_count));
362 root->left = __pamc_add_node(root->left, id,
363 &(root->child_count));
371 pamc_id_node_t *node = calloc(1, sizeof(pamc_id_node_t));
374 node->agent_id = malloc(1+strlen(id));
375 if (node->agent_id) {
376 strcpy(node->agent_id, id);
389 * drop all of the tree and any remaining ids
392 static pamc_id_node_t *__pamc_liberate_nodes(pamc_id_node_t *tree)
395 if (tree->agent_id) {
396 free(tree->agent_id);
397 tree->agent_id = NULL;
400 tree->left = __pamc_liberate_nodes(tree->left);
401 tree->right = __pamc_liberate_nodes(tree->right);
403 tree->child_count = 0;
411 * fill a list with the contents of the tree (in ascii order)
414 static void __pamc_fill_list_from_tree(pamc_id_node_t *tree, char **agent_list,
418 __pamc_fill_list_from_tree(tree->left, agent_list, counter);
419 agent_list[(*counter)++] = tree->agent_id;
420 tree->agent_id = NULL;
421 __pamc_fill_list_from_tree(tree->right, agent_list, counter);
426 * get a list of the available agents
429 char **pamc_list_agents(pamc_handle_t pch)
431 int i, total_agent_count=0;
432 pamc_id_node_t *tree = NULL;
435 /* loop over agent paths */
437 for (i=0; pch->agent_paths[i]; ++i) {
440 dir = opendir(pch->agent_paths[i]);
444 while ((item = readdir(dir))) {
446 /* this is a cheat on recognizing agent_ids */
447 if (!__pamc_valid_agent_id(strlen(item->d_name),
452 tree = __pamc_add_node(tree, item->d_name, &total_agent_count);
459 /* now, we build a list of ids */
460 D(("total of %d available agents\n", total_agent_count));
462 agent_list = calloc(total_agent_count+1, sizeof(char *));
466 __pamc_fill_list_from_tree(tree, agent_list, &counter);
467 if (counter != total_agent_count) {
468 PAM_BP_ASSERT("libpamc: internal error transcribing tree");
471 D(("no memory for agent list"));
474 __pamc_liberate_nodes(tree);