+ case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
+ /*
+ * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
+ */
+ {
+ struct RSAKey *key;
+ int len, nkeys;
+ int i;
+
+ /*
+ * Count up the number and length of keys we hold.
+ */
+ len = nkeys = 0;
+ for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
+ nkeys++;
+ len += 4; /* length field */
+ len += ssh1_bignum_length(key->exponent);
+ len += ssh1_bignum_length(key->modulus);
+ len += 4 + strlen(key->comment);
+ }
+
+ /*
+ * Packet header is the obvious five bytes, plus four
+ * bytes for the key count.
+ */
+ len += 5 + 4;
+ if (len > AGENT_MAX_MSGLEN)
+ goto failure; /* aaargh! too much stuff! */
+ PUT_32BIT(ret, len - 4);
+ ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
+ PUT_32BIT(ret + 5, nkeys);
+ p = ret + 5 + 4;
+ for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
+ PUT_32BIT(p, bignum_bitcount(key->modulus));
+ p += 4;
+ p += ssh1_write_bignum(p, key->exponent);
+ p += ssh1_write_bignum(p, key->modulus);
+ PUT_32BIT(p, strlen(key->comment));
+ memcpy(p + 4, key->comment, strlen(key->comment));
+ p += 4 + strlen(key->comment);
+ }
+ }
+ break;
+ case SSH2_AGENTC_REQUEST_IDENTITIES:
+ /*
+ * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
+ */
+ {
+ struct ssh2_userkey *key;
+ int len, nkeys;
+ unsigned char *blob;
+ int bloblen;
+ int i;
+
+ /*
+ * Count up the number and length of keys we hold.
+ */
+ len = nkeys = 0;
+ for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
+ nkeys++;
+ len += 4; /* length field */
+ blob = key->alg->public_blob(key->data, &bloblen);
+ len += bloblen;
+ sfree(blob);
+ len += 4 + strlen(key->comment);
+ }
+
+ /*
+ * Packet header is the obvious five bytes, plus four
+ * bytes for the key count.
+ */
+ len += 5 + 4;
+ if (len > AGENT_MAX_MSGLEN)
+ goto failure; /* aaargh! too much stuff! */
+ PUT_32BIT(ret, len - 4);
+ ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
+ PUT_32BIT(ret + 5, nkeys);
+ p = ret + 5 + 4;
+ for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
+ blob = key->alg->public_blob(key->data, &bloblen);
+ PUT_32BIT(p, bloblen);
+ p += 4;
+ memcpy(p, blob, bloblen);
+ p += bloblen;
+ sfree(blob);
+ PUT_32BIT(p, strlen(key->comment));
+ memcpy(p + 4, key->comment, strlen(key->comment));
+ p += 4 + strlen(key->comment);
+ }
+ }
+ break;
+ case SSH1_AGENTC_RSA_CHALLENGE:
+ /*
+ * Reply with either SSH1_AGENT_RSA_RESPONSE or
+ * SSH_AGENT_FAILURE, depending on whether we have that key
+ * or not.
+ */
+ {
+ struct RSAKey reqkey, *key;
+ Bignum challenge, response;
+ unsigned char response_source[48], response_md5[16];
+ struct MD5Context md5c;
+ int i, len;
+
+ p += 4;
+ p += ssh1_read_bignum(p, &reqkey.exponent);
+ p += ssh1_read_bignum(p, &reqkey.modulus);
+ p += ssh1_read_bignum(p, &challenge);
+ memcpy(response_source + 32, p, 16);
+ p += 16;
+ if (GET_32BIT(p) != 1 ||
+ (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
+ freebn(reqkey.exponent);
+ freebn(reqkey.modulus);
+ freebn(challenge);
+ goto failure;
+ }
+ response = rsadecrypt(challenge, key);
+ for (i = 0; i < 32; i++)
+ response_source[i] = bignum_byte(response, 31 - i);
+
+ MD5Init(&md5c);
+ MD5Update(&md5c, response_source, 48);
+ MD5Final(response_md5, &md5c);
+ memset(response_source, 0, 48); /* burn the evidence */
+ freebn(response); /* and that evidence */
+ freebn(challenge); /* yes, and that evidence */
+ freebn(reqkey.exponent); /* and free some memory ... */
+ freebn(reqkey.modulus); /* ... while we're at it. */
+
+ /*
+ * Packet is the obvious five byte header, plus sixteen
+ * bytes of MD5.
+ */
+ len = 5 + 16;
+ PUT_32BIT(ret, len - 4);
+ ret[4] = SSH1_AGENT_RSA_RESPONSE;
+ memcpy(ret + 5, response_md5, 16);
+ }
+ break;
+ case SSH2_AGENTC_SIGN_REQUEST:
+ /*
+ * Reply with either SSH2_AGENT_SIGN_RESPONSE or
+ * SSH_AGENT_FAILURE, depending on whether we have that key
+ * or not.
+ */
+ {
+ struct ssh2_userkey *key;
+ struct blob b;
+ unsigned char *data, *signature;
+ int datalen, siglen, len;
+
+ b.len = GET_32BIT(p);
+ p += 4;
+ b.blob = p;
+ p += b.len;
+ datalen = GET_32BIT(p);
+ p += 4;
+ data = p;
+ key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
+ if (!key)
+ goto failure;
+ signature = key->alg->sign(key->data, data, datalen, &siglen);
+ len = 5 + 4 + siglen;
+ PUT_32BIT(ret, len - 4);
+ ret[4] = SSH2_AGENT_SIGN_RESPONSE;
+ PUT_32BIT(ret + 5, siglen);
+ memcpy(ret + 5 + 4, signature, siglen);
+ sfree(signature);
+ }
+ break;
+ case SSH1_AGENTC_ADD_RSA_IDENTITY:
+ /*
+ * Add to the list and return SSH_AGENT_SUCCESS, or
+ * SSH_AGENT_FAILURE if the key was malformed.
+ */
+ {
+ struct RSAKey *key;
+ char *comment;
+ int commentlen;
+ key = smalloc(sizeof(struct RSAKey));
+ memset(key, 0, sizeof(struct RSAKey));
+ p += makekey(p, key, NULL, 1);
+ p += makeprivate(p, key);
+ p += ssh1_read_bignum(p, &key->iqmp); /* p^-1 mod q */
+ p += ssh1_read_bignum(p, &key->p); /* p */
+ p += ssh1_read_bignum(p, &key->q); /* q */
+ commentlen = GET_32BIT(p);
+ comment = smalloc(commentlen+1);
+ if (comment) {
+ memcpy(comment, p + 4, commentlen);
+ comment[commentlen] = '\0';
+ key->comment = comment;
+ }
+ PUT_32BIT(ret, 1);
+ ret[4] = SSH_AGENT_FAILURE;
+ if (add234(rsakeys, key) == key) {
+ keylist_update();
+ ret[4] = SSH_AGENT_SUCCESS;
+ } else {
+ freersakey(key);
+ sfree(key);
+ }
+ }
+ break;
+ case SSH2_AGENTC_ADD_IDENTITY:
+ /*
+ * Add to the list and return SSH_AGENT_SUCCESS, or
+ * SSH_AGENT_FAILURE if the key was malformed.
+ */
+ {
+ struct ssh2_userkey *key;
+ char *comment, *alg;
+ int alglen, commlen;
+ int bloblen;
+
+ key = smalloc(sizeof(struct ssh2_userkey));
+
+ alglen = GET_32BIT(p);
+ p += 4;
+ alg = p;
+ p += alglen;
+ /* Add further algorithm names here. */
+ if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
+ key->alg = &ssh_rsa;
+ else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
+ key->alg = &ssh_dss;
+ else {
+ sfree(key);
+ goto failure;
+ }
+
+ bloblen =
+ GET_32BIT((unsigned char *) msg) - (p -
+ (unsigned char *) msg -
+ 4);
+ key->data = key->alg->openssh_createkey(&p, &bloblen);
+ if (!key->data) {
+ sfree(key);
+ goto failure;
+ }
+ commlen = GET_32BIT(p);
+ p += 4;
+
+ comment = smalloc(commlen + 1);
+ if (comment) {
+ memcpy(comment, p, commlen);
+ comment[commlen] = '\0';
+ }
+ key->comment = comment;
+
+ PUT_32BIT(ret, 1);
+ ret[4] = SSH_AGENT_FAILURE;
+ if (add234(ssh2keys, key) == key) {
+ keylist_update();
+ ret[4] = SSH_AGENT_SUCCESS;
+ } else {
+ key->alg->freekey(key->data);
+ sfree(key->comment);
+ sfree(key);
+ }
+ }
+ break;
+ case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
+ /*
+ * Remove from the list and return SSH_AGENT_SUCCESS, or
+ * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
+ * start with.
+ */
+ {
+ struct RSAKey reqkey, *key;
+
+ p += makekey(p, &reqkey, NULL, 0);
+ key = find234(rsakeys, &reqkey, NULL);
+ freebn(reqkey.exponent);
+ freebn(reqkey.modulus);
+ PUT_32BIT(ret, 1);
+ ret[4] = SSH_AGENT_FAILURE;
+ if (key) {
+ del234(rsakeys, key);
+ keylist_update();
+ freersakey(key);
+ sfree(key);
+ ret[4] = SSH_AGENT_SUCCESS;
+ }
+ }
+ break;
+ case SSH2_AGENTC_REMOVE_IDENTITY:
+ /*
+ * Remove from the list and return SSH_AGENT_SUCCESS, or
+ * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
+ * start with.
+ */
+ {
+ struct ssh2_userkey *key;
+ struct blob b;
+
+ b.len = GET_32BIT(p);
+ p += 4;
+ b.blob = p;
+ p += b.len;
+ key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
+ if (!key)
+ goto failure;
+
+ PUT_32BIT(ret, 1);
+ ret[4] = SSH_AGENT_FAILURE;
+ if (key) {
+ del234(ssh2keys, key);
+ keylist_update();
+ key->alg->freekey(key->data);
+ sfree(key);
+ ret[4] = SSH_AGENT_SUCCESS;
+ }
+ }
+ break;
+ case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
+ /*
+ * Remove all SSH1 keys. Always returns success.
+ */
+ {
+ struct RSAKey *rkey;