Coverage Report

Created: 2025-11-11 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dropbear/src/cli-agentfwd.c
Line
Count
Source
1
/*
2
 * Dropbear - a SSH2 server
3
 * 
4
 * Copyright (c) 2005 Matt Johnston
5
 * All rights reserved.
6
 * 
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to deal
9
 * in the Software without restriction, including without limitation the rights
10
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 * 
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
16
 * 
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
 * SOFTWARE. */
24
25
#include "includes.h"
26
27
#if DROPBEAR_CLI_AGENTFWD
28
29
#include "agentfwd.h"
30
#include "session.h"
31
#include "ssh.h"
32
#include "dbutil.h"
33
#include "chansession.h"
34
#include "channel.h"
35
#include "packet.h"
36
#include "buffer.h"
37
#include "dbrandom.h"
38
#include "listener.h"
39
#include "runopts.h"
40
#include "atomicio.h"
41
#include "signkey.h"
42
#include "auth.h"
43
44
/* The protocol implemented to talk to OpenSSH's SSH2 agent is documented in
45
   PROTOCOL.agent in recent OpenSSH source distributions (5.1p1 has it). */
46
47
static int new_agent_chan(struct Channel * channel);
48
49
const struct ChanType cli_chan_agent = {
50
  "auth-agent@openssh.com",
51
  new_agent_chan,
52
  NULL,
53
  NULL,
54
  NULL,
55
  NULL
56
};
57
58
0
static int connect_agent() {
59
60
0
  int fd = -1;
61
0
  char* agent_sock = NULL;
62
63
0
  agent_sock = getenv("SSH_AUTH_SOCK");
64
0
  if (agent_sock == NULL)
65
0
    return -1;
66
67
0
  fd = connect_unix(agent_sock);
68
69
0
  if (fd < 0) {
70
0
    dropbear_log(LOG_INFO, "Failed to connect to agent");
71
0
  }
72
73
0
  return fd;
74
0
}
75
76
/* handle a request for a connection to the locally running ssh-agent
77
   or forward. */
78
0
static int new_agent_chan(struct Channel * channel) {
79
80
0
  int fd = -1;
81
82
0
  if (!cli_opts.agent_fwd)
83
0
    return SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
84
85
0
  fd = connect_agent();
86
0
  if (fd < 0) {
87
0
    return SSH_OPEN_CONNECT_FAILED;
88
0
  }
89
90
0
  setnonblocking(fd);
91
92
0
  ses.maxfd = MAX(ses.maxfd, fd);
93
94
0
  channel->readfd = fd;
95
0
  channel->writefd = fd;
96
0
  channel->bidir_fd = 1;
97
98
0
  return 0;
99
0
}
100
101
/* Sends a request to the agent, returning a newly allocated buffer
102
 * with the response */
103
/* This function will block waiting for a response - it will
104
 * only be used by client authentication (not for forwarded requests)
105
 * won't cause problems for interactivity. */
106
/* Packet format (from draft-ylonen)
107
   4 bytes     Length, msb first.  Does not include length itself.
108
   1 byte      Packet type.  The value 255 is reserved for future extensions.
109
   data        Any data, depending on packet type.  Encoding as in the ssh packet
110
               protocol.
111
*/
112
0
static buffer * agent_request(unsigned char type, const buffer *data) {
113
114
0
  buffer * payload = NULL;
115
0
  buffer * inbuf = NULL;
116
0
  size_t readlen = 0;
117
0
  ssize_t ret;
118
0
  const int fd = cli_opts.agent_fd;
119
0
  unsigned int data_len = 0;
120
0
  if (data)
121
0
  {
122
0
    data_len = data->len;
123
0
  }
124
125
0
  payload = buf_new(4 + 1 + data_len);
126
127
0
  buf_putint(payload, 1 + data_len);
128
0
  buf_putbyte(payload, type);
129
0
  if (data) {
130
0
    buf_putbytes(payload, data->data, data->len);
131
0
  }
132
0
  buf_setpos(payload, 0);
133
134
0
  ret = atomicio(vwrite, fd, buf_getptr(payload, payload->len), payload->len);
135
0
  if ((size_t)ret != payload->len) {
136
0
    TRACE(("write failed fd %d for agent_request, %s", fd, strerror(errno)))
137
0
    goto out;
138
0
  }
139
140
0
  buf_free(payload);
141
0
  payload = NULL;
142
0
  TRACE(("Wrote out bytes for agent_request"))
143
  /* Now we read the response */
144
0
  inbuf = buf_new(4);
145
0
  ret = atomicio(read, fd, buf_getwriteptr(inbuf, 4), 4);
146
0
  if (ret != 4) {
147
0
    TRACE(("read of length failed for agent_request"))
148
0
    goto out;
149
0
  }
150
0
  buf_setpos(inbuf, 0);
151
0
  buf_setlen(inbuf, ret);
152
153
0
  readlen = buf_getint(inbuf);
154
0
  if (readlen > MAX_AGENT_REPLY) {
155
0
    TRACE(("agent reply is too big"));
156
0
    goto out;
157
0
  }
158
  
159
0
  inbuf = buf_resize(inbuf, readlen);
160
0
  buf_setpos(inbuf, 0);
161
0
  ret = atomicio(read, fd, buf_getwriteptr(inbuf, readlen), readlen);
162
0
  if ((size_t)ret != readlen) {
163
0
    TRACE(("read of data failed for agent_request"))
164
0
    goto out;
165
0
  }
166
0
  buf_incrwritepos(inbuf, readlen);
167
0
  buf_setpos(inbuf, 0);
168
169
0
out:
170
0
  if (payload)
171
0
    buf_free(payload);
172
173
0
  return inbuf;
174
0
}
175
176
static void agent_get_key_list(m_list * ret_list)
177
0
{
178
0
  buffer * inbuf = NULL;
179
0
  unsigned int num = 0;
180
0
  unsigned char packet_type;
181
0
  unsigned int i;
182
0
  int ret;
183
184
0
  inbuf = agent_request(SSH2_AGENTC_REQUEST_IDENTITIES, NULL);
185
0
  if (!inbuf) {
186
0
    TRACE(("agent_request failed returning identities"))
187
0
    goto out;
188
0
  }
189
190
  /* The reply has a format of:
191
    byte      SSH2_AGENT_IDENTITIES_ANSWER
192
    uint32      num_keys
193
       Followed by zero or more consecutive keys, encoded as:
194
         string     key_blob
195
       string     key_comment
196
   */
197
0
  packet_type = buf_getbyte(inbuf);
198
0
  if (packet_type != SSH2_AGENT_IDENTITIES_ANSWER) {
199
0
    goto out;
200
0
  }
201
202
0
  num = buf_getint(inbuf);
203
0
  for (i = 0; i < num; i++) {
204
0
    sign_key * pubkey = NULL;
205
0
    enum signkey_type key_type = DROPBEAR_SIGNKEY_ANY;
206
0
    buffer * key_buf;
207
208
    /* each public key is encoded as a string */
209
0
    key_buf = buf_getstringbuf(inbuf);
210
0
    pubkey = new_sign_key();
211
0
    ret = buf_get_pub_key(key_buf, pubkey, &key_type);
212
0
    buf_free(key_buf);
213
0
    if (ret != DROPBEAR_SUCCESS) {
214
0
      TRACE(("Skipping bad/unknown type pubkey from agent"));
215
0
      sign_key_free(pubkey);
216
0
    } else {
217
0
      pubkey->type = key_type;
218
0
      pubkey->source = SIGNKEY_SOURCE_AGENT;
219
220
0
      list_append(ret_list, pubkey);
221
0
    }
222
223
    /* We'll ignore the comment for now. might want it later.*/
224
0
    buf_eatstring(inbuf);
225
0
  }
226
227
0
out:
228
0
  if (inbuf) {
229
0
    buf_free(inbuf);
230
0
    inbuf = NULL;
231
0
  }
232
0
}
233
234
0
void cli_setup_agent(const struct Channel *channel) {
235
0
  if (!getenv("SSH_AUTH_SOCK")) {
236
0
    return;
237
0
  }
238
  
239
0
  start_send_channel_request(channel, "auth-agent-req@openssh.com");
240
  /* Don't want replies */
241
0
  buf_putbyte(ses.writepayload, 0);
242
0
  encrypt_packet();
243
0
}
244
245
/* Returned keys are prepended to ret_list, which will
246
   be updated. */
247
0
void cli_load_agent_keys(m_list *ret_list) {
248
  /* agent_fd will be closed after successful auth */
249
0
  cli_opts.agent_fd = connect_agent();
250
0
  if (cli_opts.agent_fd < 0) {
251
0
    return;
252
0
  }
253
254
0
  agent_get_key_list(ret_list);
255
0
}
256
257
void agent_buf_sign(buffer *sigblob, sign_key *key, 
258
0
    const buffer *data_buf, enum signature_type sigtype) {
259
0
  buffer *request_data = NULL;
260
0
  buffer *response = NULL;
261
0
  unsigned int siglen;
262
0
  int packet_type;
263
0
  int flags = 0;
264
  
265
  /* Request format
266
  byte      SSH2_AGENTC_SIGN_REQUEST
267
  string      key_blob
268
  string      data
269
  uint32      flags
270
  */
271
0
  request_data = buf_new(MAX_PUBKEY_SIZE + data_buf->len + 12);
272
0
  buf_put_pub_key(request_data, key, key->type);
273
  
274
0
  buf_putbufstring(request_data, data_buf);
275
0
#if DROPBEAR_RSA_SHA256
276
0
  if (sigtype == DROPBEAR_SIGNATURE_RSA_SHA256) {
277
0
    flags |= SSH_AGENT_RSA_SHA2_256;
278
0
  }
279
0
#endif
280
0
  buf_putint(request_data, flags);
281
  
282
0
  response = agent_request(SSH2_AGENTC_SIGN_REQUEST, request_data);
283
  
284
0
  if (!response) {
285
0
    goto fail;
286
0
  }
287
  
288
0
  packet_type = buf_getbyte(response);
289
0
  if (packet_type != SSH2_AGENT_SIGN_RESPONSE) {
290
0
    goto fail;
291
0
  }
292
  
293
  /* Response format
294
  byte      SSH2_AGENT_SIGN_RESPONSE
295
  string      signature_blob
296
  */
297
0
  siglen = buf_getint(response);
298
0
  buf_putbytes(sigblob, buf_getptr(response, siglen), siglen);
299
0
  goto cleanup;
300
  
301
0
fail:
302
  /* XXX don't fail badly here. instead propagate a failure code back up to
303
     the cli auth pubkey code, and just remove this key from the list of 
304
     ones to try. */
305
0
  dropbear_exit("Agent failed signing key");
306
307
0
cleanup:
308
0
  if (request_data) {
309
0
    buf_free(request_data);
310
0
  }
311
0
  if (response) {
312
0
    buf_free(response);
313
0
  }
314
0
}
315
316
#endif