diff options
author | Weston Andros Adamson <dros@netapp.com> | 2013-10-18 15:15:19 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-10-28 15:38:02 -0400 |
commit | 4d4b69dd847a098cdca341c45326f6c6f61b8691 (patch) | |
tree | fdd1d7204d48f7af1f93f06844ee507e36dc917b /fs/nfs/super.c | |
parent | 5837f6dfcb00f764976ddc178933e612702cbf54 (diff) |
NFS: add support for multiple sec= mount options
This patch adds support for multiple security options which can be
specified using a colon-delimited list of security flavors (the same
syntax as nfsd's exports file).
This is useful, for instance, when NFSv4.x mounts cross SECINFO
boundaries. With this patch a user can use "sec=krb5i,krb5p"
to mount a remote filesystem using krb5i, but can still cross
into krb5p-only exports.
New mounts will try all security options before failing. NFSv4.x
SECINFO results will be compared against the sec= flavors to
find the first flavor in both lists or if no match is found will
return -EPERM.
Signed-off-by: Weston Andros Adamson <dros@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/super.c')
-rw-r--r-- | fs/nfs/super.c | 160 |
1 files changed, 105 insertions, 55 deletions
diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 3a4f8bf5e5a5..317d6fc2160e 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c | |||
@@ -497,7 +497,8 @@ static const char *nfs_pseudoflavour_to_name(rpc_authflavor_t flavour) | |||
497 | static const struct { | 497 | static const struct { |
498 | rpc_authflavor_t flavour; | 498 | rpc_authflavor_t flavour; |
499 | const char *str; | 499 | const char *str; |
500 | } sec_flavours[] = { | 500 | } sec_flavours[NFS_AUTH_INFO_MAX_FLAVORS] = { |
501 | /* update NFS_AUTH_INFO_MAX_FLAVORS when this list changes! */ | ||
501 | { RPC_AUTH_NULL, "null" }, | 502 | { RPC_AUTH_NULL, "null" }, |
502 | { RPC_AUTH_UNIX, "sys" }, | 503 | { RPC_AUTH_UNIX, "sys" }, |
503 | { RPC_AUTH_GSS_KRB5, "krb5" }, | 504 | { RPC_AUTH_GSS_KRB5, "krb5" }, |
@@ -1019,6 +1020,52 @@ static void nfs_set_mount_transport_protocol(struct nfs_parsed_mount_data *mnt) | |||
1019 | } | 1020 | } |
1020 | 1021 | ||
1021 | /* | 1022 | /* |
1023 | * Add 'flavor' to 'auth_info' if not already present. | ||
1024 | * Returns true if 'flavor' ends up in the list, false otherwise | ||
1025 | */ | ||
1026 | static bool nfs_auth_info_add(struct nfs_auth_info *auth_info, | ||
1027 | rpc_authflavor_t flavor) | ||
1028 | { | ||
1029 | unsigned int i; | ||
1030 | unsigned int max_flavor_len = (sizeof(auth_info->flavors) / | ||
1031 | sizeof(auth_info->flavors[0])); | ||
1032 | |||
1033 | /* make sure this flavor isn't already in the list */ | ||
1034 | for (i = 0; i < auth_info->flavor_len; i++) { | ||
1035 | if (flavor == auth_info->flavors[i]) | ||
1036 | return true; | ||
1037 | } | ||
1038 | |||
1039 | if (auth_info->flavor_len + 1 >= max_flavor_len) { | ||
1040 | dfprintk(MOUNT, "NFS: too many sec= flavors\n"); | ||
1041 | return false; | ||
1042 | } | ||
1043 | |||
1044 | auth_info->flavors[auth_info->flavor_len++] = flavor; | ||
1045 | return true; | ||
1046 | } | ||
1047 | |||
1048 | /* | ||
1049 | * Return true if 'match' is in auth_info or auth_info is empty. | ||
1050 | * Return false otherwise. | ||
1051 | */ | ||
1052 | bool nfs_auth_info_match(const struct nfs_auth_info *auth_info, | ||
1053 | rpc_authflavor_t match) | ||
1054 | { | ||
1055 | int i; | ||
1056 | |||
1057 | if (!auth_info->flavor_len) | ||
1058 | return true; | ||
1059 | |||
1060 | for (i = 0; i < auth_info->flavor_len; i++) { | ||
1061 | if (auth_info->flavors[i] == match) | ||
1062 | return true; | ||
1063 | } | ||
1064 | return false; | ||
1065 | } | ||
1066 | EXPORT_SYMBOL_GPL(nfs_auth_info_match); | ||
1067 | |||
1068 | /* | ||
1022 | * Parse the value of the 'sec=' option. | 1069 | * Parse the value of the 'sec=' option. |
1023 | */ | 1070 | */ |
1024 | static int nfs_parse_security_flavors(char *value, | 1071 | static int nfs_parse_security_flavors(char *value, |
@@ -1026,49 +1073,55 @@ static int nfs_parse_security_flavors(char *value, | |||
1026 | { | 1073 | { |
1027 | substring_t args[MAX_OPT_ARGS]; | 1074 | substring_t args[MAX_OPT_ARGS]; |
1028 | rpc_authflavor_t pseudoflavor; | 1075 | rpc_authflavor_t pseudoflavor; |
1076 | char *p; | ||
1029 | 1077 | ||
1030 | dfprintk(MOUNT, "NFS: parsing sec=%s option\n", value); | 1078 | dfprintk(MOUNT, "NFS: parsing sec=%s option\n", value); |
1031 | 1079 | ||
1032 | switch (match_token(value, nfs_secflavor_tokens, args)) { | 1080 | while ((p = strsep(&value, ":")) != NULL) { |
1033 | case Opt_sec_none: | 1081 | switch (match_token(p, nfs_secflavor_tokens, args)) { |
1034 | pseudoflavor = RPC_AUTH_NULL; | 1082 | case Opt_sec_none: |
1035 | break; | 1083 | pseudoflavor = RPC_AUTH_NULL; |
1036 | case Opt_sec_sys: | 1084 | break; |
1037 | pseudoflavor = RPC_AUTH_UNIX; | 1085 | case Opt_sec_sys: |
1038 | break; | 1086 | pseudoflavor = RPC_AUTH_UNIX; |
1039 | case Opt_sec_krb5: | 1087 | break; |
1040 | pseudoflavor = RPC_AUTH_GSS_KRB5; | 1088 | case Opt_sec_krb5: |
1041 | break; | 1089 | pseudoflavor = RPC_AUTH_GSS_KRB5; |
1042 | case Opt_sec_krb5i: | 1090 | break; |
1043 | pseudoflavor = RPC_AUTH_GSS_KRB5I; | 1091 | case Opt_sec_krb5i: |
1044 | break; | 1092 | pseudoflavor = RPC_AUTH_GSS_KRB5I; |
1045 | case Opt_sec_krb5p: | 1093 | break; |
1046 | pseudoflavor = RPC_AUTH_GSS_KRB5P; | 1094 | case Opt_sec_krb5p: |
1047 | break; | 1095 | pseudoflavor = RPC_AUTH_GSS_KRB5P; |
1048 | case Opt_sec_lkey: | 1096 | break; |
1049 | pseudoflavor = RPC_AUTH_GSS_LKEY; | 1097 | case Opt_sec_lkey: |
1050 | break; | 1098 | pseudoflavor = RPC_AUTH_GSS_LKEY; |
1051 | case Opt_sec_lkeyi: | 1099 | break; |
1052 | pseudoflavor = RPC_AUTH_GSS_LKEYI; | 1100 | case Opt_sec_lkeyi: |
1053 | break; | 1101 | pseudoflavor = RPC_AUTH_GSS_LKEYI; |
1054 | case Opt_sec_lkeyp: | 1102 | break; |
1055 | pseudoflavor = RPC_AUTH_GSS_LKEYP; | 1103 | case Opt_sec_lkeyp: |
1056 | break; | 1104 | pseudoflavor = RPC_AUTH_GSS_LKEYP; |
1057 | case Opt_sec_spkm: | 1105 | break; |
1058 | pseudoflavor = RPC_AUTH_GSS_SPKM; | 1106 | case Opt_sec_spkm: |
1059 | break; | 1107 | pseudoflavor = RPC_AUTH_GSS_SPKM; |
1060 | case Opt_sec_spkmi: | 1108 | break; |
1061 | pseudoflavor = RPC_AUTH_GSS_SPKMI; | 1109 | case Opt_sec_spkmi: |
1062 | break; | 1110 | pseudoflavor = RPC_AUTH_GSS_SPKMI; |
1063 | case Opt_sec_spkmp: | 1111 | break; |
1064 | pseudoflavor = RPC_AUTH_GSS_SPKMP; | 1112 | case Opt_sec_spkmp: |
1065 | break; | 1113 | pseudoflavor = RPC_AUTH_GSS_SPKMP; |
1066 | default: | 1114 | break; |
1067 | return 0; | 1115 | default: |
1116 | dfprintk(MOUNT, | ||
1117 | "NFS: sec= option '%s' not recognized\n", p); | ||
1118 | return 0; | ||
1119 | } | ||
1120 | |||
1121 | if (!nfs_auth_info_add(&mnt->auth_info, pseudoflavor)) | ||
1122 | return 0; | ||
1068 | } | 1123 | } |
1069 | 1124 | ||
1070 | mnt->auth_info.flavors[0] = pseudoflavor; | ||
1071 | mnt->auth_info.flavor_len = 1; | ||
1072 | return 1; | 1125 | return 1; |
1073 | } | 1126 | } |
1074 | 1127 | ||
@@ -1615,12 +1668,14 @@ out_security_failure: | |||
1615 | } | 1668 | } |
1616 | 1669 | ||
1617 | /* | 1670 | /* |
1618 | * Ensure that the specified authtype in args->auth_info is supported by | 1671 | * Ensure that a specified authtype in args->auth_info is supported by |
1619 | * the server. Returns 0 if it's ok, and -EACCES if not. | 1672 | * the server. Returns 0 and sets args->selected_flavor if it's ok, and |
1673 | * -EACCES if not. | ||
1620 | */ | 1674 | */ |
1621 | static int nfs_verify_authflavor(struct nfs_parsed_mount_data *args, | 1675 | static int nfs_verify_authflavors(struct nfs_parsed_mount_data *args, |
1622 | rpc_authflavor_t *server_authlist, unsigned int count) | 1676 | rpc_authflavor_t *server_authlist, unsigned int count) |
1623 | { | 1677 | { |
1678 | rpc_authflavor_t flavor = RPC_AUTH_MAXFLAVOR; | ||
1624 | unsigned int i; | 1679 | unsigned int i; |
1625 | 1680 | ||
1626 | /* | 1681 | /* |
@@ -1632,17 +1687,19 @@ static int nfs_verify_authflavor(struct nfs_parsed_mount_data *args, | |||
1632 | * can be used. | 1687 | * can be used. |
1633 | */ | 1688 | */ |
1634 | for (i = 0; i < count; i++) { | 1689 | for (i = 0; i < count; i++) { |
1635 | if (args->auth_info.flavors[0] == server_authlist[i] || | 1690 | flavor = server_authlist[i]; |
1636 | server_authlist[i] == RPC_AUTH_NULL) | 1691 | |
1692 | if (nfs_auth_info_match(&args->auth_info, flavor) || | ||
1693 | flavor == RPC_AUTH_NULL) | ||
1637 | goto out; | 1694 | goto out; |
1638 | } | 1695 | } |
1639 | 1696 | ||
1640 | dfprintk(MOUNT, "NFS: auth flavor %u not supported by server\n", | 1697 | dfprintk(MOUNT, |
1641 | args->auth_info.flavors[0]); | 1698 | "NFS: specified auth flavors not supported by server\n"); |
1642 | return -EACCES; | 1699 | return -EACCES; |
1643 | 1700 | ||
1644 | out: | 1701 | out: |
1645 | args->selected_flavor = args->auth_info.flavors[0]; | 1702 | args->selected_flavor = flavor; |
1646 | dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->selected_flavor); | 1703 | dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->selected_flavor); |
1647 | return 0; | 1704 | return 0; |
1648 | } | 1705 | } |
@@ -1732,7 +1789,7 @@ static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_inf | |||
1732 | * whether the server supports it, and then just try to use it if so. | 1789 | * whether the server supports it, and then just try to use it if so. |
1733 | */ | 1790 | */ |
1734 | if (args->auth_info.flavor_len > 0) { | 1791 | if (args->auth_info.flavor_len > 0) { |
1735 | status = nfs_verify_authflavor(args, authlist, authlist_len); | 1792 | status = nfs_verify_authflavors(args, authlist, authlist_len); |
1736 | dfprintk(MOUNT, "NFS: using auth flavor %u\n", | 1793 | dfprintk(MOUNT, "NFS: using auth flavor %u\n", |
1737 | args->selected_flavor); | 1794 | args->selected_flavor); |
1738 | if (status) | 1795 | if (status) |
@@ -2102,9 +2159,6 @@ static int nfs_validate_text_mount_data(void *options, | |||
2102 | 2159 | ||
2103 | nfs_set_port(sap, &args->nfs_server.port, port); | 2160 | nfs_set_port(sap, &args->nfs_server.port, port); |
2104 | 2161 | ||
2105 | if (args->auth_info.flavor_len > 1) | ||
2106 | goto out_bad_auth; | ||
2107 | |||
2108 | return nfs_parse_devname(dev_name, | 2162 | return nfs_parse_devname(dev_name, |
2109 | &args->nfs_server.hostname, | 2163 | &args->nfs_server.hostname, |
2110 | max_namelen, | 2164 | max_namelen, |
@@ -2124,10 +2178,6 @@ out_invalid_transport_udp: | |||
2124 | out_no_address: | 2178 | out_no_address: |
2125 | dfprintk(MOUNT, "NFS: mount program didn't pass remote address\n"); | 2179 | dfprintk(MOUNT, "NFS: mount program didn't pass remote address\n"); |
2126 | return -EINVAL; | 2180 | return -EINVAL; |
2127 | |||
2128 | out_bad_auth: | ||
2129 | dfprintk(MOUNT, "NFS: Too many RPC auth flavours specified\n"); | ||
2130 | return -EINVAL; | ||
2131 | } | 2181 | } |
2132 | 2182 | ||
2133 | static int | 2183 | static int |