(root)/
Linux-PAM-1.5.3/
libpamc/
pamc_load.c
       1  /*
       2   * $Id$
       3   *
       4   * Copyright (c) 1999 Andrew G. Morgan <morgan@ftp.kernel.org>
       5   *
       6   * pamc_load
       7   */
       8  
       9  #include "libpamc.h"
      10  #include "pam_inline.h"
      11  
      12  static int __pamc_exec_agent(pamc_handle_t pch, pamc_agent_t *agent)
      13  {
      14      char *full_path;
      15      int found_agent, length, reset_length, to_agent[2], from_agent[2];
      16      int return_code = PAM_BPC_FAIL;
      17  
      18      if (agent->id[agent->id_length] != '\0') {
      19  	PAM_BP_ASSERT("libpamc: internal error agent_id not terminated");
      20      }
      21  
      22      for (length=0; (length < agent->id_length); ++length) {
      23  	switch (agent->id[length]) {
      24  	case '/':
      25  	    D(("ill formed agent id"));
      26  	    return PAM_BPC_FAIL;
      27  	}
      28      }
      29  
      30      /* enough memory for any path + this agent */
      31      reset_length = 3 + pch->max_path + agent->id_length;
      32      D(("reset_length = %d (3+%d+%d)",
      33         reset_length, pch->max_path, agent->id_length));
      34      full_path = malloc(reset_length);
      35      if (full_path == NULL) {
      36  	D(("no memory for agent path"));
      37  	return PAM_BPC_FAIL;
      38      }
      39  
      40      found_agent = 0;
      41      for (length=0; pch->agent_paths[length]; ++length) {
      42  	struct stat buf;
      43  
      44  	D(("path: [%s]", pch->agent_paths[length]));
      45  	D(("agent id: [%s]", agent->id));
      46  
      47  	sprintf(full_path, "%s/%s", pch->agent_paths[length], agent->id);
      48  
      49  	D(("looking for agent here: [%s]\n", full_path));
      50  	if (stat(full_path, &buf) == 0) {
      51  	    D(("file existis"));
      52  	    found_agent = 1;
      53  	    break;
      54  	}
      55      }
      56  
      57      if (! found_agent) {
      58  	D(("no agent was found"));
      59  	goto free_and_return;
      60      }
      61  
      62      if (pipe(to_agent)) {
      63  	D(("failed to open pipe to agent"));
      64  	goto free_and_return;
      65      }
      66  
      67      if (pipe(from_agent)) {
      68  	D(("failed to open pipe from agent"));
      69  	goto close_the_agent;
      70      }
      71  
      72      agent->pid = fork();
      73      if (agent->pid == -1) {
      74  
      75  	D(("failed to fork for agent"));
      76  	goto close_both_pipes;
      77  
      78      } else if (agent->pid == 0) {
      79  
      80  	int i;
      81  
      82  	dup2(from_agent[1], STDOUT_FILENO);
      83  	dup2(to_agent[0], STDIN_FILENO);
      84  
      85  	/* we close all of the files that have filedescriptors lower
      86  	   and equal to twice the highest we have seen, The idea is
      87  	   that we don't want to leak filedescriptors to agents from a
      88  	   privileged client application.
      89  
      90  	   XXX - this is a heuristic at this point. There is a growing
      91  	    need for an extra 'set param' libpamc function, that could
      92  	    be used to supply info like the highest fd to close etc..
      93  	*/
      94  
      95  	if (from_agent[1] > pch->highest_fd_to_close) {
      96  	    pch->highest_fd_to_close = 2*from_agent[1];
      97  	}
      98  
      99  	for (i=0; i <= pch->highest_fd_to_close; ++i) {
     100  	    switch (i) {
     101  	    case STDOUT_FILENO:
     102  	    case STDERR_FILENO:
     103  	    case STDIN_FILENO:
     104  		/* only these three remain open */
     105  		break;
     106  	    default:
     107  		(void) close(i); /* don't care if its not open */
     108  	    }
     109  	}
     110  
     111  	/* we make no attempt to drop other privileges - this library
     112  	   has no idea how that would be done in the general case. It
     113  	   is up to the client application (when calling
     114  	   pamc_converse) to make sure no privilege will leak into an
     115  	   (untrusted) agent. */
     116  
     117  	/* we propagate no environment - future versions of this
     118             library may have the ability to audit all agent
     119             transactions. */
     120  
     121  	D(("exec'ing agent %s", full_path));
     122  	execle(full_path, "pam-agent", NULL, NULL);
     123  
     124  	D(("exec failed"));
     125  	_exit(1);
     126  
     127      }
     128  
     129      close(to_agent[0]);
     130      close(from_agent[1]);
     131  
     132      agent->writer = to_agent[1];
     133      agent->reader = from_agent[0];
     134  
     135      return_code = PAM_BPC_TRUE;
     136      goto free_and_return;
     137  
     138  close_both_pipes:
     139      close(from_agent[0]);
     140      close(from_agent[1]);
     141  
     142  close_the_agent:
     143      close(to_agent[0]);
     144      close(to_agent[1]);
     145  
     146  free_and_return:
     147      pam_overwrite_n(full_path, reset_length);
     148      free(full_path);
     149  
     150      D(("returning %d", return_code));
     151  
     152      return return_code;
     153  }
     154  
     155  /*
     156   * has the named agent been loaded?
     157   */
     158  
     159  static int __pamc_agent_is_enabled(pamc_handle_t pch, const char *agent_id)
     160  {
     161      pamc_agent_t *agent;
     162  
     163      for (agent = pch->chain; agent; agent = agent->next) {
     164  	if (!strcmp(agent->id, agent_id)) {
     165  	    D(("agent already loaded"));
     166  	    return PAM_BPC_TRUE;
     167  	}
     168      }
     169  
     170      D(("agent is not loaded"));
     171      return PAM_BPC_FALSE;
     172  }
     173  
     174  /*
     175   * has the named agent been disabled?
     176   */
     177  
     178  static int __pamc_agent_is_disabled(pamc_handle_t pch, const char *agent_id)
     179  {
     180      pamc_blocked_t *blocked;
     181  
     182      for (blocked=pch->blocked_agents; blocked; blocked = blocked->next) {
     183  	if (!strcmp(agent_id, blocked->id)) {
     184  	    D(("agent is disabled"));
     185  	    return PAM_BPC_TRUE;
     186  	}
     187      }
     188  
     189      D(("agent is not disabled"));
     190      return PAM_BPC_FALSE;
     191  }
     192  
     193  /*
     194   * disable an agent
     195   */
     196  
     197  int pamc_disable(pamc_handle_t pch, const char *agent_id)
     198  {
     199      pamc_blocked_t *block;
     200  
     201      if (pch == NULL) {
     202  	D(("pch is NULL"));
     203  	return PAM_BPC_FALSE;
     204      }
     205  
     206      if (agent_id == NULL) {
     207  	D(("agent_id is NULL"));
     208  	return PAM_BPC_FALSE;
     209      }
     210  
     211      if (__pamc_agent_is_enabled(pch, agent_id) != PAM_BPC_FALSE) {
     212  	D(("agent is already loaded"));
     213  	return PAM_BPC_FALSE;
     214      }
     215  
     216      if (__pamc_agent_is_disabled(pch, agent_id) != PAM_BPC_FALSE) {
     217  	D(("agent is already disabled"));
     218  	return PAM_BPC_TRUE;
     219      }
     220  
     221      block = calloc(1, sizeof(pamc_blocked_t));
     222      if (block == NULL) {
     223  	D(("no memory for new blocking structure"));
     224  	return PAM_BPC_FALSE;
     225      }
     226  
     227      block->id =	malloc(1 + strlen(agent_id));
     228      if (block->id == NULL) {
     229  	D(("no memory for agent id"));
     230  	free(block);
     231  	return PAM_BPC_FALSE;
     232      }
     233  
     234      strcpy(block->id, agent_id);
     235      block->next = pch->blocked_agents;
     236      pch->blocked_agents = block;
     237  
     238      return PAM_BPC_TRUE;
     239  }
     240  
     241  /*
     242   * force the loading of a particular agent
     243   */
     244  
     245  int pamc_load(pamc_handle_t pch, const char *agent_id)
     246  {
     247      pamc_agent_t *agent;
     248      int length;
     249  
     250      /* santity checking */
     251  
     252      if (pch == NULL) {
     253  	D(("pch is NULL"));
     254  	return PAM_BPC_FALSE;
     255      }
     256  
     257      if (agent_id == NULL) {
     258  	D(("agent_id is NULL"));
     259  	return PAM_BPC_FALSE;
     260      }
     261  
     262      if (__pamc_agent_is_disabled(pch, agent_id) != PAM_BPC_FALSE) {
     263  	D(("sorry agent is disabled"));
     264  	return PAM_BPC_FALSE;
     265      }
     266  
     267      length = strlen(agent_id);
     268  
     269      /* scan list to see if agent is loaded */
     270  
     271      if (__pamc_agent_is_enabled(pch, agent_id) == PAM_BPC_TRUE) {
     272  	D(("no need to load an already loaded agent (%s)", agent_id));
     273  	return PAM_BPC_TRUE;
     274      }
     275  
     276      /* not in the list, so we need to load it and add it to the head
     277         of the chain */
     278  
     279      agent = calloc(1, sizeof(pamc_agent_t));
     280      if (agent == NULL) {
     281  	D(("no memory for new agent"));
     282  	return PAM_BPC_FALSE;
     283      }
     284      agent->id = calloc(1, 1+length);
     285      if (agent->id == NULL) {
     286  	D(("no memory for new agent's id"));
     287  	goto fail_free_agent;
     288      }
     289      memcpy(agent->id, agent_id, length);
     290      agent->id[length] = '\0';
     291      agent->id_length = length;
     292  
     293      if (__pamc_exec_agent(pch, agent) != PAM_BPC_TRUE) {
     294  	D(("unable to exec agent"));
     295  	goto fail_free_agent_id;
     296      }
     297  
     298      agent->next = pch->chain;
     299      pch->chain = agent;
     300  
     301      return PAM_BPC_TRUE;
     302  
     303  fail_free_agent_id:
     304  
     305      pam_overwrite_n(agent->id, agent->id_length);
     306      free(agent->id);
     307  
     308      pam_overwrite_object(agent);
     309  
     310  fail_free_agent:
     311  
     312      free(agent);
     313      return PAM_BPC_FALSE;
     314  }
     315  
     316  /*
     317   * what's a valid agent name?
     318   */
     319  
     320  int __pamc_valid_agent_id(int id_length, const char *id)
     321  {
     322      int post, i;
     323  
     324      for (i=post=0 ; i < id_length; ++i) {
     325  	int ch = id[i++];
     326  
     327  	if (isalpha(ch) || isdigit(ch) || (ch == '_')) {
     328  	    continue;
     329  	} else if (post && (ch == '.')) {
     330  	    continue;
     331  	} else if ((i > 1) && (!post) && (ch == '@')) {
     332  	    post = 1;
     333  	} else {
     334  	    D(("id=%s contains '%c' which is illegal", id, ch));
     335  	    return 0;
     336  	}
     337      }
     338  
     339      if (!i) {
     340  	D(("length of id is 0"));
     341  	return 0;
     342      } else {
     343  	return 1;                       /* id is valid */
     344      }
     345  }
     346  
     347  /*
     348   * building a tree of available agent names
     349   */
     350  
     351  static pamc_id_node_t *__pamc_add_node(pamc_id_node_t *root, const char *id,
     352  				       int *counter)
     353  {
     354      if (root) {
     355  
     356  	int cmp;
     357  
     358  	if ((cmp = strcmp(id, root->agent_id))) {
     359  	    if (cmp > 0) {
     360  		root->right = __pamc_add_node(root->right, id,
     361  					      &(root->child_count));
     362  	    } else {
     363  		root->left = __pamc_add_node(root->left, id,
     364  					     &(root->child_count));
     365  	    }
     366  	}
     367  
     368  	return root;
     369  
     370      } else {
     371  
     372  	pamc_id_node_t *node = calloc(1, sizeof(pamc_id_node_t));
     373  
     374  	if (node) {
     375  	    node->agent_id = malloc(1+strlen(id));
     376  	    if (node->agent_id) {
     377  		strcpy(node->agent_id, id);
     378  	    } else {
     379  		free(node);
     380  		node = NULL;
     381  	    }
     382  	}
     383  
     384  	(*counter)++;
     385  	return node;
     386      }
     387  }
     388  
     389  /*
     390   * drop all of the tree and any remaining ids
     391   */
     392  
     393  static pamc_id_node_t *__pamc_liberate_nodes(pamc_id_node_t *tree)
     394  {
     395      if (tree) {
     396  	if (tree->agent_id) {
     397  	    free(tree->agent_id);
     398  	    tree->agent_id = NULL;
     399  	}
     400  
     401  	tree->left = __pamc_liberate_nodes(tree->left);
     402  	tree->right = __pamc_liberate_nodes(tree->right);
     403  
     404  	tree->child_count = 0;
     405  	free(tree);
     406      }
     407  
     408      return NULL;
     409  }
     410  
     411  /*
     412   * fill a list with the contents of the tree (in ascii order)
     413   */
     414  
     415  static void __pamc_fill_list_from_tree(pamc_id_node_t *tree, char **agent_list,
     416  				       int *counter)
     417  {
     418      if (tree) {
     419  	__pamc_fill_list_from_tree(tree->left, agent_list, counter);
     420  	agent_list[(*counter)++] = tree->agent_id;
     421  	tree->agent_id = NULL;
     422  	__pamc_fill_list_from_tree(tree->right, agent_list, counter);
     423      }
     424  }
     425  
     426  /*
     427   * get a list of the available agents
     428   */
     429  
     430  char **pamc_list_agents(pamc_handle_t pch)
     431  {
     432      int i, total_agent_count=0;
     433      pamc_id_node_t *tree = NULL;
     434      char **agent_list;
     435  
     436      /* loop over agent paths */
     437  
     438      for (i=0; pch->agent_paths[i]; ++i) {
     439  	DIR *dir;
     440  
     441  	dir = opendir(pch->agent_paths[i]);
     442  	if (dir) {
     443  	    struct dirent *item;
     444  
     445  	    while ((item = readdir(dir))) {
     446  
     447  		/* this is a cheat on recognizing agent_ids */
     448  		if (!__pamc_valid_agent_id(strlen(item->d_name),
     449  					   item->d_name)) {
     450  		    continue;
     451  		}
     452  
     453  		tree = __pamc_add_node(tree, item->d_name, &total_agent_count);
     454  	    }
     455  
     456  	    closedir(dir);
     457  	}
     458      }
     459  
     460      /* now, we build a list of ids */
     461      D(("total of %d available agents\n", total_agent_count));
     462  
     463      agent_list = calloc(total_agent_count+1, sizeof(char *));
     464      if (agent_list) {
     465  	int counter=0;
     466  
     467  	__pamc_fill_list_from_tree(tree, agent_list, &counter);
     468  	if (counter != total_agent_count) {
     469  	    PAM_BP_ASSERT("libpamc: internal error transcribing tree");
     470  	}
     471      } else {
     472  	D(("no memory for agent list"));
     473      }
     474  
     475      __pamc_liberate_nodes(tree);
     476  
     477      return agent_list;
     478  }