summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2018-03-03 03:06:02 +0000
committerDamien Miller <djm@mindrot.org>2018-03-03 14:22:11 +1100
commit90c4bec8b5f9ec4c003ae4abdf13fc7766f00c8b (patch)
tree81333f6969082c66dd6261573f48644dd3203494
parent26074380767e639ef89321610e146ae11016b385 (diff)
upstream: Introduce a new API for handling authorized_keys options.
This API parses options to a dedicated structure rather than the old API's approach of setting global state. It also includes support for merging options, e.g. from authorized_keys, authorized_principals and/or certificates. feedback and ok markus@ OpenBSD-Commit-ID: 98badda102cd575210d7802943e93a34232c80a2
-rw-r--r--auth-options.c829
-rw-r--r--auth-options.h70
2 files changed, 897 insertions, 2 deletions
diff --git a/auth-options.c b/auth-options.c
index bed00eef0..8b93b51e5 100644
--- a/auth-options.c
+++ b/auth-options.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth-options.c,v 1.74 2017/09/12 06:32:07 djm Exp $ */ 1/* $OpenBSD: auth-options.c,v 1.75 2018/03/03 03:06:02 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -9,6 +9,21 @@
9 * incompatible with the protocol description in the RFC file, it must be 9 * incompatible with the protocol description in the RFC file, it must be
10 * called by a name other than "ssh" or "Secure Shell". 10 * called by a name other than "ssh" or "Secure Shell".
11 */ 11 */
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 */
12 27
13#include "includes.h" 28#include "includes.h"
14 29
@@ -19,6 +34,8 @@
19#include <string.h> 34#include <string.h>
20#include <stdio.h> 35#include <stdio.h>
21#include <stdarg.h> 36#include <stdarg.h>
37#include <ctype.h>
38#include <limits.h>
22 39
23#include "openbsd-compat/sys-queue.h" 40#include "openbsd-compat/sys-queue.h"
24 41
@@ -27,6 +44,7 @@
27#include "xmalloc.h" 44#include "xmalloc.h"
28#include "match.h" 45#include "match.h"
29#include "ssherr.h" 46#include "ssherr.h"
47#include "ssh2.h"
30#include "log.h" 48#include "log.h"
31#include "canohost.h" 49#include "canohost.h"
32#include "packet.h" 50#include "packet.h"
@@ -661,3 +679,812 @@ auth_cert_options(struct sshkey *k, struct passwd *pw, const char **reason)
661 return 0; 679 return 0;
662} 680}
663 681
682/*
683 * authorized_keys options processing.
684 */
685
686/*
687 * Match flag 'opt' in *optsp, and if allow_negate is set then also match
688 * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0
689 * if negated option matches.
690 * If the option or negated option matches, then *optsp is updated to
691 * point to the first character after the option.
692 */
693static int
694opt_flag(const char *opt, int allow_negate, const char **optsp)
695{
696 size_t opt_len = strlen(opt);
697 const char *opts = *optsp;
698 int negate = 0;
699
700 if (allow_negate && strncasecmp(opts, "no-", 3) == 0) {
701 opts += 3;
702 negate = 1;
703 }
704 if (strncasecmp(opts, opt, opt_len) == 0) {
705 *optsp = opts + opt_len;
706 return negate ? 0 : 1;
707 }
708 return -1;
709}
710
711static char *
712opt_dequote(const char **sp, const char **errstrp)
713{
714 const char *s = *sp;
715 char *ret;
716 size_t i;
717
718 *errstrp = NULL;
719 if (*s != '"') {
720 *errstrp = "missing start quote";
721 return NULL;
722 }
723 s++;
724 if ((ret = malloc(strlen((s)) + 1)) == NULL) {
725 *errstrp = "memory allocation failed";
726 return NULL;
727 }
728 for (i = 0; *s != '\0' && *s != '"';) {
729 if (s[0] == '\\' && s[1] == '"')
730 s++;
731 ret[i++] = *s++;
732 }
733 if (*s == '\0') {
734 *errstrp = "missing end quote";
735 free(ret);
736 return NULL;
737 }
738 ret[i] = '\0';
739 s++;
740 *sp = s;
741 return ret;
742}
743
744static int
745opt_match(const char **opts, const char *term)
746{
747 if (strncasecmp((*opts), term, strlen(term)) == 0 &&
748 (*opts)[strlen(term)] == '=') {
749 *opts += strlen(term) + 1;
750 return 1;
751 }
752 return 0;
753}
754
755static int
756dup_strings(char ***dstp, size_t *ndstp, char **src, size_t nsrc)
757{
758 char **dst;
759 size_t i, j;
760
761 *dstp = NULL;
762 *ndstp = 0;
763 if (nsrc == 0)
764 return 0;
765
766 if ((dst = calloc(nsrc, sizeof(*src))) == NULL)
767 return -1;
768 for (i = 0; i < nsrc; i++) {
769 if ((dst[i] = strdup(src[i])) == NULL) {
770 for (j = 0; j < i; j++)
771 free(dst[j]);
772 free(dst);
773 return -1;
774 }
775 }
776 /* success */
777 *dstp = dst;
778 *ndstp = nsrc;
779 return 0;
780}
781
782#define OPTIONS_CRITICAL 1
783#define OPTIONS_EXTENSIONS 2
784static int
785cert_option_list(struct sshauthopt *opts, struct sshbuf *oblob,
786 u_int which, int crit)
787{
788 char *command, *allowed;
789 char *name = NULL;
790 struct sshbuf *c = NULL, *data = NULL;
791 int r, ret = -1, found;
792
793 if ((c = sshbuf_fromb(oblob)) == NULL) {
794 error("%s: sshbuf_fromb failed", __func__);
795 goto out;
796 }
797
798 while (sshbuf_len(c) > 0) {
799 sshbuf_free(data);
800 data = NULL;
801 if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 ||
802 (r = sshbuf_froms(c, &data)) != 0) {
803 error("Unable to parse certificate options: %s",
804 ssh_err(r));
805 goto out;
806 }
807 debug3("found certificate option \"%.100s\" len %zu",
808 name, sshbuf_len(data));
809 found = 0;
810 if ((which & OPTIONS_EXTENSIONS) != 0) {
811 if (strcmp(name, "permit-X11-forwarding") == 0) {
812 opts->permit_x11_forwarding_flag = 1;
813 found = 1;
814 } else if (strcmp(name,
815 "permit-agent-forwarding") == 0) {
816 opts->permit_agent_forwarding_flag = 1;
817 found = 1;
818 } else if (strcmp(name,
819 "permit-port-forwarding") == 0) {
820 opts->permit_port_forwarding_flag = 1;
821 found = 1;
822 } else if (strcmp(name, "permit-pty") == 0) {
823 opts->permit_pty_flag = 1;
824 found = 1;
825 } else if (strcmp(name, "permit-user-rc") == 0) {
826 opts->permit_user_rc = 1;
827 found = 1;
828 }
829 }
830 if (!found && (which & OPTIONS_CRITICAL) != 0) {
831 if (strcmp(name, "force-command") == 0) {
832 if ((r = sshbuf_get_cstring(data, &command,
833 NULL)) != 0) {
834 error("Unable to parse \"%s\" "
835 "section: %s", name, ssh_err(r));
836 goto out;
837 }
838 if (opts->force_command != NULL) {
839 error("Certificate has multiple "
840 "force-command options");
841 free(command);
842 goto out;
843 }
844 opts->force_command = command;
845 found = 1;
846 }
847 if (strcmp(name, "source-address") == 0) {
848 if ((r = sshbuf_get_cstring(data, &allowed,
849 NULL)) != 0) {
850 error("Unable to parse \"%s\" "
851 "section: %s", name, ssh_err(r));
852 goto out;
853 }
854 if (opts->required_from_host_cert != NULL) {
855 error("Certificate has multiple "
856 "source-address options");
857 free(allowed);
858 goto out;
859 }
860 /* Check syntax */
861 if (addr_match_cidr_list(NULL, allowed) == -1) {
862 error("Certificate source-address "
863 "contents invalid");
864 goto out;
865 }
866 opts->required_from_host_cert = allowed;
867 found = 1;
868 }
869 }
870
871 if (!found) {
872 if (crit) {
873 error("Certificate critical option \"%s\" "
874 "is not supported", name);
875 goto out;
876 } else {
877 logit("Certificate extension \"%s\" "
878 "is not supported", name);
879 }
880 } else if (sshbuf_len(data) != 0) {
881 error("Certificate option \"%s\" corrupt "
882 "(extra data)", name);
883 goto out;
884 }
885 free(name);
886 name = NULL;
887 }
888 /* successfully parsed all options */
889 ret = 0;
890
891 out:
892 free(name);
893 sshbuf_free(data);
894 sshbuf_free(c);
895 return ret;
896}
897
898struct sshauthopt *
899sshauthopt_new(void)
900{
901 struct sshauthopt *ret;
902
903 if ((ret = calloc(1, sizeof(*ret))) == NULL)
904 return NULL;
905 ret->force_tun_device = -1;
906 return ret;
907}
908
909void
910sshauthopt_free(struct sshauthopt *opts)
911{
912 size_t i;
913
914 if (opts == NULL)
915 return;
916
917 free(opts->cert_principals);
918 free(opts->force_command);
919 free(opts->required_from_host_cert);
920 free(opts->required_from_host_keys);
921
922 for (i = 0; i < opts->nenv; i++)
923 free(opts->env[i]);
924 free(opts->env);
925
926 for (i = 0; i < opts->npermitopen; i++)
927 free(opts->permitopen[i]);
928 free(opts->permitopen);
929
930 explicit_bzero(opts, sizeof(*opts));
931 free(opts);
932}
933
934struct sshauthopt *
935sshauthopt_new_with_keys_defaults(void)
936{
937 struct sshauthopt *ret = NULL;
938
939 if ((ret = sshauthopt_new()) == NULL)
940 return NULL;
941
942 /* Defaults for authorized_keys flags */
943 ret->permit_port_forwarding_flag = 1;
944 ret->permit_agent_forwarding_flag = 1;
945 ret->permit_x11_forwarding_flag = 1;
946 ret->permit_pty_flag = 1;
947 ret->permit_user_rc = 1;
948 return ret;
949}
950
951struct sshauthopt *
952sshauthopt_parse(const char *opts, const char **errstrp)
953{
954 char **oarray, *opt, *cp, *tmp, *host;
955 int r;
956 struct sshauthopt *ret = NULL;
957 const char *errstr = "unknown error";
958
959 if (errstrp != NULL)
960 *errstrp = NULL;
961 if ((ret = sshauthopt_new_with_keys_defaults()) == NULL)
962 goto alloc_fail;
963
964 if (opts == NULL)
965 return ret;
966
967 while (*opts && *opts != ' ' && *opts != '\t') {
968 /* flag options */
969 if ((r = opt_flag("restrict", 0, &opts)) != -1) {
970 ret->restricted = 1;
971 ret->permit_port_forwarding_flag = 0;
972 ret->permit_agent_forwarding_flag = 0;
973 ret->permit_x11_forwarding_flag = 0;
974 ret->permit_pty_flag = 0;
975 ret->permit_user_rc = 0;
976 } else if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
977 ret->cert_authority = r;
978 } else if ((r = opt_flag("port-forwarding", 1, &opts)) != -1) {
979 ret->permit_port_forwarding_flag = r == 1;
980 } else if ((r = opt_flag("agent-forwarding", 1, &opts)) != -1) {
981 ret->permit_agent_forwarding_flag = r == 1;
982 } else if ((r = opt_flag("x11-forwarding", 1, &opts)) != -1) {
983 ret->permit_x11_forwarding_flag = r == 1;
984 } else if ((r = opt_flag("pty", 1, &opts)) != -1) {
985 ret->permit_pty_flag = r == 1;
986 } else if ((r = opt_flag("user-rc", 1, &opts)) != -1) {
987 ret->permit_user_rc = r == 1;
988 } else if (opt_match(&opts, "command")) {
989 if (ret->force_command != NULL) {
990 errstr = "multiple \"command\" clauses";
991 goto fail;
992 }
993 ret->force_command = opt_dequote(&opts, &errstr);
994 if (ret->force_command == NULL)
995 goto fail;
996 } else if (opt_match(&opts, "principals")) {
997 if (ret->cert_principals != NULL) {
998 errstr = "multiple \"principals\" clauses";
999 goto fail;
1000 }
1001 ret->cert_principals = opt_dequote(&opts, &errstr);
1002 if (ret->cert_principals == NULL)
1003 goto fail;
1004 } else if (opt_match(&opts, "from")) {
1005 if (ret->required_from_host_keys != NULL) {
1006 errstr = "multiple \"from\" clauses";
1007 goto fail;
1008 }
1009 ret->required_from_host_keys = opt_dequote(&opts,
1010 &errstr);
1011 if (ret->required_from_host_keys == NULL)
1012 goto fail;
1013 } else if (opt_match(&opts, "environment")) {
1014 if (ret->nenv > INT_MAX) {
1015 errstr = "too many environment strings";
1016 goto fail;
1017 }
1018 if ((opt = opt_dequote(&opts, &errstr)) == NULL)
1019 goto fail;
1020 /* env name must be alphanumeric and followed by '=' */
1021 if ((tmp = strchr(opt, '=')) == NULL) {
1022 free(opt);
1023 errstr = "invalid environment string";
1024 goto fail;
1025 }
1026 for (cp = opt; cp < tmp; cp++) {
1027 if (!isalnum((u_char)*cp)) {
1028 free(opt);
1029 errstr = "invalid environment string";
1030 goto fail;
1031 }
1032 }
1033 /* Append it. */
1034 oarray = ret->env;
1035 if ((ret->env = recallocarray(ret->env, ret->nenv,
1036 ret->nenv + 1, sizeof(*ret->env))) == NULL) {
1037 free(opt);
1038 ret->env = oarray; /* put it back for cleanup */
1039 goto alloc_fail;
1040 }
1041 ret->env[ret->nenv++] = opt;
1042 } else if (opt_match(&opts, "permitopen")) {
1043 if (ret->npermitopen > INT_MAX) {
1044 errstr = "too many permitopens";
1045 goto fail;
1046 }
1047 if ((opt = opt_dequote(&opts, &errstr)) == NULL)
1048 goto fail;
1049 if ((tmp = strdup(opt)) == NULL) {
1050 free(opt);
1051 goto alloc_fail;
1052 }
1053 cp = tmp;
1054 /* validate syntax of permitopen before recording it. */
1055 host = hpdelim(&cp);
1056 if (host == NULL || strlen(host) >= NI_MAXHOST) {
1057 free(tmp);
1058 free(opt);
1059 errstr = "invalid permitopen hostname";
1060 goto fail;
1061 }
1062 /*
1063 * don't want to use permitopen_port to avoid
1064 * dependency on channels.[ch] here.
1065 */
1066 if (cp == NULL ||
1067 (strcmp(cp, "*") != 0 && a2port(cp) <= 0)) {
1068 free(tmp);
1069 free(opt);
1070 errstr = "invalid permitopen port";
1071 goto fail;
1072 }
1073 /* XXX - add streamlocal support */
1074 free(tmp);
1075 /* Record it */
1076 oarray = ret->permitopen;
1077 if ((ret->permitopen = recallocarray(ret->permitopen,
1078 ret->npermitopen, ret->npermitopen + 1,
1079 sizeof(*ret->permitopen))) == NULL) {
1080 free(opt);
1081 ret->permitopen = oarray;
1082 goto alloc_fail;
1083 }
1084 ret->permitopen[ret->npermitopen++] = opt;
1085 } else if (opt_match(&opts, "tunnel")) {
1086 if ((opt = opt_dequote(&opts, &errstr)) == NULL)
1087 goto fail;
1088 ret->force_tun_device = a2tun(opt, NULL);
1089 free(opt);
1090 if (ret->force_tun_device == SSH_TUNID_ERR) {
1091 errstr = "invalid tun device";
1092 goto fail;
1093 }
1094 }
1095 /*
1096 * Skip the comma, and move to the next option
1097 * (or break out if there are no more).
1098 */
1099 if (*opts == '\0' || *opts == ' ' || *opts == '\t')
1100 break; /* End of options. */
1101 /* Anything other than a comma is an unknown option */
1102 if (*opts != ',') {
1103 errstr = "unknown key option";
1104 goto fail;
1105 }
1106 opts++;
1107 if (*opts == '\0') {
1108 errstr = "unexpected end-of-options";
1109 goto fail;
1110 }
1111 }
1112
1113 /* success */
1114 if (errstrp != NULL)
1115 *errstrp = NULL;
1116 return ret;
1117
1118alloc_fail:
1119 errstr = "memory allocation failed";
1120fail:
1121 sshauthopt_free(ret);
1122 if (errstrp != NULL)
1123 *errstrp = errstr;
1124 return NULL;
1125}
1126
1127struct sshauthopt *
1128sshauthopt_from_cert(struct sshkey *k)
1129{
1130 struct sshauthopt *ret;
1131
1132 if (k == NULL || !sshkey_type_is_cert(k->type) || k->cert == NULL ||
1133 k->cert->type != SSH2_CERT_TYPE_USER)
1134 return NULL;
1135
1136 if ((ret = sshauthopt_new()) == NULL)
1137 return NULL;
1138
1139 /* Handle options and critical extensions separately */
1140 if (cert_option_list(ret, k->cert->critical,
1141 OPTIONS_CRITICAL, 1) == -1) {
1142 sshauthopt_free(ret);
1143 return NULL;
1144 }
1145 if (cert_option_list(ret, k->cert->extensions,
1146 OPTIONS_EXTENSIONS, 0) == -1) {
1147 sshauthopt_free(ret);
1148 return NULL;
1149 }
1150 /* success */
1151 return ret;
1152}
1153
1154/*
1155 * Merges "additional" options to "primary" and returns the result.
1156 * NB. Some options from primary have primacy.
1157 */
1158struct sshauthopt *
1159sshauthopt_merge(const struct sshauthopt *primary,
1160 const struct sshauthopt *additional, const char **errstrp)
1161{
1162 struct sshauthopt *ret;
1163 const char *errstr = "internal error";
1164 const char *tmp;
1165
1166 if (errstrp != NULL)
1167 *errstrp = NULL;
1168
1169 if ((ret = sshauthopt_new()) == NULL)
1170 goto alloc_fail;
1171
1172 /* cert_authority and cert_principals are cleared in result */
1173
1174 /* Prefer access lists from primary. */
1175 /* XXX err is both set and mismatch? */
1176 tmp = primary->required_from_host_cert;
1177 if (tmp == NULL)
1178 tmp = additional->required_from_host_cert;
1179 if (tmp != NULL && (ret->required_from_host_cert = strdup(tmp)) == NULL)
1180 goto alloc_fail;
1181 tmp = primary->required_from_host_keys;
1182 if (tmp == NULL)
1183 tmp = additional->required_from_host_keys;
1184 if (tmp != NULL && (ret->required_from_host_keys = strdup(tmp)) == NULL)
1185 goto alloc_fail;
1186
1187 /* force_tun_device, permitopen and environment prefer the primary. */
1188 ret->force_tun_device = primary->force_tun_device;
1189 if (ret->force_tun_device == -1)
1190 ret->force_tun_device = additional->force_tun_device;
1191 if (primary->nenv > 0) {
1192 if (dup_strings(&ret->env, &ret->nenv,
1193 primary->env, primary->nenv) != 0)
1194 goto alloc_fail;
1195 } else if (additional->nenv) {
1196 if (dup_strings(&ret->env, &ret->nenv,
1197 additional->env, additional->nenv) != 0)
1198 goto alloc_fail;
1199 }
1200 if (primary->npermitopen > 0) {
1201 if (dup_strings(&ret->permitopen, &ret->npermitopen,
1202 primary->permitopen, primary->npermitopen) != 0)
1203 goto alloc_fail;
1204 } else if (additional->npermitopen > 0) {
1205 if (dup_strings(&ret->permitopen, &ret->npermitopen,
1206 additional->permitopen, additional->npermitopen) != 0)
1207 goto alloc_fail;
1208 }
1209
1210 /* Flags are logical-AND (i.e. must be set in both for permission) */
1211#define OPTFLAG(x) ret->x = (primary->x == 1) && (additional->x == 1)
1212 OPTFLAG(permit_port_forwarding_flag);
1213 OPTFLAG(permit_agent_forwarding_flag);
1214 OPTFLAG(permit_x11_forwarding_flag);
1215 OPTFLAG(permit_pty_flag);
1216 OPTFLAG(permit_user_rc);
1217#undef OPTFLAG
1218
1219 /*
1220 * When both multiple forced-command are specified, only
1221 * proceed if they are identical, otherwise fail.
1222 */
1223 if (primary->force_command != NULL &&
1224 additional->force_command != NULL) {
1225 if (strcmp(primary->force_command,
1226 additional->force_command) == 0) {
1227 /* ok */
1228 ret->force_command = strdup(primary->force_command);
1229 if (ret->force_command == NULL)
1230 goto alloc_fail;
1231 } else {
1232 errstr = "forced command options do not match";
1233 goto fail;
1234 }
1235 } else if (primary->force_command != NULL) {
1236 if ((ret->force_command = strdup(
1237 primary->force_command)) == NULL)
1238 goto alloc_fail;
1239 } else if (additional->force_command != NULL) {
1240 if ((ret->force_command = strdup(
1241 additional->force_command)) == NULL)
1242 goto alloc_fail;
1243 }
1244 /* success */
1245 if (errstrp != NULL)
1246 *errstrp = NULL;
1247 return ret;
1248
1249 alloc_fail:
1250 errstr = "memory allocation failed";
1251 fail:
1252 if (errstrp != NULL)
1253 *errstrp = errstr;
1254 sshauthopt_free(ret);
1255 return NULL;
1256}
1257
1258/*
1259 * Copy options
1260 */
1261struct sshauthopt *
1262sshauthopt_copy(const struct sshauthopt *orig)
1263{
1264 struct sshauthopt *ret;
1265
1266 if ((ret = sshauthopt_new()) == NULL)
1267 return NULL;
1268
1269#define OPTSCALAR(x) ret->x = orig->x
1270 OPTSCALAR(permit_port_forwarding_flag);
1271 OPTSCALAR(permit_agent_forwarding_flag);
1272 OPTSCALAR(permit_x11_forwarding_flag);
1273 OPTSCALAR(permit_pty_flag);
1274 OPTSCALAR(permit_user_rc);
1275 OPTSCALAR(restricted);
1276 OPTSCALAR(cert_authority);
1277 OPTSCALAR(force_tun_device);
1278#undef OPTSCALAR
1279#define OPTSTRING(x) \
1280 do { \
1281 if (orig->x != NULL && (ret->x = strdup(orig->x)) == NULL) { \
1282 sshauthopt_free(ret); \
1283 return NULL; \
1284 } \
1285 } while (0)
1286 OPTSTRING(cert_principals);
1287 OPTSTRING(force_command);
1288 OPTSTRING(required_from_host_cert);
1289 OPTSTRING(required_from_host_keys);
1290#undef OPTSTRING
1291
1292 if (dup_strings(&ret->env, &ret->nenv, orig->env, orig->nenv) != 0 ||
1293 dup_strings(&ret->permitopen, &ret->npermitopen,
1294 orig->permitopen, orig->npermitopen) != 0) {
1295 sshauthopt_free(ret);
1296 return NULL;
1297 }
1298 return ret;
1299}
1300
1301static int
1302serialise_array(struct sshbuf *m, char **a, size_t n)
1303{
1304 struct sshbuf *b;
1305 size_t i;
1306 int r;
1307
1308 if (n > INT_MAX)
1309 return SSH_ERR_INTERNAL_ERROR;
1310
1311 if ((b = sshbuf_new()) == NULL) {
1312 return SSH_ERR_ALLOC_FAIL;
1313 }
1314 for (i = 0; i < n; i++) {
1315 if ((r = sshbuf_put_cstring(b, a[i])) != 0) {
1316 sshbuf_free(b);
1317 return r;
1318 }
1319 }
1320 if ((r = sshbuf_put_u32(m, n)) != 0 ||
1321 (r = sshbuf_put_stringb(m, b)) != 0) {
1322 sshbuf_free(b);
1323 return r;
1324 }
1325 /* success */
1326 return 0;
1327}
1328
1329static int
1330deserialise_array(struct sshbuf *m, char ***ap, size_t *np)
1331{
1332 char **a = NULL;
1333 size_t i, n = 0;
1334 struct sshbuf *b = NULL;
1335 u_int tmp;
1336 int r = SSH_ERR_INTERNAL_ERROR;
1337
1338 if ((r = sshbuf_get_u32(m, &tmp)) != 0 ||
1339 (r = sshbuf_froms(m, &b)) != 0)
1340 goto out;
1341 if (tmp > INT_MAX) {
1342 r = SSH_ERR_INVALID_FORMAT;
1343 goto out;
1344 }
1345 n = tmp;
1346 if (n > 0 && (a = calloc(n, sizeof(*a))) == NULL) {
1347 r = SSH_ERR_ALLOC_FAIL;
1348 goto out;
1349 }
1350 for (i = 0; i < n; i++) {
1351 if ((r = sshbuf_get_cstring(b, &a[i], NULL)) != 0)
1352 goto out;
1353 }
1354 /* success */
1355 r = 0;
1356 *ap = a;
1357 a = NULL;
1358 *np = n;
1359 n = 0;
1360 out:
1361 for (i = 0; i < n; i++)
1362 free(a[i]);
1363 free(a);
1364 sshbuf_free(b);
1365 return r;
1366}
1367
1368static int
1369serialise_nullable_string(struct sshbuf *m, const char *s)
1370{
1371 int r;
1372
1373 if ((r = sshbuf_put_u8(m, s == NULL)) != 0 ||
1374 (r = sshbuf_put_cstring(m, s)) != 0)
1375 return r;
1376 return 0;
1377}
1378
1379static int
1380deserialise_nullable_string(struct sshbuf *m, char **sp)
1381{
1382 int r;
1383 u_char flag;
1384
1385 *sp = NULL;
1386 if ((r = sshbuf_get_u8(m, &flag)) != 0 ||
1387 (r = sshbuf_get_cstring(m, flag ? NULL : sp, NULL)) != 0)
1388 return r;
1389 return 0;
1390}
1391
1392int
1393sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m,
1394 int untrusted)
1395{
1396 int r = SSH_ERR_INTERNAL_ERROR;
1397
1398 /* Flag options */
1399 if ((r = sshbuf_put_u8(m, opts->permit_port_forwarding_flag)) != 0 ||
1400 (r = sshbuf_put_u8(m, opts->permit_agent_forwarding_flag)) != 0 ||
1401 (r = sshbuf_put_u8(m, opts->permit_x11_forwarding_flag)) != 0 ||
1402 (r = sshbuf_put_u8(m, opts->permit_pty_flag)) != 0 ||
1403 (r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 ||
1404 (r = sshbuf_put_u8(m, opts->restricted)) != 0 ||
1405 (r = sshbuf_put_u8(m, opts->cert_authority)) != 0)
1406 return r;
1407
1408 /* tunnel number can be negative to indicate "unset" */
1409 if ((r = sshbuf_put_u8(m, opts->force_tun_device == -1)) != 0 ||
1410 (r = sshbuf_put_u32(m, (opts->force_tun_device < 0) ?
1411 0 : (u_int)opts->force_tun_device)) != 0)
1412 return r;
1413
1414 /* String options; these may be NULL */
1415 if ((r = serialise_nullable_string(m,
1416 untrusted ? "yes" : opts->cert_principals)) != 0 ||
1417 (r = serialise_nullable_string(m,
1418 untrusted ? "true" : opts->force_command)) != 0 ||
1419 (r = serialise_nullable_string(m,
1420 untrusted ? NULL : opts->required_from_host_cert)) != 0 ||
1421 (r = serialise_nullable_string(m,
1422 untrusted ? NULL : opts->required_from_host_keys)) != 0)
1423 return r;
1424
1425 /* Array options */
1426 if ((r = serialise_array(m, opts->env,
1427 untrusted ? 0 : opts->nenv)) != 0 ||
1428 (r = serialise_array(m, opts->permitopen,
1429 untrusted ? 0 : opts->npermitopen)) != 0)
1430 return r;
1431
1432 /* success */
1433 return 0;
1434}
1435
1436int
1437sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **optsp)
1438{
1439 struct sshauthopt *opts = NULL;
1440 int r = SSH_ERR_INTERNAL_ERROR;
1441 u_char f;
1442 u_int tmp;
1443
1444 if ((opts = calloc(1, sizeof(*opts))) == NULL)
1445 return SSH_ERR_ALLOC_FAIL;
1446
1447#define OPT_FLAG(x) \
1448 do { \
1449 if ((r = sshbuf_get_u8(m, &f)) != 0) \
1450 goto out; \
1451 opts->x = f; \
1452 } while (0)
1453 OPT_FLAG(permit_port_forwarding_flag);
1454 OPT_FLAG(permit_agent_forwarding_flag);
1455 OPT_FLAG(permit_x11_forwarding_flag);
1456 OPT_FLAG(permit_pty_flag);
1457 OPT_FLAG(permit_user_rc);
1458 OPT_FLAG(restricted);
1459 OPT_FLAG(cert_authority);
1460#undef OPT_FLAG
1461
1462 /* tunnel number can be negative to indicate "unset" */
1463 if ((r = sshbuf_get_u8(m, &f)) != 0 ||
1464 (r = sshbuf_get_u32(m, &tmp)) != 0)
1465 goto out;
1466 opts->force_tun_device = f ? -1 : (int)tmp;
1467
1468 /* String options may be NULL */
1469 if ((r = deserialise_nullable_string(m, &opts->cert_principals)) != 0 ||
1470 (r = deserialise_nullable_string(m, &opts->force_command)) != 0 ||
1471 (r = deserialise_nullable_string(m,
1472 &opts->required_from_host_cert)) != 0 ||
1473 (r = deserialise_nullable_string(m,
1474 &opts->required_from_host_keys)) != 0)
1475 goto out;
1476
1477 /* Array options */
1478 if ((r = deserialise_array(m, &opts->env, &opts->nenv)) != 0 ||
1479 (r = deserialise_array(m,
1480 &opts->permitopen, &opts->npermitopen)) != 0)
1481 goto out;
1482
1483 /* success */
1484 r = 0;
1485 *optsp = opts;
1486 opts = NULL;
1487 out:
1488 sshauthopt_free(opts);
1489 return r;
1490}
diff --git a/auth-options.h b/auth-options.h
index 547f01635..0dbfc325e 100644
--- a/auth-options.h
+++ b/auth-options.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth-options.h,v 1.23 2017/05/31 10:54:00 markus Exp $ */ 1/* $OpenBSD: auth-options.h,v 1.24 2018/03/03 03:06:02 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -15,6 +15,9 @@
15#ifndef AUTH_OPTIONS_H 15#ifndef AUTH_OPTIONS_H
16#define AUTH_OPTIONS_H 16#define AUTH_OPTIONS_H
17 17
18struct passwd;
19struct sshkey;
20
18/* Linked list of custom environment strings */ 21/* Linked list of custom environment strings */
19struct envstring { 22struct envstring {
20 struct envstring *next; 23 struct envstring *next;
@@ -37,4 +40,69 @@ int auth_parse_options(struct passwd *, char *, const char *, u_long);
37void auth_clear_options(void); 40void auth_clear_options(void);
38int auth_cert_options(struct sshkey *, struct passwd *, const char **); 41int auth_cert_options(struct sshkey *, struct passwd *, const char **);
39 42
43/* authorized_keys options handling */
44
45/*
46 * sshauthopt represents key options parsed from authorized_keys or
47 * from certificate extensions/options.
48 */
49struct sshauthopt {
50 /* Feature flags */
51 int permit_port_forwarding_flag;
52 int permit_agent_forwarding_flag;
53 int permit_x11_forwarding_flag;
54 int permit_pty_flag;
55 int permit_user_rc;
56
57 /* "restrict" keyword was invoked */
58 int restricted;
59
60 /* Certificate-related options */
61 int cert_authority;
62 char *cert_principals;
63
64 int force_tun_device;
65 char *force_command;
66
67 /* Custom environment */
68 size_t nenv;
69 char **env;
70
71 /* Permitted port forwardings */
72 size_t npermitopen;
73 char **permitopen;
74
75 /*
76 * Permitted host/addresses (comma-separated)
77 * Caller must check source address matches both lists (if present).
78 */
79 char *required_from_host_cert;
80 char *required_from_host_keys;
81};
82
83struct sshauthopt *sshauthopt_new(void);
84struct sshauthopt *sshauthopt_new_with_keys_defaults(void);
85void sshauthopt_free(struct sshauthopt *opts);
86struct sshauthopt *sshauthopt_copy(const struct sshauthopt *orig);
87int sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m, int);
88int sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **opts);
89
90/*
91 * Parse authorized_keys options. Returns an options structure on success
92 * or NULL on failure. Will set errstr on failure.
93 */
94struct sshauthopt *sshauthopt_parse(const char *s, const char **errstr);
95
96/*
97 * Parse certification options to a struct sshauthopt.
98 * Returns options on success or NULL on failure.
99 */
100struct sshauthopt *sshauthopt_from_cert(struct sshkey *k);
101
102/*
103 * Merge key options.
104 */
105struct sshauthopt *sshauthopt_merge(const struct sshauthopt *primary,
106 const struct sshauthopt *additional, const char **errstrp);
107
40#endif 108#endif