Coverage Report

Created: 2023-06-07 06:44

/src/dropbear/src/svr-authpubkeyoptions.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Dropbear - a SSH2 server
3
 * 
4
 * Copyright (c) 2008 Frederic Moulins
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
 * This file incorporates work covered by the following copyright and  
26
 * permission notice:
27
 *
28
 *  Author: Tatu Ylonen <ylo@cs.hut.fi>
29
 *  Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
30
 *                All rights reserved
31
 *  As far as I am concerned, the code I have written for this software
32
 *  can be used freely for any purpose.  Any derived versions of this
33
 *  software must be clearly marked as such, and if the derived work is
34
 *  incompatible with the protocol description in the RFC file, it must be
35
 *  called by a name other than "ssh" or "Secure Shell".
36
 *
37
 * This copyright and permission notice applies to the code parsing public keys
38
 * options string which can also be found in OpenSSH auth-options.c file 
39
 * (auth_parse_options).
40
 *
41
 */
42
43
/* Process pubkey options during a pubkey auth request */
44
#include "includes.h"
45
#include "session.h"
46
#include "dbutil.h"
47
#include "signkey.h"
48
#include "auth.h"
49
#include "runopts.h"
50
51
#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
52
53
/* Returns 1 if pubkey allows agent forwarding,
54
 * 0 otherwise */
55
0
int svr_pubkey_allows_agentfwd() {
56
0
  if (ses.authstate.pubkey_options 
57
0
    && ses.authstate.pubkey_options->no_agent_forwarding_flag) {
58
0
    return 0;
59
0
  }
60
0
  return 1;
61
0
}
62
63
/* Returns 1 if pubkey allows tcp forwarding,
64
 * 0 otherwise */
65
0
int svr_pubkey_allows_tcpfwd() {
66
0
  if (ses.authstate.pubkey_options 
67
0
    && ses.authstate.pubkey_options->no_port_forwarding_flag) {
68
0
    return 0;
69
0
  }
70
0
  return 1;
71
0
}
72
73
/* Returns 1 if pubkey allows x11 forwarding,
74
 * 0 otherwise */
75
0
int svr_pubkey_allows_x11fwd() {
76
0
  if (ses.authstate.pubkey_options 
77
0
    && ses.authstate.pubkey_options->no_x11_forwarding_flag) {
78
0
    return 0;
79
0
  }
80
0
  return 1;
81
0
}
82
83
/* Returns 1 if pubkey allows pty, 0 otherwise */
84
0
int svr_pubkey_allows_pty() {
85
0
  if (ses.authstate.pubkey_options 
86
0
    && ses.authstate.pubkey_options->no_pty_flag) {
87
0
    return 0;
88
0
  }
89
0
  return 1;
90
0
}
91
92
/* Returns 1 if pubkey allows local tcp fowarding to the provided destination,
93
 * 0 otherwise */
94
0
int svr_pubkey_allows_local_tcpfwd(const char *host, unsigned int port) {
95
0
  if (ses.authstate.pubkey_options
96
0
    && ses.authstate.pubkey_options->permit_open_destinations) {
97
0
    m_list_elem *iter = ses.authstate.pubkey_options->permit_open_destinations->first;
98
0
    while (iter) {
99
0
      struct PermitTCPFwdEntry *entry = (struct PermitTCPFwdEntry*)iter->item;
100
0
      if (strcmp(entry->host, host) == 0) {
101
0
        if ((entry->port == PUBKEY_OPTIONS_ANY_PORT) || (entry->port == port)) {
102
0
          return 1;
103
0
        }
104
0
      }
105
106
0
      iter = iter->next;
107
0
    }
108
109
0
    return 0;
110
0
  }
111
112
0
  return 1;
113
0
}
114
115
/* Set chansession command to the one forced 
116
 * by any 'command' public key option. */
117
0
void svr_pubkey_set_forced_command(struct ChanSess *chansess) {
118
0
  if (ses.authstate.pubkey_options && ses.authstate.pubkey_options->forced_command) {
119
0
    TRACE(("Forced command '%s'", ses.authstate.pubkey_options->forced_command))
120
0
    if (chansess->cmd) {
121
      /* original_command takes ownership */
122
0
      chansess->original_command = chansess->cmd;
123
0
      chansess->cmd = NULL;
124
0
    } else {
125
0
      chansess->original_command = m_strdup("");
126
0
    }
127
0
    chansess->cmd = m_strdup(ses.authstate.pubkey_options->forced_command);
128
#if LOG_COMMANDS
129
    dropbear_log(LOG_INFO, "Command forced to '%s'", chansess->original_command);
130
#endif
131
0
  }
132
0
}
133
134
/* Free potential public key options */
135
3.86k
void svr_pubkey_options_cleanup() {
136
3.86k
  if (ses.authstate.pubkey_options) {
137
0
    if (ses.authstate.pubkey_options->forced_command) {
138
0
      m_free(ses.authstate.pubkey_options->forced_command);
139
0
    }
140
0
    if (ses.authstate.pubkey_options->permit_open_destinations) {
141
0
      m_list_elem *iter = ses.authstate.pubkey_options->permit_open_destinations->first;
142
0
      while (iter) {
143
0
        struct PermitTCPFwdEntry *entry = (struct PermitTCPFwdEntry*)list_remove(iter);
144
0
        m_free(entry->host);
145
0
        m_free(entry);
146
0
        iter = ses.authstate.pubkey_options->permit_open_destinations->first;
147
0
      }
148
0
      m_free(ses.authstate.pubkey_options->permit_open_destinations);
149
0
    }
150
0
    m_free(ses.authstate.pubkey_options);
151
0
  }
152
3.86k
  if (ses.authstate.pubkey_info) {
153
0
    m_free(ses.authstate.pubkey_info);
154
0
  }
155
3.86k
}
156
157
/* helper for svr_add_pubkey_options. returns DROPBEAR_SUCCESS if the option is matched,
158
   and increments the options_buf */
159
0
static int match_option(buffer *options_buf, const char *opt_name) {
160
0
  const unsigned int len = strlen(opt_name);
161
0
  if (options_buf->len - options_buf->pos < len) {
162
0
    return DROPBEAR_FAILURE;
163
0
  }
164
0
  if (strncasecmp((const char *) buf_getptr(options_buf, len), opt_name, len) == 0) {
165
0
    buf_incrpos(options_buf, len);
166
0
    return DROPBEAR_SUCCESS;
167
0
  }
168
0
  return DROPBEAR_FAILURE;
169
0
}
170
171
/* Parse pubkey options and set ses.authstate.pubkey_options accordingly.
172
 * Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
173
0
int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename) {
174
0
  int ret = DROPBEAR_FAILURE;
175
176
0
  TRACE(("enter addpubkeyoptions"))
177
178
0
  ses.authstate.pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions ));
179
180
0
  buf_setpos(options_buf, 0);
181
0
  while (options_buf->pos < options_buf->len) {
182
0
    if (match_option(options_buf, "no-port-forwarding") == DROPBEAR_SUCCESS) {
183
0
      dropbear_log(LOG_WARNING, "Port forwarding disabled.");
184
0
      ses.authstate.pubkey_options->no_port_forwarding_flag = 1;
185
0
      goto next_option;
186
0
    }
187
0
    if (match_option(options_buf, "no-agent-forwarding") == DROPBEAR_SUCCESS) {
188
0
#if DROPBEAR_SVR_AGENTFWD
189
0
      dropbear_log(LOG_WARNING, "Agent forwarding disabled.");
190
0
      ses.authstate.pubkey_options->no_agent_forwarding_flag = 1;
191
0
#endif
192
0
      goto next_option;
193
0
    }
194
0
    if (match_option(options_buf, "no-X11-forwarding") == DROPBEAR_SUCCESS) {
195
#if DROPBEAR_X11FWD
196
      dropbear_log(LOG_WARNING, "X11 forwarding disabled.");
197
      ses.authstate.pubkey_options->no_x11_forwarding_flag = 1;
198
#endif
199
0
      goto next_option;
200
0
    }
201
0
    if (match_option(options_buf, "no-pty") == DROPBEAR_SUCCESS) {
202
0
      dropbear_log(LOG_WARNING, "Pty allocation disabled.");
203
0
      ses.authstate.pubkey_options->no_pty_flag = 1;
204
0
      goto next_option;
205
0
    }
206
0
    if (match_option(options_buf, "restrict") == DROPBEAR_SUCCESS) {
207
0
      dropbear_log(LOG_WARNING, "Restrict option set");
208
0
      ses.authstate.pubkey_options->no_port_forwarding_flag = 1;
209
0
#if DROPBEAR_SVR_AGENTFWD
210
0
      ses.authstate.pubkey_options->no_agent_forwarding_flag = 1;
211
0
#endif
212
#if DROPBEAR_X11FWD
213
      ses.authstate.pubkey_options->no_x11_forwarding_flag = 1;
214
#endif
215
0
      ses.authstate.pubkey_options->no_pty_flag = 1;
216
0
      goto next_option;
217
0
    }
218
0
    if (match_option(options_buf, "command=\"") == DROPBEAR_SUCCESS) {
219
0
      int escaped = 0;
220
0
      const unsigned char* command_start = buf_getptr(options_buf, 0);
221
222
0
      if (ses.authstate.pubkey_options->forced_command) {
223
        /* multiple command= options */
224
0
        goto bad_option;
225
0
      }
226
227
0
      while (options_buf->pos < options_buf->len) {
228
0
        const char c = buf_getbyte(options_buf);
229
0
        if (!escaped && c == '"') {
230
0
          const int command_len = buf_getptr(options_buf, 0) - command_start;
231
0
          ses.authstate.pubkey_options->forced_command = m_malloc(command_len);
232
0
          memcpy(ses.authstate.pubkey_options->forced_command,
233
0
              command_start, command_len-1);
234
0
          ses.authstate.pubkey_options->forced_command[command_len-1] = '\0';
235
0
          goto next_option;
236
0
        }
237
0
        escaped = (!escaped && c == '\\');
238
0
      }
239
0
      dropbear_log(LOG_WARNING, "Badly formatted command= authorized_keys option");
240
0
      goto bad_option;
241
0
    }
242
243
0
    if (match_option(options_buf, "permitopen=\"") == DROPBEAR_SUCCESS) {
244
0
      int valid_option = 0;
245
0
      const unsigned char* permitopen_start = buf_getptr(options_buf, 0);
246
247
0
      if (!ses.authstate.pubkey_options->permit_open_destinations) {
248
0
        ses.authstate.pubkey_options->permit_open_destinations = list_new();
249
0
      }
250
251
0
      while (options_buf->pos < options_buf->len) {
252
0
        const char c = buf_getbyte(options_buf);
253
0
        if (c == '"') {
254
0
          char *spec = NULL;
255
0
          char *portstring = NULL;
256
0
          const int permitopen_len = buf_getptr(options_buf, 0) - permitopen_start;
257
0
          struct PermitTCPFwdEntry *entry =
258
0
              (struct PermitTCPFwdEntry*)m_malloc(sizeof(struct PermitTCPFwdEntry));
259
260
0
          list_append(ses.authstate.pubkey_options->permit_open_destinations, entry);
261
0
          spec = m_malloc(permitopen_len);
262
0
          memcpy(spec, permitopen_start, permitopen_len - 1);
263
0
          spec[permitopen_len - 1] = '\0';
264
0
          if ((split_address_port(spec, &entry->host, &portstring) == DROPBEAR_SUCCESS)
265
0
            && entry->host && portstring) {
266
0
            if (strcmp(portstring, "*") == 0) {
267
0
              valid_option = 1;
268
0
              entry->port = PUBKEY_OPTIONS_ANY_PORT;
269
0
              TRACE(("local port forwarding allowed to host '%s'", entry->host));
270
0
            } else if (m_str_to_uint(portstring, &entry->port) == DROPBEAR_SUCCESS) {
271
0
              valid_option = 1;
272
0
              TRACE(("local port forwarding allowed to host '%s' and port '%u'",
273
0
                  entry->host, entry->port));
274
0
            }
275
0
          }
276
277
0
          m_free(spec);
278
0
          m_free(portstring);
279
0
          break;
280
0
        }
281
0
      }
282
283
0
      if (valid_option) {
284
0
        goto next_option;
285
0
      } else {
286
0
        dropbear_log(LOG_WARNING, "Badly formatted permitopen= authorized_keys option");
287
0
        goto bad_option;
288
0
      }
289
0
    }
290
291
0
    if (match_option(options_buf, "no-touch-required") == DROPBEAR_SUCCESS) {
292
0
#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519
293
0
      dropbear_log(LOG_WARNING, "No user presence check required for U2F/FIDO key.");
294
0
      ses.authstate.pubkey_options->no_touch_required_flag = 1;
295
0
#endif
296
0
      goto next_option;
297
0
    }
298
0
    if (match_option(options_buf, "verify-required") == DROPBEAR_SUCCESS) {
299
0
#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519
300
0
      dropbear_log(LOG_WARNING, "User verification required for U2F/FIDO key.");
301
0
      ses.authstate.pubkey_options->verify_required_flag = 1;
302
0
#endif
303
0
      goto next_option;
304
0
    }
305
306
0
next_option:
307
    /*
308
     * Skip the comma, and move to the next option
309
     * (or break out if there are no more).
310
     */
311
0
    if (options_buf->pos < options_buf->len 
312
0
        && buf_getbyte(options_buf) != ',') {
313
0
      goto bad_option;
314
0
    }
315
    /* Process the next option. */
316
0
  }
317
  /* parsed all options with no problem */
318
0
  ret = DROPBEAR_SUCCESS;
319
0
  goto end;
320
321
0
bad_option:
322
0
  ret = DROPBEAR_FAILURE;
323
0
  svr_pubkey_options_cleanup();
324
0
  dropbear_log(LOG_WARNING, "Bad public key options at %s:%d", filename, line_num);
325
326
0
end:
327
0
  TRACE(("leave addpubkeyoptions"))
328
0
  return ret;
329
0
}
330
331
#endif