[BACK]Return to auth-options.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Annotation of src/usr.bin/ssh/auth-options.c, Revision 1.75

1.75    ! djm         1: /* $OpenBSD: auth-options.c,v 1.74 2017/09/12 06:32:07 djm Exp $ */
1.3       deraadt     2: /*
                      3:  * Author: Tatu Ylonen <ylo@cs.hut.fi>
                      4:  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
                      5:  *                    All rights reserved
                      6:  * As far as I am concerned, the code I have written for this software
                      7:  * can be used freely for any purpose.  Any derived versions of this
                      8:  * software must be clearly marked as such, and if the derived work is
                      9:  * incompatible with the protocol description in the RFC file, it must be
                     10:  * called by a name other than "ssh" or "Secure Shell".
                     11:  */
1.75    ! djm        12: /*
        !            13:  * Copyright (c) 2018 Damien Miller <djm@mindrot.org>
        !            14:  *
        !            15:  * Permission to use, copy, modify, and distribute this software for any
        !            16:  * purpose with or without fee is hereby granted, provided that the above
        !            17:  * copyright notice and this permission notice appear in all copies.
        !            18:  *
        !            19:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            20:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            21:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            22:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            23:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            24:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            25:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            26:  */
1.3       deraadt    27:
1.36      stevesk    28: #include <sys/types.h>
1.42      djm        29: #include <sys/queue.h>
1.36      stevesk    30:
1.37      stevesk    31: #include <netdb.h>
1.36      stevesk    32: #include <pwd.h>
1.39      stevesk    33: #include <string.h>
1.40      deraadt    34: #include <stdio.h>
                     35: #include <stdarg.h>
1.75    ! djm        36: #include <ctype.h>
        !            37: #include <limits.h>
1.1       markus     38:
1.65      markus     39: #include "key.h"       /* XXX for typedef */
                     40: #include "buffer.h"    /* XXX for typedef */
1.1       markus     41: #include "xmalloc.h"
                     42: #include "match.h"
1.65      markus     43: #include "ssherr.h"
1.75    ! djm        44: #include "ssh2.h"
1.11      markus     45: #include "log.h"
                     46: #include "canohost.h"
1.71      djm        47: #include "packet.h"
1.65      markus     48: #include "sshbuf.h"
1.64      millert    49: #include "misc.h"
1.18      markus     50: #include "channels.h"
1.12      markus     51: #include "servconf.h"
1.65      markus     52: #include "sshkey.h"
1.50      djm        53: #include "auth-options.h"
1.40      deraadt    54: #include "hostfile.h"
                     55: #include "auth.h"
1.1       markus     56:
                     57: /* Flags set authorized_keys flags */
                     58: int no_port_forwarding_flag = 0;
                     59: int no_agent_forwarding_flag = 0;
                     60: int no_x11_forwarding_flag = 0;
                     61: int no_pty_flag = 0;
1.41      djm        62: int no_user_rc = 0;
1.45      djm        63: int key_is_cert_authority = 0;
1.1       markus     64:
                     65: /* "command=" option. */
                     66: char *forced_command = NULL;
                     67:
                     68: /* "environment=" options. */
                     69: struct envstring *custom_environment = NULL;
                     70:
1.32      reyk       71: /* "tunnel=" option. */
                     72: int forced_tun_device = -1;
                     73:
1.51      djm        74: /* "principals=" option. */
                     75: char *authorized_principals = NULL;
                     76:
1.12      markus     77: extern ServerOptions options;
                     78:
1.74      djm        79: /* XXX refactor to be stateless */
                     80:
1.22      provos     81: void
1.5       markus     82: auth_clear_options(void)
                     83: {
1.74      djm        84:        struct ssh *ssh = active_state;         /* XXX */
                     85:
1.5       markus     86:        no_agent_forwarding_flag = 0;
                     87:        no_port_forwarding_flag = 0;
                     88:        no_pty_flag = 0;
                     89:        no_x11_forwarding_flag = 0;
1.41      djm        90:        no_user_rc = 0;
1.45      djm        91:        key_is_cert_authority = 0;
1.5       markus     92:        while (custom_environment) {
                     93:                struct envstring *ce = custom_environment;
                     94:                custom_environment = ce->next;
1.58      djm        95:                free(ce->s);
                     96:                free(ce);
1.5       markus     97:        }
1.70      mmcc       98:        free(forced_command);
                     99:        forced_command = NULL;
                    100:        free(authorized_principals);
                    101:        authorized_principals = NULL;
1.32      reyk      102:        forced_tun_device = -1;
1.74      djm       103:        channel_clear_permitted_opens(ssh);
1.5       markus    104: }
                    105:
1.10      markus    106: /*
1.69      djm       107:  * Match flag 'opt' in *optsp, and if allow_negate is set then also match
                    108:  * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0
                    109:  * if negated option matches.
                    110:  * If the option or negated option matches, then *optsp is updated to
                    111:  * point to the first character after the option and, if 'msg' is not NULL
                    112:  * then a message based on it added via auth_debug_add().
                    113:  */
                    114: static int
                    115: match_flag(const char *opt, int allow_negate, char **optsp, const char *msg)
                    116: {
                    117:        size_t opt_len = strlen(opt);
                    118:        char *opts = *optsp;
                    119:        int negate = 0;
                    120:
                    121:        if (allow_negate && strncasecmp(opts, "no-", 3) == 0) {
                    122:                opts += 3;
                    123:                negate = 1;
                    124:        }
                    125:        if (strncasecmp(opts, opt, opt_len) == 0) {
                    126:                *optsp = opts + opt_len;
                    127:                if (msg != NULL) {
                    128:                        auth_debug_add("%s %s.", msg,
                    129:                            negate ? "disabled" : "enabled");
                    130:                }
                    131:                return negate ? 0 : 1;
                    132:        }
                    133:        return -1;
                    134: }
                    135:
                    136: /*
1.10      markus    137:  * return 1 if access is granted, 0 if not.
                    138:  * side effect: sets key option flags
1.74      djm       139:  * XXX remove side effects; fill structure instead.
1.10      markus    140:  */
1.1       markus    141: int
1.73      markus    142: auth_parse_options(struct passwd *pw, char *opts, const char *file,
                    143:     u_long linenum)
1.1       markus    144: {
1.71      djm       145:        struct ssh *ssh = active_state;         /* XXX */
1.1       markus    146:        const char *cp;
1.69      djm       147:        int i, r;
1.5       markus    148:
                    149:        /* reset options */
                    150:        auth_clear_options();
1.13      markus    151:
                    152:        if (!opts)
                    153:                return 1;
1.5       markus    154:
1.12      markus    155:        while (*opts && *opts != ' ' && *opts != '\t') {
1.69      djm       156:                if ((r = match_flag("cert-authority", 0, &opts, NULL)) != -1) {
                    157:                        key_is_cert_authority = r;
1.45      djm       158:                        goto next_option;
                    159:                }
1.69      djm       160:                if ((r = match_flag("restrict", 0, &opts, NULL)) != -1) {
                    161:                        auth_debug_add("Key is restricted.");
1.1       markus    162:                        no_port_forwarding_flag = 1;
1.69      djm       163:                        no_agent_forwarding_flag = 1;
                    164:                        no_x11_forwarding_flag = 1;
                    165:                        no_pty_flag = 1;
                    166:                        no_user_rc = 1;
                    167:                        goto next_option;
                    168:                }
                    169:                if ((r = match_flag("port-forwarding", 1, &opts,
                    170:                    "Port forwarding")) != -1) {
                    171:                        no_port_forwarding_flag = r != 1;
1.1       markus    172:                        goto next_option;
                    173:                }
1.69      djm       174:                if ((r = match_flag("agent-forwarding", 1, &opts,
                    175:                    "Agent forwarding")) != -1) {
                    176:                        no_agent_forwarding_flag = r != 1;
1.1       markus    177:                        goto next_option;
                    178:                }
1.69      djm       179:                if ((r = match_flag("x11-forwarding", 1, &opts,
                    180:                    "X11 forwarding")) != -1) {
                    181:                        no_x11_forwarding_flag = r != 1;
1.1       markus    182:                        goto next_option;
                    183:                }
1.69      djm       184:                if ((r = match_flag("pty", 1, &opts,
                    185:                    "PTY allocation")) != -1) {
                    186:                        no_pty_flag = r != 1;
1.41      djm       187:                        goto next_option;
                    188:                }
1.69      djm       189:                if ((r = match_flag("user-rc", 1, &opts,
                    190:                    "User rc execution")) != -1) {
                    191:                        no_user_rc = r != 1;
1.1       markus    192:                        goto next_option;
                    193:                }
                    194:                cp = "command=\"";
1.12      markus    195:                if (strncasecmp(opts, cp, strlen(cp)) == 0) {
                    196:                        opts += strlen(cp);
1.70      mmcc      197:                        free(forced_command);
1.12      markus    198:                        forced_command = xmalloc(strlen(opts) + 1);
1.1       markus    199:                        i = 0;
1.12      markus    200:                        while (*opts) {
                    201:                                if (*opts == '"')
1.1       markus    202:                                        break;
1.12      markus    203:                                if (*opts == '\\' && opts[1] == '"') {
                    204:                                        opts += 2;
1.1       markus    205:                                        forced_command[i++] = '"';
                    206:                                        continue;
                    207:                                }
1.12      markus    208:                                forced_command[i++] = *opts++;
1.1       markus    209:                        }
1.12      markus    210:                        if (!*opts) {
1.1       markus    211:                                debug("%.100s, line %lu: missing end quote",
1.10      markus    212:                                    file, linenum);
1.24      markus    213:                                auth_debug_add("%.100s, line %lu: missing end quote",
1.10      markus    214:                                    file, linenum);
1.58      djm       215:                                free(forced_command);
1.14      markus    216:                                forced_command = NULL;
                    217:                                goto bad_option;
1.1       markus    218:                        }
1.38      dtucker   219:                        forced_command[i] = '\0';
1.54      djm       220:                        auth_debug_add("Forced command.");
1.51      djm       221:                        opts++;
                    222:                        goto next_option;
                    223:                }
                    224:                cp = "principals=\"";
                    225:                if (strncasecmp(opts, cp, strlen(cp)) == 0) {
                    226:                        opts += strlen(cp);
1.70      mmcc      227:                        free(authorized_principals);
1.51      djm       228:                        authorized_principals = xmalloc(strlen(opts) + 1);
                    229:                        i = 0;
                    230:                        while (*opts) {
                    231:                                if (*opts == '"')
                    232:                                        break;
                    233:                                if (*opts == '\\' && opts[1] == '"') {
                    234:                                        opts += 2;
                    235:                                        authorized_principals[i++] = '"';
                    236:                                        continue;
                    237:                                }
                    238:                                authorized_principals[i++] = *opts++;
                    239:                        }
                    240:                        if (!*opts) {
                    241:                                debug("%.100s, line %lu: missing end quote",
                    242:                                    file, linenum);
                    243:                                auth_debug_add("%.100s, line %lu: missing end quote",
                    244:                                    file, linenum);
1.58      djm       245:                                free(authorized_principals);
1.51      djm       246:                                authorized_principals = NULL;
                    247:                                goto bad_option;
                    248:                        }
                    249:                        authorized_principals[i] = '\0';
                    250:                        auth_debug_add("principals: %.900s",
                    251:                            authorized_principals);
1.12      markus    252:                        opts++;
1.1       markus    253:                        goto next_option;
                    254:                }
                    255:                cp = "environment=\"";
1.67      djm       256:                if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.1       markus    257:                        char *s;
                    258:                        struct envstring *new_envstring;
1.15      markus    259:
1.12      markus    260:                        opts += strlen(cp);
                    261:                        s = xmalloc(strlen(opts) + 1);
1.1       markus    262:                        i = 0;
1.12      markus    263:                        while (*opts) {
                    264:                                if (*opts == '"')
1.1       markus    265:                                        break;
1.12      markus    266:                                if (*opts == '\\' && opts[1] == '"') {
                    267:                                        opts += 2;
1.1       markus    268:                                        s[i++] = '"';
                    269:                                        continue;
                    270:                                }
1.12      markus    271:                                s[i++] = *opts++;
1.1       markus    272:                        }
1.12      markus    273:                        if (!*opts) {
1.1       markus    274:                                debug("%.100s, line %lu: missing end quote",
1.10      markus    275:                                    file, linenum);
1.24      markus    276:                                auth_debug_add("%.100s, line %lu: missing end quote",
1.10      markus    277:                                    file, linenum);
1.58      djm       278:                                free(s);
1.14      markus    279:                                goto bad_option;
1.1       markus    280:                        }
1.38      dtucker   281:                        s[i] = '\0';
1.12      markus    282:                        opts++;
1.67      djm       283:                        if (options.permit_user_env) {
                    284:                                auth_debug_add("Adding to environment: "
                    285:                                    "%.900s", s);
                    286:                                debug("Adding to environment: %.900s", s);
                    287:                                new_envstring = xcalloc(1,
                    288:                                    sizeof(*new_envstring));
                    289:                                new_envstring->s = s;
                    290:                                new_envstring->next = custom_environment;
                    291:                                custom_environment = new_envstring;
                    292:                                s = NULL;
                    293:                        }
                    294:                        free(s);
1.1       markus    295:                        goto next_option;
                    296:                }
                    297:                cp = "from=\"";
1.12      markus    298:                if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.71      djm       299:                        const char *remote_ip = ssh_remote_ipaddr(ssh);
                    300:                        const char *remote_host = auth_get_canonical_hostname(
                    301:                            ssh, options.use_dns);
1.12      markus    302:                        char *patterns = xmalloc(strlen(opts) + 1);
1.15      markus    303:
1.12      markus    304:                        opts += strlen(cp);
1.1       markus    305:                        i = 0;
1.12      markus    306:                        while (*opts) {
                    307:                                if (*opts == '"')
1.1       markus    308:                                        break;
1.12      markus    309:                                if (*opts == '\\' && opts[1] == '"') {
                    310:                                        opts += 2;
1.1       markus    311:                                        patterns[i++] = '"';
                    312:                                        continue;
                    313:                                }
1.12      markus    314:                                patterns[i++] = *opts++;
1.1       markus    315:                        }
1.12      markus    316:                        if (!*opts) {
1.1       markus    317:                                debug("%.100s, line %lu: missing end quote",
1.10      markus    318:                                    file, linenum);
1.24      markus    319:                                auth_debug_add("%.100s, line %lu: missing end quote",
1.10      markus    320:                                    file, linenum);
1.58      djm       321:                                free(patterns);
1.14      markus    322:                                goto bad_option;
1.1       markus    323:                        }
1.38      dtucker   324:                        patterns[i] = '\0';
1.12      markus    325:                        opts++;
1.43      djm       326:                        switch (match_host_and_ip(remote_host, remote_ip,
                    327:                            patterns)) {
                    328:                        case 1:
1.58      djm       329:                                free(patterns);
1.43      djm       330:                                /* Host name matches. */
                    331:                                goto next_option;
                    332:                        case -1:
                    333:                                debug("%.100s, line %lu: invalid criteria",
                    334:                                    file, linenum);
                    335:                                auth_debug_add("%.100s, line %lu: "
                    336:                                    "invalid criteria", file, linenum);
                    337:                                /* FALLTHROUGH */
                    338:                        case 0:
1.58      djm       339:                                free(patterns);
1.27      itojun    340:                                logit("Authentication tried for %.100s with "
1.12      markus    341:                                    "correct key but not from a permitted "
                    342:                                    "host (host=%.200s, ip=%.200s).",
                    343:                                    pw->pw_name, remote_host, remote_ip);
1.24      markus    344:                                auth_debug_add("Your host '%.200s' is not "
1.12      markus    345:                                    "permitted to use this key for login.",
                    346:                                    remote_host);
1.43      djm       347:                                break;
1.1       markus    348:                        }
1.43      djm       349:                        /* deny access */
                    350:                        return 0;
1.15      markus    351:                }
                    352:                cp = "permitopen=\"";
                    353:                if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.29      djm       354:                        char *host, *p;
1.44      djm       355:                        int port;
1.15      markus    356:                        char *patterns = xmalloc(strlen(opts) + 1);
                    357:
                    358:                        opts += strlen(cp);
                    359:                        i = 0;
                    360:                        while (*opts) {
                    361:                                if (*opts == '"')
                    362:                                        break;
                    363:                                if (*opts == '\\' && opts[1] == '"') {
                    364:                                        opts += 2;
                    365:                                        patterns[i++] = '"';
                    366:                                        continue;
                    367:                                }
                    368:                                patterns[i++] = *opts++;
                    369:                        }
                    370:                        if (!*opts) {
                    371:                                debug("%.100s, line %lu: missing end quote",
                    372:                                    file, linenum);
1.29      djm       373:                                auth_debug_add("%.100s, line %lu: missing "
                    374:                                    "end quote", file, linenum);
1.58      djm       375:                                free(patterns);
1.15      markus    376:                                goto bad_option;
                    377:                        }
1.38      dtucker   378:                        patterns[i] = '\0';
1.15      markus    379:                        opts++;
1.29      djm       380:                        p = patterns;
1.64      millert   381:                        /* XXX - add streamlocal support */
1.29      djm       382:                        host = hpdelim(&p);
                    383:                        if (host == NULL || strlen(host) >= NI_MAXHOST) {
                    384:                                debug("%.100s, line %lu: Bad permitopen "
1.31      deraadt   385:                                    "specification <%.100s>", file, linenum,
1.29      djm       386:                                    patterns);
1.24      markus    387:                                auth_debug_add("%.100s, line %lu: "
1.29      djm       388:                                    "Bad permitopen specification", file,
                    389:                                    linenum);
1.58      djm       390:                                free(patterns);
1.15      markus    391:                                goto bad_option;
                    392:                        }
1.30      deraadt   393:                        host = cleanhostname(host);
1.55      dtucker   394:                        if (p == NULL || (port = permitopen_port(p)) < 0) {
1.29      djm       395:                                debug("%.100s, line %lu: Bad permitopen port "
                    396:                                    "<%.100s>", file, linenum, p ? p : "");
1.24      markus    397:                                auth_debug_add("%.100s, line %lu: "
1.20      stevesk   398:                                    "Bad permitopen port", file, linenum);
1.58      djm       399:                                free(patterns);
1.15      markus    400:                                goto bad_option;
                    401:                        }
1.57      djm       402:                        if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0)
1.74      djm       403:                                channel_add_permitted_opens(ssh, host, port);
1.58      djm       404:                        free(patterns);
1.32      reyk      405:                        goto next_option;
                    406:                }
                    407:                cp = "tunnel=\"";
                    408:                if (strncasecmp(opts, cp, strlen(cp)) == 0) {
                    409:                        char *tun = NULL;
                    410:                        opts += strlen(cp);
                    411:                        tun = xmalloc(strlen(opts) + 1);
                    412:                        i = 0;
                    413:                        while (*opts) {
                    414:                                if (*opts == '"')
                    415:                                        break;
                    416:                                tun[i++] = *opts++;
                    417:                        }
                    418:                        if (!*opts) {
                    419:                                debug("%.100s, line %lu: missing end quote",
                    420:                                    file, linenum);
                    421:                                auth_debug_add("%.100s, line %lu: missing end quote",
                    422:                                    file, linenum);
1.58      djm       423:                                free(tun);
1.32      reyk      424:                                forced_tun_device = -1;
                    425:                                goto bad_option;
                    426:                        }
1.38      dtucker   427:                        tun[i] = '\0';
1.32      reyk      428:                        forced_tun_device = a2tun(tun, NULL);
1.58      djm       429:                        free(tun);
1.33      reyk      430:                        if (forced_tun_device == SSH_TUNID_ERR) {
1.32      reyk      431:                                debug("%.100s, line %lu: invalid tun device",
                    432:                                    file, linenum);
                    433:                                auth_debug_add("%.100s, line %lu: invalid tun device",
                    434:                                    file, linenum);
                    435:                                forced_tun_device = -1;
                    436:                                goto bad_option;
                    437:                        }
                    438:                        auth_debug_add("Forced tun device: %d", forced_tun_device);
                    439:                        opts++;
1.1       markus    440:                        goto next_option;
                    441:                }
                    442: next_option:
                    443:                /*
                    444:                 * Skip the comma, and move to the next option
                    445:                 * (or break out if there are no more).
                    446:                 */
1.12      markus    447:                if (!*opts)
1.1       markus    448:                        fatal("Bugs in auth-options.c option processing.");
1.12      markus    449:                if (*opts == ' ' || *opts == '\t')
1.1       markus    450:                        break;          /* End of options. */
1.12      markus    451:                if (*opts != ',')
1.1       markus    452:                        goto bad_option;
1.12      markus    453:                opts++;
1.1       markus    454:                /* Process the next option. */
                    455:        }
1.22      provos    456:
1.1       markus    457:        /* grant access */
                    458:        return 1;
                    459:
                    460: bad_option:
1.27      itojun    461:        logit("Bad options in %.100s file, line %lu: %.50s",
1.12      markus    462:            file, linenum, opts);
1.24      markus    463:        auth_debug_add("Bad options in %.100s file, line %lu: %.50s",
1.12      markus    464:            file, linenum, opts);
1.22      provos    465:
1.1       markus    466:        /* deny access */
                    467:        return 0;
                    468: }
1.45      djm       469:
1.52      djm       470: #define OPTIONS_CRITICAL       1
                    471: #define OPTIONS_EXTENSIONS     2
                    472: static int
1.65      markus    473: parse_option_list(struct sshbuf *oblob, struct passwd *pw,
1.52      djm       474:     u_int which, int crit,
                    475:     int *cert_no_port_forwarding_flag,
                    476:     int *cert_no_agent_forwarding_flag,
                    477:     int *cert_no_x11_forwarding_flag,
                    478:     int *cert_no_pty_flag,
                    479:     int *cert_no_user_rc,
                    480:     char **cert_forced_command,
                    481:     int *cert_source_address_done)
1.45      djm       482: {
1.71      djm       483:        struct ssh *ssh = active_state;         /* XXX */
1.52      djm       484:        char *command, *allowed;
                    485:        const char *remote_ip;
1.59      djm       486:        char *name = NULL;
1.65      markus    487:        struct sshbuf *c = NULL, *data = NULL;
                    488:        int r, ret = -1, result, found;
                    489:
                    490:        if ((c = sshbuf_fromb(oblob)) == NULL) {
                    491:                error("%s: sshbuf_fromb failed", __func__);
                    492:                goto out;
                    493:        }
                    494:
                    495:        while (sshbuf_len(c) > 0) {
                    496:                sshbuf_free(data);
                    497:                data = NULL;
                    498:                if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 ||
                    499:                    (r = sshbuf_froms(c, &data)) != 0) {
                    500:                        error("Unable to parse certificate options: %s",
                    501:                            ssh_err(r));
1.45      djm       502:                        goto out;
                    503:                }
1.65      markus    504:                debug3("found certificate option \"%.100s\" len %zu",
                    505:                    name, sshbuf_len(data));
1.52      djm       506:                found = 0;
                    507:                if ((which & OPTIONS_EXTENSIONS) != 0) {
                    508:                        if (strcmp(name, "permit-X11-forwarding") == 0) {
                    509:                                *cert_no_x11_forwarding_flag = 0;
                    510:                                found = 1;
                    511:                        } else if (strcmp(name,
                    512:                            "permit-agent-forwarding") == 0) {
                    513:                                *cert_no_agent_forwarding_flag = 0;
                    514:                                found = 1;
                    515:                        } else if (strcmp(name,
                    516:                            "permit-port-forwarding") == 0) {
                    517:                                *cert_no_port_forwarding_flag = 0;
                    518:                                found = 1;
                    519:                        } else if (strcmp(name, "permit-pty") == 0) {
                    520:                                *cert_no_pty_flag = 0;
                    521:                                found = 1;
                    522:                        } else if (strcmp(name, "permit-user-rc") == 0) {
                    523:                                *cert_no_user_rc = 0;
                    524:                                found = 1;
                    525:                        }
                    526:                }
                    527:                if (!found && (which & OPTIONS_CRITICAL) != 0) {
                    528:                        if (strcmp(name, "force-command") == 0) {
1.65      markus    529:                                if ((r = sshbuf_get_cstring(data, &command,
                    530:                                    NULL)) != 0) {
                    531:                                        error("Unable to parse \"%s\" "
                    532:                                            "section: %s", name, ssh_err(r));
1.52      djm       533:                                        goto out;
                    534:                                }
                    535:                                if (*cert_forced_command != NULL) {
                    536:                                        error("Certificate has multiple "
                    537:                                            "force-command options");
1.58      djm       538:                                        free(command);
1.52      djm       539:                                        goto out;
                    540:                                }
                    541:                                *cert_forced_command = command;
                    542:                                found = 1;
1.45      djm       543:                        }
1.52      djm       544:                        if (strcmp(name, "source-address") == 0) {
1.65      markus    545:                                if ((r = sshbuf_get_cstring(data, &allowed,
                    546:                                    NULL)) != 0) {
                    547:                                        error("Unable to parse \"%s\" "
                    548:                                            "section: %s", name, ssh_err(r));
1.52      djm       549:                                        goto out;
                    550:                                }
                    551:                                if ((*cert_source_address_done)++) {
                    552:                                        error("Certificate has multiple "
                    553:                                            "source-address options");
1.58      djm       554:                                        free(allowed);
1.52      djm       555:                                        goto out;
                    556:                                }
1.71      djm       557:                                remote_ip = ssh_remote_ipaddr(ssh);
1.62      djm       558:                                result = addr_match_cidr_list(remote_ip,
                    559:                                    allowed);
                    560:                                free(allowed);
                    561:                                switch (result) {
1.52      djm       562:                                case 1:
                    563:                                        /* accepted */
                    564:                                        break;
                    565:                                case 0:
                    566:                                        /* no match */
                    567:                                        logit("Authentication tried for %.100s "
                    568:                                            "with valid certificate but not "
                    569:                                            "from a permitted host "
                    570:                                            "(ip=%.200s).", pw->pw_name,
                    571:                                            remote_ip);
                    572:                                        auth_debug_add("Your address '%.200s' "
                    573:                                            "is not permitted to use this "
                    574:                                            "certificate for login.",
                    575:                                            remote_ip);
                    576:                                        goto out;
                    577:                                case -1:
1.62      djm       578:                                default:
1.52      djm       579:                                        error("Certificate source-address "
                    580:                                            "contents invalid");
                    581:                                        goto out;
                    582:                                }
                    583:                                found = 1;
1.46      djm       584:                        }
1.52      djm       585:                }
                    586:
                    587:                if (!found) {
                    588:                        if (crit) {
                    589:                                error("Certificate critical option \"%s\" "
                    590:                                    "is not supported", name);
1.45      djm       591:                                goto out;
1.52      djm       592:                        } else {
                    593:                                logit("Certificate extension \"%s\" "
                    594:                                    "is not supported", name);
1.45      djm       595:                        }
1.65      markus    596:                } else if (sshbuf_len(data) != 0) {
1.52      djm       597:                        error("Certificate option \"%s\" corrupt "
1.45      djm       598:                            "(extra data)", name);
                    599:                        goto out;
                    600:                }
1.58      djm       601:                free(name);
1.59      djm       602:                name = NULL;
1.45      djm       603:        }
1.50      djm       604:        /* successfully parsed all options */
1.45      djm       605:        ret = 0;
                    606:
1.52      djm       607:  out:
                    608:        if (ret != 0 &&
                    609:            cert_forced_command != NULL &&
                    610:            *cert_forced_command != NULL) {
1.58      djm       611:                free(*cert_forced_command);
1.52      djm       612:                *cert_forced_command = NULL;
                    613:        }
1.70      mmcc      614:        free(name);
1.65      markus    615:        sshbuf_free(data);
                    616:        sshbuf_free(c);
1.52      djm       617:        return ret;
                    618: }
                    619:
                    620: /*
                    621:  * Set options from critical certificate options. These supersede user key
                    622:  * options so this must be called after auth_parse_options().
                    623:  */
                    624: int
1.72      djm       625: auth_cert_options(struct sshkey *k, struct passwd *pw, const char **reason)
1.52      djm       626: {
                    627:        int cert_no_port_forwarding_flag = 1;
                    628:        int cert_no_agent_forwarding_flag = 1;
                    629:        int cert_no_x11_forwarding_flag = 1;
                    630:        int cert_no_pty_flag = 1;
                    631:        int cert_no_user_rc = 1;
                    632:        char *cert_forced_command = NULL;
                    633:        int cert_source_address_done = 0;
                    634:
1.72      djm       635:        *reason = "invalid certificate options";
                    636:
1.68      djm       637:        /* Separate options and extensions for v01 certs */
                    638:        if (parse_option_list(k->cert->critical, pw,
                    639:            OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL,
                    640:            &cert_forced_command,
                    641:            &cert_source_address_done) == -1)
                    642:                return -1;
                    643:        if (parse_option_list(k->cert->extensions, pw,
                    644:            OPTIONS_EXTENSIONS, 0,
                    645:            &cert_no_port_forwarding_flag,
                    646:            &cert_no_agent_forwarding_flag,
                    647:            &cert_no_x11_forwarding_flag,
                    648:            &cert_no_pty_flag,
                    649:            &cert_no_user_rc,
                    650:            NULL, NULL) == -1)
                    651:                return -1;
1.52      djm       652:
1.45      djm       653:        no_port_forwarding_flag |= cert_no_port_forwarding_flag;
                    654:        no_agent_forwarding_flag |= cert_no_agent_forwarding_flag;
                    655:        no_x11_forwarding_flag |= cert_no_x11_forwarding_flag;
                    656:        no_pty_flag |= cert_no_pty_flag;
                    657:        no_user_rc |= cert_no_user_rc;
1.72      djm       658:        /*
                    659:         * Only permit both CA and key option forced-command if they match.
                    660:         * Otherwise refuse the certificate.
                    661:         */
                    662:        if (cert_forced_command != NULL && forced_command != NULL) {
                    663:                if (strcmp(forced_command, cert_forced_command) == 0) {
                    664:                        free(forced_command);
                    665:                        forced_command = cert_forced_command;
                    666:                } else {
                    667:                        *reason = "certificate and key options forced command "
                    668:                            "do not match";
                    669:                        free(cert_forced_command);
                    670:                        return -1;
                    671:                }
                    672:        } else if (cert_forced_command != NULL)
1.45      djm       673:                forced_command = cert_forced_command;
1.72      djm       674:        /* success */
                    675:        *reason = NULL;
1.52      djm       676:        return 0;
1.45      djm       677: }
                    678:
1.75    ! djm       679: /*
        !           680:  * authorized_keys options processing.
        !           681:  */
        !           682:
        !           683: /*
        !           684:  * Match flag 'opt' in *optsp, and if allow_negate is set then also match
        !           685:  * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0
        !           686:  * if negated option matches.
        !           687:  * If the option or negated option matches, then *optsp is updated to
        !           688:  * point to the first character after the option.
        !           689:  */
        !           690: static int
        !           691: opt_flag(const char *opt, int allow_negate, const char **optsp)
        !           692: {
        !           693:        size_t opt_len = strlen(opt);
        !           694:        const char *opts = *optsp;
        !           695:        int negate = 0;
        !           696:
        !           697:        if (allow_negate && strncasecmp(opts, "no-", 3) == 0) {
        !           698:                opts += 3;
        !           699:                negate = 1;
        !           700:        }
        !           701:        if (strncasecmp(opts, opt, opt_len) == 0) {
        !           702:                *optsp = opts + opt_len;
        !           703:                return negate ? 0 : 1;
        !           704:        }
        !           705:        return -1;
        !           706: }
        !           707:
        !           708: static char *
        !           709: opt_dequote(const char **sp, const char **errstrp)
        !           710: {
        !           711:        const char *s = *sp;
        !           712:        char *ret;
        !           713:        size_t i;
        !           714:
        !           715:        *errstrp = NULL;
        !           716:        if (*s != '"') {
        !           717:                *errstrp = "missing start quote";
        !           718:                return NULL;
        !           719:        }
        !           720:        s++;
        !           721:        if ((ret = malloc(strlen((s)) + 1)) == NULL) {
        !           722:                *errstrp = "memory allocation failed";
        !           723:                return NULL;
        !           724:        }
        !           725:        for (i = 0; *s != '\0' && *s != '"';) {
        !           726:                if (s[0] == '\\' && s[1] == '"')
        !           727:                        s++;
        !           728:                ret[i++] = *s++;
        !           729:        }
        !           730:        if (*s == '\0') {
        !           731:                *errstrp = "missing end quote";
        !           732:                free(ret);
        !           733:                return NULL;
        !           734:        }
        !           735:        ret[i] = '\0';
        !           736:        s++;
        !           737:        *sp = s;
        !           738:        return ret;
        !           739: }
        !           740:
        !           741: static int
        !           742: opt_match(const char **opts, const char *term)
        !           743: {
        !           744:        if (strncasecmp((*opts), term, strlen(term)) == 0 &&
        !           745:            (*opts)[strlen(term)] == '=') {
        !           746:                *opts += strlen(term) + 1;
        !           747:                return 1;
        !           748:        }
        !           749:        return 0;
        !           750: }
        !           751:
        !           752: static int
        !           753: dup_strings(char ***dstp, size_t *ndstp, char **src, size_t nsrc)
        !           754: {
        !           755:        char **dst;
        !           756:        size_t i, j;
        !           757:
        !           758:        *dstp = NULL;
        !           759:        *ndstp = 0;
        !           760:        if (nsrc == 0)
        !           761:                return 0;
        !           762:
        !           763:        if ((dst = calloc(nsrc, sizeof(*src))) == NULL)
        !           764:                return -1;
        !           765:        for (i = 0; i < nsrc; i++) {
        !           766:                if ((dst[i] = strdup(src[i])) == NULL) {
        !           767:                        for (j = 0; j < i; j++)
        !           768:                                free(dst[j]);
        !           769:                        free(dst);
        !           770:                        return -1;
        !           771:                }
        !           772:        }
        !           773:        /* success */
        !           774:        *dstp = dst;
        !           775:        *ndstp = nsrc;
        !           776:        return 0;
        !           777: }
        !           778:
        !           779: #define OPTIONS_CRITICAL       1
        !           780: #define OPTIONS_EXTENSIONS     2
        !           781: static int
        !           782: cert_option_list(struct sshauthopt *opts, struct sshbuf *oblob,
        !           783:     u_int which, int crit)
        !           784: {
        !           785:        char *command, *allowed;
        !           786:        char *name = NULL;
        !           787:        struct sshbuf *c = NULL, *data = NULL;
        !           788:        int r, ret = -1, found;
        !           789:
        !           790:        if ((c = sshbuf_fromb(oblob)) == NULL) {
        !           791:                error("%s: sshbuf_fromb failed", __func__);
        !           792:                goto out;
        !           793:        }
        !           794:
        !           795:        while (sshbuf_len(c) > 0) {
        !           796:                sshbuf_free(data);
        !           797:                data = NULL;
        !           798:                if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 ||
        !           799:                    (r = sshbuf_froms(c, &data)) != 0) {
        !           800:                        error("Unable to parse certificate options: %s",
        !           801:                            ssh_err(r));
        !           802:                        goto out;
        !           803:                }
        !           804:                debug3("found certificate option \"%.100s\" len %zu",
        !           805:                    name, sshbuf_len(data));
        !           806:                found = 0;
        !           807:                if ((which & OPTIONS_EXTENSIONS) != 0) {
        !           808:                        if (strcmp(name, "permit-X11-forwarding") == 0) {
        !           809:                                opts->permit_x11_forwarding_flag = 1;
        !           810:                                found = 1;
        !           811:                        } else if (strcmp(name,
        !           812:                            "permit-agent-forwarding") == 0) {
        !           813:                                opts->permit_agent_forwarding_flag = 1;
        !           814:                                found = 1;
        !           815:                        } else if (strcmp(name,
        !           816:                            "permit-port-forwarding") == 0) {
        !           817:                                opts->permit_port_forwarding_flag = 1;
        !           818:                                found = 1;
        !           819:                        } else if (strcmp(name, "permit-pty") == 0) {
        !           820:                                opts->permit_pty_flag = 1;
        !           821:                                found = 1;
        !           822:                        } else if (strcmp(name, "permit-user-rc") == 0) {
        !           823:                                opts->permit_user_rc = 1;
        !           824:                                found = 1;
        !           825:                        }
        !           826:                }
        !           827:                if (!found && (which & OPTIONS_CRITICAL) != 0) {
        !           828:                        if (strcmp(name, "force-command") == 0) {
        !           829:                                if ((r = sshbuf_get_cstring(data, &command,
        !           830:                                    NULL)) != 0) {
        !           831:                                        error("Unable to parse \"%s\" "
        !           832:                                            "section: %s", name, ssh_err(r));
        !           833:                                        goto out;
        !           834:                                }
        !           835:                                if (opts->force_command != NULL) {
        !           836:                                        error("Certificate has multiple "
        !           837:                                            "force-command options");
        !           838:                                        free(command);
        !           839:                                        goto out;
        !           840:                                }
        !           841:                                opts->force_command = command;
        !           842:                                found = 1;
        !           843:                        }
        !           844:                        if (strcmp(name, "source-address") == 0) {
        !           845:                                if ((r = sshbuf_get_cstring(data, &allowed,
        !           846:                                    NULL)) != 0) {
        !           847:                                        error("Unable to parse \"%s\" "
        !           848:                                            "section: %s", name, ssh_err(r));
        !           849:                                        goto out;
        !           850:                                }
        !           851:                                if (opts->required_from_host_cert != NULL) {
        !           852:                                        error("Certificate has multiple "
        !           853:                                            "source-address options");
        !           854:                                        free(allowed);
        !           855:                                        goto out;
        !           856:                                }
        !           857:                                /* Check syntax */
        !           858:                                if (addr_match_cidr_list(NULL, allowed) == -1) {
        !           859:                                        error("Certificate source-address "
        !           860:                                            "contents invalid");
        !           861:                                        goto out;
        !           862:                                }
        !           863:                                opts->required_from_host_cert = allowed;
        !           864:                                found = 1;
        !           865:                        }
        !           866:                }
        !           867:
        !           868:                if (!found) {
        !           869:                        if (crit) {
        !           870:                                error("Certificate critical option \"%s\" "
        !           871:                                    "is not supported", name);
        !           872:                                goto out;
        !           873:                        } else {
        !           874:                                logit("Certificate extension \"%s\" "
        !           875:                                    "is not supported", name);
        !           876:                        }
        !           877:                } else if (sshbuf_len(data) != 0) {
        !           878:                        error("Certificate option \"%s\" corrupt "
        !           879:                            "(extra data)", name);
        !           880:                        goto out;
        !           881:                }
        !           882:                free(name);
        !           883:                name = NULL;
        !           884:        }
        !           885:        /* successfully parsed all options */
        !           886:        ret = 0;
        !           887:
        !           888:  out:
        !           889:        free(name);
        !           890:        sshbuf_free(data);
        !           891:        sshbuf_free(c);
        !           892:        return ret;
        !           893: }
        !           894:
        !           895: struct sshauthopt *
        !           896: sshauthopt_new(void)
        !           897: {
        !           898:        struct sshauthopt *ret;
        !           899:
        !           900:        if ((ret = calloc(1, sizeof(*ret))) == NULL)
        !           901:                return NULL;
        !           902:        ret->force_tun_device = -1;
        !           903:        return ret;
        !           904: }
        !           905:
        !           906: void
        !           907: sshauthopt_free(struct sshauthopt *opts)
        !           908: {
        !           909:        size_t i;
        !           910:
        !           911:        if (opts == NULL)
        !           912:                return;
        !           913:
        !           914:        free(opts->cert_principals);
        !           915:        free(opts->force_command);
        !           916:        free(opts->required_from_host_cert);
        !           917:        free(opts->required_from_host_keys);
        !           918:
        !           919:        for (i = 0; i < opts->nenv; i++)
        !           920:                free(opts->env[i]);
        !           921:        free(opts->env);
        !           922:
        !           923:        for (i = 0; i < opts->npermitopen; i++)
        !           924:                free(opts->permitopen[i]);
        !           925:        free(opts->permitopen);
        !           926:
        !           927:        explicit_bzero(opts, sizeof(*opts));
        !           928:        free(opts);
        !           929: }
        !           930:
        !           931: struct sshauthopt *
        !           932: sshauthopt_new_with_keys_defaults(void)
        !           933: {
        !           934:        struct sshauthopt *ret = NULL;
        !           935:
        !           936:        if ((ret = sshauthopt_new()) == NULL)
        !           937:                return NULL;
        !           938:
        !           939:        /* Defaults for authorized_keys flags */
        !           940:        ret->permit_port_forwarding_flag = 1;
        !           941:        ret->permit_agent_forwarding_flag = 1;
        !           942:        ret->permit_x11_forwarding_flag = 1;
        !           943:        ret->permit_pty_flag = 1;
        !           944:        ret->permit_user_rc = 1;
        !           945:        return ret;
        !           946: }
        !           947:
        !           948: struct sshauthopt *
        !           949: sshauthopt_parse(const char *opts, const char **errstrp)
        !           950: {
        !           951:        char **oarray, *opt, *cp, *tmp, *host;
        !           952:        int r;
        !           953:        struct sshauthopt *ret = NULL;
        !           954:        const char *errstr = "unknown error";
        !           955:
        !           956:        if (errstrp != NULL)
        !           957:                *errstrp = NULL;
        !           958:        if ((ret = sshauthopt_new_with_keys_defaults()) == NULL)
        !           959:                goto alloc_fail;
        !           960:
        !           961:        if (opts == NULL)
        !           962:                return ret;
        !           963:
        !           964:        while (*opts && *opts != ' ' && *opts != '\t') {
        !           965:                /* flag options */
        !           966:                if ((r = opt_flag("restrict", 0, &opts)) != -1) {
        !           967:                        ret->restricted = 1;
        !           968:                        ret->permit_port_forwarding_flag = 0;
        !           969:                        ret->permit_agent_forwarding_flag = 0;
        !           970:                        ret->permit_x11_forwarding_flag = 0;
        !           971:                        ret->permit_pty_flag = 0;
        !           972:                        ret->permit_user_rc = 0;
        !           973:                } else if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
        !           974:                        ret->cert_authority = r;
        !           975:                } else if ((r = opt_flag("port-forwarding", 1, &opts)) != -1) {
        !           976:                        ret->permit_port_forwarding_flag = r == 1;
        !           977:                } else if ((r = opt_flag("agent-forwarding", 1, &opts)) != -1) {
        !           978:                        ret->permit_agent_forwarding_flag = r == 1;
        !           979:                } else if ((r = opt_flag("x11-forwarding", 1, &opts)) != -1) {
        !           980:                        ret->permit_x11_forwarding_flag = r == 1;
        !           981:                } else if ((r = opt_flag("pty", 1, &opts)) != -1) {
        !           982:                        ret->permit_pty_flag = r == 1;
        !           983:                } else if ((r = opt_flag("user-rc", 1, &opts)) != -1) {
        !           984:                        ret->permit_user_rc = r == 1;
        !           985:                } else if (opt_match(&opts, "command")) {
        !           986:                        if (ret->force_command != NULL) {
        !           987:                                errstr = "multiple \"command\" clauses";
        !           988:                                goto fail;
        !           989:                        }
        !           990:                        ret->force_command = opt_dequote(&opts, &errstr);
        !           991:                        if (ret->force_command == NULL)
        !           992:                                goto fail;
        !           993:                } else if (opt_match(&opts, "principals")) {
        !           994:                        if (ret->cert_principals != NULL) {
        !           995:                                errstr = "multiple \"principals\" clauses";
        !           996:                                goto fail;
        !           997:                        }
        !           998:                        ret->cert_principals = opt_dequote(&opts, &errstr);
        !           999:                        if (ret->cert_principals == NULL)
        !          1000:                                goto fail;
        !          1001:                } else if (opt_match(&opts, "from")) {
        !          1002:                        if (ret->required_from_host_keys != NULL) {
        !          1003:                                errstr = "multiple \"from\" clauses";
        !          1004:                                goto fail;
        !          1005:                        }
        !          1006:                        ret->required_from_host_keys = opt_dequote(&opts,
        !          1007:                            &errstr);
        !          1008:                        if (ret->required_from_host_keys == NULL)
        !          1009:                                goto fail;
        !          1010:                } else if (opt_match(&opts, "environment")) {
        !          1011:                        if (ret->nenv > INT_MAX) {
        !          1012:                                errstr = "too many environment strings";
        !          1013:                                goto fail;
        !          1014:                        }
        !          1015:                        if ((opt = opt_dequote(&opts, &errstr)) == NULL)
        !          1016:                                goto fail;
        !          1017:                        /* env name must be alphanumeric and followed by '=' */
        !          1018:                        if ((tmp = strchr(opt, '=')) == NULL) {
        !          1019:                                free(opt);
        !          1020:                                errstr = "invalid environment string";
        !          1021:                                goto fail;
        !          1022:                        }
        !          1023:                        for (cp = opt; cp < tmp; cp++) {
        !          1024:                                if (!isalnum((u_char)*cp)) {
        !          1025:                                        free(opt);
        !          1026:                                        errstr = "invalid environment string";
        !          1027:                                        goto fail;
        !          1028:                                }
        !          1029:                        }
        !          1030:                        /* Append it. */
        !          1031:                        oarray = ret->env;
        !          1032:                        if ((ret->env = recallocarray(ret->env, ret->nenv,
        !          1033:                            ret->nenv + 1, sizeof(*ret->env))) == NULL) {
        !          1034:                                free(opt);
        !          1035:                                ret->env = oarray; /* put it back for cleanup */
        !          1036:                                goto alloc_fail;
        !          1037:                        }
        !          1038:                        ret->env[ret->nenv++] = opt;
        !          1039:                } else if (opt_match(&opts, "permitopen")) {
        !          1040:                        if (ret->npermitopen > INT_MAX) {
        !          1041:                                errstr = "too many permitopens";
        !          1042:                                goto fail;
        !          1043:                        }
        !          1044:                        if ((opt = opt_dequote(&opts, &errstr)) == NULL)
        !          1045:                                goto fail;
        !          1046:                        if ((tmp = strdup(opt)) == NULL) {
        !          1047:                                free(opt);
        !          1048:                                goto alloc_fail;
        !          1049:                        }
        !          1050:                        cp = tmp;
        !          1051:                        /* validate syntax of permitopen before recording it. */
        !          1052:                        host = hpdelim(&cp);
        !          1053:                        if (host == NULL || strlen(host) >= NI_MAXHOST) {
        !          1054:                                free(tmp);
        !          1055:                                free(opt);
        !          1056:                                errstr = "invalid permitopen hostname";
        !          1057:                                goto fail;
        !          1058:                        }
        !          1059:                        /*
        !          1060:                         * don't want to use permitopen_port to avoid
        !          1061:                         * dependency on channels.[ch] here.
        !          1062:                         */
        !          1063:                        if (cp == NULL ||
        !          1064:                            (strcmp(cp, "*") != 0 && a2port(cp) <= 0)) {
        !          1065:                                free(tmp);
        !          1066:                                free(opt);
        !          1067:                                errstr = "invalid permitopen port";
        !          1068:                                goto fail;
        !          1069:                        }
        !          1070:                        /* XXX - add streamlocal support */
        !          1071:                        free(tmp);
        !          1072:                        /* Record it */
        !          1073:                        oarray = ret->permitopen;
        !          1074:                        if ((ret->permitopen = recallocarray(ret->permitopen,
        !          1075:                            ret->npermitopen, ret->npermitopen + 1,
        !          1076:                            sizeof(*ret->permitopen))) == NULL) {
        !          1077:                                free(opt);
        !          1078:                                ret->permitopen = oarray;
        !          1079:                                goto alloc_fail;
        !          1080:                        }
        !          1081:                        ret->permitopen[ret->npermitopen++] = opt;
        !          1082:                } else if (opt_match(&opts, "tunnel")) {
        !          1083:                        if ((opt = opt_dequote(&opts, &errstr)) == NULL)
        !          1084:                                goto fail;
        !          1085:                        ret->force_tun_device = a2tun(opt, NULL);
        !          1086:                        free(opt);
        !          1087:                        if (ret->force_tun_device == SSH_TUNID_ERR) {
        !          1088:                                errstr = "invalid tun device";
        !          1089:                                goto fail;
        !          1090:                        }
        !          1091:                }
        !          1092:                /*
        !          1093:                 * Skip the comma, and move to the next option
        !          1094:                 * (or break out if there are no more).
        !          1095:                 */
        !          1096:                if (*opts == '\0' || *opts == ' ' || *opts == '\t')
        !          1097:                        break;          /* End of options. */
        !          1098:                /* Anything other than a comma is an unknown option */
        !          1099:                if (*opts != ',') {
        !          1100:                        errstr = "unknown key option";
        !          1101:                        goto fail;
        !          1102:                }
        !          1103:                opts++;
        !          1104:                if (*opts == '\0') {
        !          1105:                        errstr = "unexpected end-of-options";
        !          1106:                        goto fail;
        !          1107:                }
        !          1108:        }
        !          1109:
        !          1110:        /* success */
        !          1111:        if (errstrp != NULL)
        !          1112:                *errstrp = NULL;
        !          1113:        return ret;
        !          1114:
        !          1115: alloc_fail:
        !          1116:        errstr = "memory allocation failed";
        !          1117: fail:
        !          1118:        sshauthopt_free(ret);
        !          1119:        if (errstrp != NULL)
        !          1120:                *errstrp = errstr;
        !          1121:        return NULL;
        !          1122: }
        !          1123:
        !          1124: struct sshauthopt *
        !          1125: sshauthopt_from_cert(struct sshkey *k)
        !          1126: {
        !          1127:        struct sshauthopt *ret;
        !          1128:
        !          1129:        if (k == NULL || !sshkey_type_is_cert(k->type) || k->cert == NULL ||
        !          1130:            k->cert->type != SSH2_CERT_TYPE_USER)
        !          1131:                return NULL;
        !          1132:
        !          1133:        if ((ret = sshauthopt_new()) == NULL)
        !          1134:                return NULL;
        !          1135:
        !          1136:        /* Handle options and critical extensions separately */
        !          1137:        if (cert_option_list(ret, k->cert->critical,
        !          1138:            OPTIONS_CRITICAL, 1) == -1) {
        !          1139:                sshauthopt_free(ret);
        !          1140:                return NULL;
        !          1141:        }
        !          1142:        if (cert_option_list(ret, k->cert->extensions,
        !          1143:            OPTIONS_EXTENSIONS, 0) == -1) {
        !          1144:                sshauthopt_free(ret);
        !          1145:                return NULL;
        !          1146:        }
        !          1147:        /* success */
        !          1148:        return ret;
        !          1149: }
        !          1150:
        !          1151: /*
        !          1152:  * Merges "additional" options to "primary" and returns the result.
        !          1153:  * NB. Some options from primary have primacy.
        !          1154:  */
        !          1155: struct sshauthopt *
        !          1156: sshauthopt_merge(const struct sshauthopt *primary,
        !          1157:     const struct sshauthopt *additional, const char **errstrp)
        !          1158: {
        !          1159:        struct sshauthopt *ret;
        !          1160:        const char *errstr = "internal error";
        !          1161:        const char *tmp;
        !          1162:
        !          1163:        if (errstrp != NULL)
        !          1164:                *errstrp = NULL;
        !          1165:
        !          1166:        if ((ret = sshauthopt_new()) == NULL)
        !          1167:                goto alloc_fail;
        !          1168:
        !          1169:        /* cert_authority and cert_principals are cleared in result */
        !          1170:
        !          1171:        /* Prefer access lists from primary. */
        !          1172:        /* XXX err is both set and mismatch? */
        !          1173:        tmp = primary->required_from_host_cert;
        !          1174:        if (tmp == NULL)
        !          1175:                tmp = additional->required_from_host_cert;
        !          1176:        if (tmp != NULL && (ret->required_from_host_cert = strdup(tmp)) == NULL)
        !          1177:                goto alloc_fail;
        !          1178:        tmp = primary->required_from_host_keys;
        !          1179:        if (tmp == NULL)
        !          1180:                tmp = additional->required_from_host_keys;
        !          1181:        if (tmp != NULL && (ret->required_from_host_keys = strdup(tmp)) == NULL)
        !          1182:                goto alloc_fail;
        !          1183:
        !          1184:        /* force_tun_device, permitopen and environment prefer the primary. */
        !          1185:        ret->force_tun_device = primary->force_tun_device;
        !          1186:        if (ret->force_tun_device == -1)
        !          1187:                ret->force_tun_device = additional->force_tun_device;
        !          1188:        if (primary->nenv > 0) {
        !          1189:                if (dup_strings(&ret->env, &ret->nenv,
        !          1190:                    primary->env, primary->nenv) != 0)
        !          1191:                        goto alloc_fail;
        !          1192:        } else if (additional->nenv) {
        !          1193:                if (dup_strings(&ret->env, &ret->nenv,
        !          1194:                    additional->env, additional->nenv) != 0)
        !          1195:                        goto alloc_fail;
        !          1196:        }
        !          1197:        if (primary->npermitopen > 0) {
        !          1198:                if (dup_strings(&ret->permitopen, &ret->npermitopen,
        !          1199:                    primary->permitopen, primary->npermitopen) != 0)
        !          1200:                        goto alloc_fail;
        !          1201:        } else if (additional->npermitopen > 0) {
        !          1202:                if (dup_strings(&ret->permitopen, &ret->npermitopen,
        !          1203:                    additional->permitopen, additional->npermitopen) != 0)
        !          1204:                        goto alloc_fail;
        !          1205:        }
        !          1206:
        !          1207:        /* Flags are logical-AND (i.e. must be set in both for permission) */
        !          1208: #define OPTFLAG(x) ret->x = (primary->x == 1) && (additional->x == 1)
        !          1209:        OPTFLAG(permit_port_forwarding_flag);
        !          1210:        OPTFLAG(permit_agent_forwarding_flag);
        !          1211:        OPTFLAG(permit_x11_forwarding_flag);
        !          1212:        OPTFLAG(permit_pty_flag);
        !          1213:        OPTFLAG(permit_user_rc);
        !          1214: #undef OPTFLAG
        !          1215:
        !          1216:        /*
        !          1217:         * When both multiple forced-command are specified, only
        !          1218:         * proceed if they are identical, otherwise fail.
        !          1219:         */
        !          1220:        if (primary->force_command != NULL &&
        !          1221:            additional->force_command != NULL) {
        !          1222:                if (strcmp(primary->force_command,
        !          1223:                    additional->force_command) == 0) {
        !          1224:                        /* ok */
        !          1225:                        ret->force_command = strdup(primary->force_command);
        !          1226:                        if (ret->force_command == NULL)
        !          1227:                                goto alloc_fail;
        !          1228:                } else {
        !          1229:                        errstr = "forced command options do not match";
        !          1230:                        goto fail;
        !          1231:                }
        !          1232:        } else if (primary->force_command != NULL) {
        !          1233:                if ((ret->force_command = strdup(
        !          1234:                    primary->force_command)) == NULL)
        !          1235:                        goto alloc_fail;
        !          1236:        } else if (additional->force_command != NULL) {
        !          1237:                if ((ret->force_command = strdup(
        !          1238:                    additional->force_command)) == NULL)
        !          1239:                        goto alloc_fail;
        !          1240:        }
        !          1241:        /* success */
        !          1242:        if (errstrp != NULL)
        !          1243:                *errstrp = NULL;
        !          1244:        return ret;
        !          1245:
        !          1246:  alloc_fail:
        !          1247:        errstr = "memory allocation failed";
        !          1248:  fail:
        !          1249:        if (errstrp != NULL)
        !          1250:                *errstrp = errstr;
        !          1251:        sshauthopt_free(ret);
        !          1252:        return NULL;
        !          1253: }
        !          1254:
        !          1255: /*
        !          1256:  * Copy options
        !          1257:  */
        !          1258: struct sshauthopt *
        !          1259: sshauthopt_copy(const struct sshauthopt *orig)
        !          1260: {
        !          1261:        struct sshauthopt *ret;
        !          1262:
        !          1263:        if ((ret = sshauthopt_new()) == NULL)
        !          1264:                return NULL;
        !          1265:
        !          1266: #define OPTSCALAR(x) ret->x = orig->x
        !          1267:        OPTSCALAR(permit_port_forwarding_flag);
        !          1268:        OPTSCALAR(permit_agent_forwarding_flag);
        !          1269:        OPTSCALAR(permit_x11_forwarding_flag);
        !          1270:        OPTSCALAR(permit_pty_flag);
        !          1271:        OPTSCALAR(permit_user_rc);
        !          1272:        OPTSCALAR(restricted);
        !          1273:        OPTSCALAR(cert_authority);
        !          1274:        OPTSCALAR(force_tun_device);
        !          1275: #undef OPTSCALAR
        !          1276: #define OPTSTRING(x) \
        !          1277:        do { \
        !          1278:                if (orig->x != NULL && (ret->x = strdup(orig->x)) == NULL) { \
        !          1279:                        sshauthopt_free(ret); \
        !          1280:                        return NULL; \
        !          1281:                } \
        !          1282:        } while (0)
        !          1283:        OPTSTRING(cert_principals);
        !          1284:        OPTSTRING(force_command);
        !          1285:        OPTSTRING(required_from_host_cert);
        !          1286:        OPTSTRING(required_from_host_keys);
        !          1287: #undef OPTSTRING
        !          1288:
        !          1289:        if (dup_strings(&ret->env, &ret->nenv, orig->env, orig->nenv) != 0 ||
        !          1290:            dup_strings(&ret->permitopen, &ret->npermitopen,
        !          1291:            orig->permitopen, orig->npermitopen) != 0) {
        !          1292:                sshauthopt_free(ret);
        !          1293:                return NULL;
        !          1294:        }
        !          1295:        return ret;
        !          1296: }
        !          1297:
        !          1298: static int
        !          1299: serialise_array(struct sshbuf *m, char **a, size_t n)
        !          1300: {
        !          1301:        struct sshbuf *b;
        !          1302:        size_t i;
        !          1303:        int r;
        !          1304:
        !          1305:        if (n > INT_MAX)
        !          1306:                return SSH_ERR_INTERNAL_ERROR;
        !          1307:
        !          1308:        if ((b = sshbuf_new()) == NULL) {
        !          1309:                return SSH_ERR_ALLOC_FAIL;
        !          1310:        }
        !          1311:        for (i = 0; i < n; i++) {
        !          1312:                if ((r = sshbuf_put_cstring(b, a[i])) != 0) {
        !          1313:                        sshbuf_free(b);
        !          1314:                        return r;
        !          1315:                }
        !          1316:        }
        !          1317:        if ((r = sshbuf_put_u32(m, n)) != 0 ||
        !          1318:            (r = sshbuf_put_stringb(m, b)) != 0) {
        !          1319:                sshbuf_free(b);
        !          1320:                return r;
        !          1321:        }
        !          1322:        /* success */
        !          1323:        return 0;
        !          1324: }
        !          1325:
        !          1326: static int
        !          1327: deserialise_array(struct sshbuf *m, char ***ap, size_t *np)
        !          1328: {
        !          1329:        char **a = NULL;
        !          1330:        size_t i, n = 0;
        !          1331:        struct sshbuf *b = NULL;
        !          1332:        u_int tmp;
        !          1333:        int r = SSH_ERR_INTERNAL_ERROR;
        !          1334:
        !          1335:        if ((r = sshbuf_get_u32(m, &tmp)) != 0 ||
        !          1336:            (r = sshbuf_froms(m, &b)) != 0)
        !          1337:                goto out;
        !          1338:        if (tmp > INT_MAX) {
        !          1339:                r = SSH_ERR_INVALID_FORMAT;
        !          1340:                goto out;
        !          1341:        }
        !          1342:        n = tmp;
        !          1343:        if (n > 0 && (a = calloc(n, sizeof(*a))) == NULL) {
        !          1344:                r = SSH_ERR_ALLOC_FAIL;
        !          1345:                goto out;
        !          1346:        }
        !          1347:        for (i = 0; i < n; i++) {
        !          1348:                if ((r = sshbuf_get_cstring(b, &a[i], NULL)) != 0)
        !          1349:                        goto out;
        !          1350:        }
        !          1351:        /* success */
        !          1352:        r = 0;
        !          1353:        *ap = a;
        !          1354:        a = NULL;
        !          1355:        *np = n;
        !          1356:        n = 0;
        !          1357:  out:
        !          1358:        for (i = 0; i < n; i++)
        !          1359:                free(a[i]);
        !          1360:        free(a);
        !          1361:        sshbuf_free(b);
        !          1362:        return r;
        !          1363: }
        !          1364:
        !          1365: static int
        !          1366: serialise_nullable_string(struct sshbuf *m, const char *s)
        !          1367: {
        !          1368:        int r;
        !          1369:
        !          1370:        if ((r = sshbuf_put_u8(m, s == NULL)) != 0 ||
        !          1371:            (r = sshbuf_put_cstring(m, s)) != 0)
        !          1372:                return r;
        !          1373:        return 0;
        !          1374: }
        !          1375:
        !          1376: static int
        !          1377: deserialise_nullable_string(struct sshbuf *m, char **sp)
        !          1378: {
        !          1379:        int r;
        !          1380:        u_char flag;
        !          1381:
        !          1382:        *sp = NULL;
        !          1383:        if ((r = sshbuf_get_u8(m, &flag)) != 0 ||
        !          1384:            (r = sshbuf_get_cstring(m, flag ? NULL : sp, NULL)) != 0)
        !          1385:                return r;
        !          1386:        return 0;
        !          1387: }
        !          1388:
        !          1389: int
        !          1390: sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m,
        !          1391:     int untrusted)
        !          1392: {
        !          1393:        int r = SSH_ERR_INTERNAL_ERROR;
        !          1394:
        !          1395:        /* Flag options */
        !          1396:        if ((r = sshbuf_put_u8(m, opts->permit_port_forwarding_flag)) != 0 ||
        !          1397:            (r = sshbuf_put_u8(m, opts->permit_agent_forwarding_flag)) != 0 ||
        !          1398:            (r = sshbuf_put_u8(m, opts->permit_x11_forwarding_flag)) != 0 ||
        !          1399:            (r = sshbuf_put_u8(m, opts->permit_pty_flag)) != 0 ||
        !          1400:            (r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 ||
        !          1401:            (r = sshbuf_put_u8(m, opts->restricted)) != 0 ||
        !          1402:            (r = sshbuf_put_u8(m, opts->cert_authority)) != 0)
        !          1403:                return r;
        !          1404:
        !          1405:        /* tunnel number can be negative to indicate "unset" */
        !          1406:        if ((r = sshbuf_put_u8(m, opts->force_tun_device == -1)) != 0 ||
        !          1407:            (r = sshbuf_put_u32(m, (opts->force_tun_device < 0) ?
        !          1408:            0 : (u_int)opts->force_tun_device)) != 0)
        !          1409:                return r;
        !          1410:
        !          1411:        /* String options; these may be NULL */
        !          1412:        if ((r = serialise_nullable_string(m,
        !          1413:            untrusted ? "yes" : opts->cert_principals)) != 0 ||
        !          1414:            (r = serialise_nullable_string(m,
        !          1415:            untrusted ? "true" : opts->force_command)) != 0 ||
        !          1416:            (r = serialise_nullable_string(m,
        !          1417:            untrusted ? NULL : opts->required_from_host_cert)) != 0 ||
        !          1418:            (r = serialise_nullable_string(m,
        !          1419:             untrusted ? NULL : opts->required_from_host_keys)) != 0)
        !          1420:                return r;
        !          1421:
        !          1422:        /* Array options */
        !          1423:        if ((r = serialise_array(m, opts->env,
        !          1424:            untrusted ? 0 : opts->nenv)) != 0 ||
        !          1425:            (r = serialise_array(m, opts->permitopen,
        !          1426:            untrusted ? 0 : opts->npermitopen)) != 0)
        !          1427:                return r;
        !          1428:
        !          1429:        /* success */
        !          1430:        return 0;
        !          1431: }
        !          1432:
        !          1433: int
        !          1434: sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **optsp)
        !          1435: {
        !          1436:        struct sshauthopt *opts = NULL;
        !          1437:        int r = SSH_ERR_INTERNAL_ERROR;
        !          1438:        u_char f;
        !          1439:        u_int tmp;
        !          1440:
        !          1441:        if ((opts = calloc(1, sizeof(*opts))) == NULL)
        !          1442:                return SSH_ERR_ALLOC_FAIL;
        !          1443:
        !          1444: #define OPT_FLAG(x) \
        !          1445:        do { \
        !          1446:                if ((r = sshbuf_get_u8(m, &f)) != 0) \
        !          1447:                        goto out; \
        !          1448:                opts->x = f; \
        !          1449:        } while (0)
        !          1450:        OPT_FLAG(permit_port_forwarding_flag);
        !          1451:        OPT_FLAG(permit_agent_forwarding_flag);
        !          1452:        OPT_FLAG(permit_x11_forwarding_flag);
        !          1453:        OPT_FLAG(permit_pty_flag);
        !          1454:        OPT_FLAG(permit_user_rc);
        !          1455:        OPT_FLAG(restricted);
        !          1456:        OPT_FLAG(cert_authority);
        !          1457: #undef OPT_FLAG
        !          1458:
        !          1459:        /* tunnel number can be negative to indicate "unset" */
        !          1460:        if ((r = sshbuf_get_u8(m, &f)) != 0 ||
        !          1461:            (r = sshbuf_get_u32(m, &tmp)) != 0)
        !          1462:                goto out;
        !          1463:        opts->force_tun_device = f ? -1 : (int)tmp;
        !          1464:
        !          1465:        /* String options may be NULL */
        !          1466:        if ((r = deserialise_nullable_string(m, &opts->cert_principals)) != 0 ||
        !          1467:            (r = deserialise_nullable_string(m, &opts->force_command)) != 0 ||
        !          1468:            (r = deserialise_nullable_string(m,
        !          1469:            &opts->required_from_host_cert)) != 0 ||
        !          1470:            (r = deserialise_nullable_string(m,
        !          1471:            &opts->required_from_host_keys)) != 0)
        !          1472:                goto out;
        !          1473:
        !          1474:        /* Array options */
        !          1475:        if ((r = deserialise_array(m, &opts->env, &opts->nenv)) != 0 ||
        !          1476:            (r = deserialise_array(m,
        !          1477:            &opts->permitopen, &opts->npermitopen)) != 0)
        !          1478:                goto out;
        !          1479:
        !          1480:        /* success */
        !          1481:        r = 0;
        !          1482:        *optsp = opts;
        !          1483:        opts = NULL;
        !          1484:  out:
        !          1485:        sshauthopt_free(opts);
        !          1486:        return r;
        !          1487: }