1  /*
       2   * This is a small test program for testing libpamc against the
       3   * secret@here agent. It does the same as the test.secret@here perl
       4   * script in this directory, but via the libpamc API.
       5   */
       6  
       7  #include <stdio.h>
       8  #include <stdint.h>
       9  #include <string.h>
      10  #include <security/pam_client.h>
      11  #include <ctype.h>
      12  
      13  struct internal_packet {
      14      int length;
      15      int at;
      16      char *buffer;
      17  };
      18  
      19  
      20  void append_data(struct internal_packet *packet, int extra, const char *data)
      21  {
      22      if ((extra + packet->at) >= packet->length) {
      23  	if (packet->length == 0) {
      24  	    packet->length = 1000;
      25  	}
      26  	/* make sure we have at least a char extra space available */
      27  	while (packet->length <= (extra + packet->at)) {
      28  	    packet->length <<= 1;
      29  	}
      30  	packet->buffer = realloc(packet->buffer, packet->length);
      31  	if (packet->buffer == NULL) {
      32  	    fprintf(stderr, "out of memory\n");
      33  	    exit(1);
      34  	}
      35      }
      36  
      37      if (data != NULL) {
      38  	memcpy(packet->at + packet->buffer, data, extra);
      39      }
      40      packet->at += extra;
      41  
      42      /* assisting string manipulation */
      43      packet->buffer[packet->at] = '\0';
      44  }
      45  
      46  void append_string(struct internal_packet *packet, const char *string,
      47  		   int with_nul)
      48  {
      49      append_data(packet, strlen(string) + (with_nul ? 1:0), string);
      50  }
      51  
      52  char *identify_secret(char *identity)
      53  {
      54      struct internal_packet temp_packet;
      55      FILE *secrets;
      56      int length_id;
      57  
      58      temp_packet.length = temp_packet.at = 0;
      59      temp_packet.buffer = NULL;
      60  
      61      append_string(&temp_packet, "/home/", 0);
      62      append_string(&temp_packet, getlogin(), 0);
      63      append_string(&temp_packet, "/.secret@here", 1);
      64  
      65      secrets = fopen(temp_packet.buffer, "r");
      66      if (secrets == NULL) {
      67  	fprintf(stderr, "server: failed to open\n  [%s]\n",
      68  		temp_packet.buffer);
      69  	exit(1);
      70      }
      71  
      72      length_id = strlen(identity);
      73      for (;;) {
      74  	char *secret = NULL;
      75  	temp_packet.at = 0;
      76  
      77  	if (fgets(temp_packet.buffer, temp_packet.length, secrets) == NULL) {
      78  	    fclose(secrets);
      79  	    return NULL;
      80  	}
      81  
      82  	if (memcmp(temp_packet.buffer, identity, length_id)) {
      83  	    continue;
      84  	}
      85  
      86  	fclose(secrets);
      87  	for (secret=temp_packet.buffer; *secret; ++secret) {
      88  	    if (*secret == ' ' || *secret == '\n' || *secret == '\t') {
      89  		break;
      90  	    }
      91  	}
      92  	for (; *secret; ++secret) {
      93  	    if (!(*secret == ' ' || *secret == '\n' || *secret == '\t')) {
      94  		break;
      95  	    }
      96  	}
      97  
      98  	for (temp_packet.buffer=secret; *temp_packet.buffer;
      99  	     ++temp_packet.buffer) {
     100  	    if (*temp_packet.buffer == ' ' || *temp_packet.buffer == '\n'
     101  		|| *temp_packet.buffer == '\t') {
     102  		break;
     103  	    }
     104  	}
     105  	if (*temp_packet.buffer) {
     106  	    *temp_packet.buffer = '\0';
     107  	}
     108  
     109  	return secret;
     110      }
     111  
     112      /* NOT REACHED */
     113  }
     114  
     115  /*
     116   * This is a hack, and is fundamentally insecure. All our secrets will be
     117   * displayed on the command line for someone doing 'ps' to see. This
     118   * is just for programming convenience in this instance, since this
     119   * program is simply a regression test. The pam_secret module should
     120   * not do this, but make use of md5 routines directly.
     121   */
     122  
     123  char *create_digest(int length, const char *raw)
     124  {
     125      struct internal_packet temp_packet;
     126      FILE *pipe;
     127  
     128      temp_packet.length = temp_packet.at = 0;
     129      temp_packet.buffer = NULL;
     130  
     131      append_string(&temp_packet, "echo -n '", 0);
     132      append_string(&temp_packet, raw, 0);
     133      append_string(&temp_packet, "'|/usr/bin/md5sum -", 1);
     134  
     135      fprintf(stderr, "am attempting to run [%s]\n", temp_packet.buffer);
     136  
     137      pipe = popen(temp_packet.buffer, "r");
     138      if (pipe == NULL) {
     139  	fprintf(stderr, "server: failed to run\n  [%s]\n", temp_packet.buffer);
     140  	exit(1);
     141      }
     142  
     143      temp_packet.at = 0;
     144      append_data(&temp_packet, 32, NULL);
     145  
     146      if (fgets(temp_packet.buffer, 33, pipe) == NULL) {
     147  	fprintf(stderr, "server: failed to read digest\n");
     148  	exit(1);
     149      }
     150      if (strlen(temp_packet.buffer) != 32) {
     151  	fprintf(stderr, "server: digest was not 32 chars?? [%s]\n",
     152  		temp_packet.buffer);
     153  	exit(1);
     154      }
     155  
     156      fclose(pipe);
     157  
     158      return temp_packet.buffer;
     159  }
     160  
     161  void packet_to_prompt(pamc_bp_t *prompt_p, uint8_t control,
     162  		      struct internal_packet *packet)
     163  {
     164      PAM_BP_RENEW(prompt_p, control, packet->at);
     165      PAM_BP_FILL(*prompt_p, 0, packet->at, packet->buffer);
     166      packet->at = 0;
     167  }
     168  
     169  void prompt_to_packet(pamc_bp_t prompt, struct internal_packet *packet)
     170  {
     171      int data_length;
     172  
     173      data_length = PAM_BP_LENGTH(prompt);
     174      packet->at = 0;
     175      append_data(packet, data_length, NULL);
     176  
     177      PAM_BP_EXTRACT(prompt, 0, data_length, packet->buffer);
     178  
     179      fprintf(stderr, "server received[%d]: {%d|0x%.2x|%s}\n",
     180  	    data_length,
     181  	    PAM_BP_SIZE(prompt), PAM_BP_RCONTROL(prompt),
     182  	    PAM_BP_RDATA(prompt));
     183  }
     184  
     185  int main(int argc, char **argv)
     186  {
     187      pamc_handle_t pch;
     188      pamc_bp_t prompt = NULL;
     189      struct internal_packet packet_data, *packet;
     190      char *temp_string, *secret, *user, *a_cookie, *seqid, *digest;
     191      const char *cookie = "123451234512345";
     192      int retval;
     193  
     194      packet = &packet_data;
     195      packet->length = 0;
     196      packet->at = 0;
     197      packet->buffer = NULL;
     198  
     199      pch = pamc_start();
     200      if (pch == NULL) {
     201  	fprintf(stderr, "server: unable to get a handle from libpamc\n");
     202  	exit(1);
     203      }
     204  
     205      temp_string = getlogin();
     206      if (temp_string == NULL) {
     207  	fprintf(stderr, "server: who are you?\n");
     208  	exit(1);
     209      }
     210  #define DOMAIN "@local.host"
     211      user = malloc(1+strlen(temp_string)+strlen(DOMAIN));
     212      if (user == NULL) {
     213  	fprintf(stderr, "server: out of memory for user id\n");
     214  	exit(1);
     215      }
     216      sprintf(user, "%s%s", temp_string, DOMAIN);
     217  
     218      append_string(packet, "secret@here/", 0);
     219      append_string(packet, user, 0);
     220      append_string(packet, "|", 0);
     221      append_string(packet, cookie, 0);
     222      packet_to_prompt(&prompt, PAM_BPC_SELECT, packet);
     223  
     224      /* get the library to accept the first packet (which should load
     225         the secret@here agent) */
     226  
     227      retval = pamc_converse(pch, &prompt);
     228      fprintf(stderr, "server: after conversation\n");
     229      if (PAM_BP_RCONTROL(prompt) != PAM_BPC_OK) {
     230  	fprintf(stderr, "server: prompt had unexpected control type: %u\n",
     231  		PAM_BP_RCONTROL(prompt));
     232  	exit(1);
     233      }
     234  
     235      fprintf(stderr, "server: got a prompt back\n");
     236  
     237      prompt_to_packet(prompt, packet);
     238  
     239      temp_string = strtok(packet->buffer, "|");
     240      if (temp_string == NULL) {
     241  	fprintf(stderr, "server: prompt does not contain anything");
     242  	exit(1);
     243      }
     244      seqid = strdup(temp_string);
     245      if (seqid == NULL) {
     246  	fprintf(stderr, "server: unable to store sequence id\n");
     247      }
     248  
     249      temp_string = strtok(NULL, "|");
     250      if (temp_string == NULL) {
     251  	fprintf(stderr, "server: no cookie from agent\n");
     252  	exit(1);
     253      }
     254      a_cookie = strdup(temp_string);
     255      if (a_cookie == NULL) {
     256  	fprintf(stderr, "server: no memory to store agent cookie\n");
     257  	exit(1);
     258      }
     259  
     260      fprintf(stderr, "server: agent responded with {%s|%s}\n", seqid, a_cookie);
     261      secret = identify_secret(user);
     262      fprintf(stderr, "server: secret=%s\n", secret);
     263  
     264      /* now, we construct the response */
     265      packet->at = 0;
     266      append_string(packet, a_cookie, 0);
     267      append_string(packet, "|", 0);
     268      append_string(packet, cookie, 0);
     269      append_string(packet, "|", 0);
     270      append_string(packet, secret, 0);
     271  
     272      fprintf(stderr, "server: get digest of %s\n", packet->buffer);
     273  
     274      digest = create_digest(packet->at, packet->buffer);
     275  
     276      fprintf(stderr, "server: secret=%s, digest=%s\n", secret, digest);
     277  
     278      packet->at = 0;
     279      append_string(packet, seqid, 0);
     280      append_string(packet, "|", 0);
     281      append_string(packet, digest, 0);
     282      packet_to_prompt(&prompt, PAM_BPC_OK, packet);
     283  
     284      retval = pamc_converse(pch, &prompt);
     285      fprintf(stderr, "server: after 2nd conversation\n");
     286      if (PAM_BP_RCONTROL(prompt) != PAM_BPC_DONE) {
     287  	fprintf(stderr, "server: 2nd prompt had unexpected control type: %u\n",
     288  		PAM_BP_RCONTROL(prompt));
     289  	exit(1);
     290      }
     291  
     292      prompt_to_packet(prompt, packet);
     293      PAM_BP_RENEW(&prompt, 0, 0);
     294  
     295      temp_string = strtok(packet->buffer, "|");
     296      if (temp_string == NULL) {
     297  	fprintf(stderr, "no digest from agent\n");
     298  	exit(1);
     299      }
     300      temp_string = strdup(temp_string);
     301  
     302      packet->at = 0;
     303      append_string(packet, secret, 0);
     304      append_string(packet, "|", 0);
     305      append_string(packet, cookie, 0);
     306      append_string(packet, "|", 0);
     307      append_string(packet, a_cookie, 0);
     308  
     309      fprintf(stderr, "server: get digest of %s\n", packet->buffer);
     310  
     311      digest = create_digest(packet->at, packet->buffer);
     312  
     313      fprintf(stderr, "server: digest=%s\n", digest);
     314  
     315      if (strcmp(digest, temp_string)) {
     316  	fprintf(stderr, "server: agent doesn't know the secret\n");
     317  	fprintf(stderr, "server: agent says:  [%s]\n"
     318  	                "server: server says: [%s]\n", temp_string, digest);
     319  	exit(1);
     320      } else {
     321  	fprintf(stderr, "server: agent seems to know the secret\n");
     322  
     323  	packet->at = 0;
     324  	append_string(packet, cookie, 0);
     325  	append_string(packet, "|", 0);
     326  	append_string(packet, secret, 0);
     327  	append_string(packet, "|", 0);
     328  	append_string(packet, a_cookie, 0);
     329  
     330  	digest = create_digest(packet->at, packet->buffer);
     331  
     332  	fprintf(stderr, "server: putenv(\"AUTH_SESSION_TICKET=%s\")\n",
     333  		digest);
     334      }
     335  
     336  
     337      retval = pamc_end(&pch);
     338  
     339      fprintf(stderr, "server: agent(s) were %shappy to terminate\n",
     340  	    retval == PAM_BPC_TRUE ? "":"un");
     341  
     342      exit(!retval);
     343  }