source: filezilla/trunk/fuentes/src/putty/unix/uxagentc.c @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 4 years ago

First release to xenial

File size: 4.9 KB
Line 
1/*
2 * SSH agent client code.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <assert.h>
8#include <unistd.h>
9#include <sys/socket.h>
10#include <sys/un.h>
11#include <fcntl.h>
12
13#include "putty.h"
14#include "../misc.h"
15#include "tree234.h"
16#include "puttymem.h"
17
18int agent_exists(void)
19{
20    const char *p = getenv("SSH_AUTH_SOCK");
21    if (p && *p)
22        return TRUE;
23    return FALSE;
24}
25
26static tree234 *agent_connections;
27struct agent_connection {
28    int fd;
29    char *retbuf;
30    char sizebuf[4];
31    int retsize, retlen;
32    void (*callback)(void *, void *, int);
33    void *callback_ctx;
34};
35static int agent_conncmp(void *av, void *bv)
36{
37    struct agent_connection *a = (struct agent_connection *) av;
38    struct agent_connection *b = (struct agent_connection *) bv;
39    if (a->fd < b->fd)
40        return -1;
41    if (a->fd > b->fd)
42        return +1;
43    return 0;
44}
45static int agent_connfind(void *av, void *bv)
46{
47    int afd = *(int *) av;
48    struct agent_connection *b = (struct agent_connection *) bv;
49    if (afd < b->fd)
50        return -1;
51    if (afd > b->fd)
52        return +1;
53    return 0;
54}
55
56/*
57 * Attempt to read from an agent socket fd. Returns 0 if the expected
58 * response is as yet incomplete; returns 1 if it's either complete
59 * (conn->retbuf non-NULL and filled with something useful) or has
60 * failed totally (conn->retbuf is NULL).
61 */
62static int agent_try_read(struct agent_connection *conn)
63{
64    int ret;
65
66    ret = read(conn->fd, conn->retbuf+conn->retlen,
67               conn->retsize-conn->retlen);
68    if (ret <= 0) {
69        if (conn->retbuf != conn->sizebuf) sfree(conn->retbuf);
70        conn->retbuf = NULL;
71        conn->retlen = 0;
72        return 1;
73    }
74    conn->retlen += ret;
75    if (conn->retsize == 4 && conn->retlen == 4) {
76        conn->retsize = toint(GET_32BIT(conn->retbuf) + 4);
77        if (conn->retsize <= 0) {
78            conn->retbuf = NULL;
79            conn->retlen = 0;
80            return -1;                 /* way too large */
81        }
82        assert(conn->retbuf == conn->sizebuf);
83        conn->retbuf = snewn(conn->retsize, char);
84        memcpy(conn->retbuf, conn->sizebuf, 4);
85    }
86
87    if (conn->retlen < conn->retsize)
88        return 0;                      /* more data to come */
89
90    return 1;
91}
92
93static int agent_select_result(int fd, int event)
94{
95    struct agent_connection *conn;
96
97    assert(event == 1);                /* not selecting for anything but R */
98
99    conn = find234(agent_connections, &fd, agent_connfind);
100    if (!conn) {
101        uxsel_del(fd);
102        return 1;
103    }
104
105    if (!agent_try_read(conn))
106        return 0;                      /* more data to come */
107
108    /*
109     * We have now completed the agent query. Do the callback, and
110     * clean up. (Of course we don't free retbuf, since ownership
111     * of that passes to the callback.)
112     */
113    conn->callback(conn->callback_ctx, conn->retbuf, conn->retlen);
114    uxsel_del(fd);
115    close(fd);
116    del234(agent_connections, conn);
117    sfree(conn);
118    return 0;
119}
120
121int agent_query(void *in, int inlen, void **out, int *outlen,
122                void (*callback)(void *, void *, int), void *callback_ctx)
123{
124    char *name;
125    int sock;
126    struct sockaddr_un addr;
127    int done;
128    struct agent_connection *conn;
129
130    name = getenv("SSH_AUTH_SOCK");
131    if (!name)
132        goto failure;
133
134    sock = socket(PF_UNIX, SOCK_STREAM, 0);
135    if (sock < 0) {
136        perror("socket(PF_UNIX)");
137        exit(1);
138    }
139
140    cloexec(sock);
141
142    addr.sun_family = AF_UNIX;
143    strncpy(addr.sun_path, name, sizeof(addr.sun_path));
144    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
145        close(sock);
146        goto failure;
147    }
148
149    for (done = 0; done < inlen ;) {
150        int ret = write(sock, (char *)in + done, inlen - done);
151        if (ret <= 0) {
152            close(sock);
153            goto failure;
154        }
155        done += ret;
156    }
157
158    conn = snew(struct agent_connection);
159    conn->fd = sock;
160    conn->retbuf = conn->sizebuf;
161    conn->retsize = 4;
162    conn->retlen = 0;
163    conn->callback = callback;
164    conn->callback_ctx = callback_ctx;
165
166    if (!callback) {
167        /*
168         * Bodge to permit making deliberately synchronous agent
169         * requests. Used by Unix Pageant in command-line client mode,
170         * which is legit because it really is true that no other part
171         * of the program is trying to get anything useful done
172         * simultaneously. But this special case shouldn't be used in
173         * any more general program.
174         */
175        no_nonblock(conn->fd);
176        while (!agent_try_read(conn))
177            /* empty loop body */;
178
179        *out = conn->retbuf;
180        *outlen = conn->retlen;
181        sfree(conn);
182        return 1;
183    }
184
185    /*
186     * Otherwise do it properly: add conn to the tree of agent
187     * connections currently in flight, return 0 to indicate that the
188     * response hasn't been received yet, and call the callback when
189     * select_result comes back to us.
190     */
191    if (!agent_connections)
192        agent_connections = newtree234(agent_conncmp);
193    add234(agent_connections, conn);
194
195    uxsel_set(sock, 1, agent_select_result);
196    return 0;
197
198    failure:
199    *out = NULL;
200    *outlen = 0;
201    return 1;
202}
Note: See TracBrowser for help on using the repository browser.