diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/sunrpc/auth_gss/gss_rpc_upcall.c | 2 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/svcauth_gss.c | 347 | ||||
-rw-r--r-- | net/sunrpc/netns.h | 3 |
3 files changed, 343 insertions, 9 deletions
diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c index 2d33ddfe74e5..3f874d704859 100644 --- a/net/sunrpc/auth_gss/gss_rpc_upcall.c +++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c | |||
@@ -137,6 +137,7 @@ void init_gssp_clnt(struct sunrpc_net *sn) | |||
137 | { | 137 | { |
138 | mutex_init(&sn->gssp_lock); | 138 | mutex_init(&sn->gssp_lock); |
139 | sn->gssp_clnt = NULL; | 139 | sn->gssp_clnt = NULL; |
140 | init_waitqueue_head(&sn->gssp_wq); | ||
140 | } | 141 | } |
141 | 142 | ||
142 | int set_gssp_clnt(struct net *net) | 143 | int set_gssp_clnt(struct net *net) |
@@ -153,6 +154,7 @@ int set_gssp_clnt(struct net *net) | |||
153 | sn->gssp_clnt = clnt; | 154 | sn->gssp_clnt = clnt; |
154 | } | 155 | } |
155 | mutex_unlock(&sn->gssp_lock); | 156 | mutex_unlock(&sn->gssp_lock); |
157 | wake_up(&sn->gssp_wq); | ||
156 | return ret; | 158 | return ret; |
157 | } | 159 | } |
158 | 160 | ||
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 20eedecc35f8..58f5bc329408 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c | |||
@@ -48,8 +48,8 @@ | |||
48 | #include <linux/sunrpc/svcauth.h> | 48 | #include <linux/sunrpc/svcauth.h> |
49 | #include <linux/sunrpc/svcauth_gss.h> | 49 | #include <linux/sunrpc/svcauth_gss.h> |
50 | #include <linux/sunrpc/cache.h> | 50 | #include <linux/sunrpc/cache.h> |
51 | #include "gss_rpc_upcall.h" | ||
51 | 52 | ||
52 | #include "../netns.h" | ||
53 | 53 | ||
54 | #ifdef RPC_DEBUG | 54 | #ifdef RPC_DEBUG |
55 | # define RPCDBG_FACILITY RPCDBG_AUTH | 55 | # define RPCDBG_FACILITY RPCDBG_AUTH |
@@ -988,13 +988,10 @@ gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp, | |||
988 | } | 988 | } |
989 | 989 | ||
990 | static inline int | 990 | static inline int |
991 | gss_read_verf(struct rpc_gss_wire_cred *gc, | 991 | gss_read_common_verf(struct rpc_gss_wire_cred *gc, |
992 | struct kvec *argv, __be32 *authp, | 992 | struct kvec *argv, __be32 *authp, |
993 | struct xdr_netobj *in_handle, | 993 | struct xdr_netobj *in_handle) |
994 | struct xdr_netobj *in_token) | ||
995 | { | 994 | { |
996 | struct xdr_netobj tmpobj; | ||
997 | |||
998 | /* Read the verifier; should be NULL: */ | 995 | /* Read the verifier; should be NULL: */ |
999 | *authp = rpc_autherr_badverf; | 996 | *authp = rpc_autherr_badverf; |
1000 | if (argv->iov_len < 2 * 4) | 997 | if (argv->iov_len < 2 * 4) |
@@ -1010,6 +1007,23 @@ gss_read_verf(struct rpc_gss_wire_cred *gc, | |||
1010 | if (dup_netobj(in_handle, &gc->gc_ctx)) | 1007 | if (dup_netobj(in_handle, &gc->gc_ctx)) |
1011 | return SVC_CLOSE; | 1008 | return SVC_CLOSE; |
1012 | *authp = rpc_autherr_badverf; | 1009 | *authp = rpc_autherr_badverf; |
1010 | |||
1011 | return 0; | ||
1012 | } | ||
1013 | |||
1014 | static inline int | ||
1015 | gss_read_verf(struct rpc_gss_wire_cred *gc, | ||
1016 | struct kvec *argv, __be32 *authp, | ||
1017 | struct xdr_netobj *in_handle, | ||
1018 | struct xdr_netobj *in_token) | ||
1019 | { | ||
1020 | struct xdr_netobj tmpobj; | ||
1021 | int res; | ||
1022 | |||
1023 | res = gss_read_common_verf(gc, argv, authp, in_handle); | ||
1024 | if (res) | ||
1025 | return res; | ||
1026 | |||
1013 | if (svc_safe_getnetobj(argv, &tmpobj)) { | 1027 | if (svc_safe_getnetobj(argv, &tmpobj)) { |
1014 | kfree(in_handle->data); | 1028 | kfree(in_handle->data); |
1015 | return SVC_DENIED; | 1029 | return SVC_DENIED; |
@@ -1022,6 +1036,40 @@ gss_read_verf(struct rpc_gss_wire_cred *gc, | |||
1022 | return 0; | 1036 | return 0; |
1023 | } | 1037 | } |
1024 | 1038 | ||
1039 | /* Ok this is really heavily depending on a set of semantics in | ||
1040 | * how rqstp is set up by svc_recv and pages laid down by the | ||
1041 | * server when reading a request. We are basically guaranteed that | ||
1042 | * the token lays all down linearly across a set of pages, starting | ||
1043 | * at iov_base in rq_arg.head[0] which happens to be the first of a | ||
1044 | * set of pages stored in rq_pages[]. | ||
1045 | * rq_arg.head[0].iov_base will provide us the page_base to pass | ||
1046 | * to the upcall. | ||
1047 | */ | ||
1048 | static inline int | ||
1049 | gss_read_proxy_verf(struct svc_rqst *rqstp, | ||
1050 | struct rpc_gss_wire_cred *gc, __be32 *authp, | ||
1051 | struct xdr_netobj *in_handle, | ||
1052 | struct gssp_in_token *in_token) | ||
1053 | { | ||
1054 | struct kvec *argv = &rqstp->rq_arg.head[0]; | ||
1055 | u32 inlen; | ||
1056 | int res; | ||
1057 | |||
1058 | res = gss_read_common_verf(gc, argv, authp, in_handle); | ||
1059 | if (res) | ||
1060 | return res; | ||
1061 | |||
1062 | inlen = svc_getnl(argv); | ||
1063 | if (inlen > (argv->iov_len + rqstp->rq_arg.page_len)) | ||
1064 | return SVC_DENIED; | ||
1065 | |||
1066 | in_token->pages = rqstp->rq_pages; | ||
1067 | in_token->page_base = (ulong)argv->iov_base & ~PAGE_MASK; | ||
1068 | in_token->page_len = inlen; | ||
1069 | |||
1070 | return 0; | ||
1071 | } | ||
1072 | |||
1025 | static inline int | 1073 | static inline int |
1026 | gss_write_resv(struct kvec *resv, size_t size_limit, | 1074 | gss_write_resv(struct kvec *resv, size_t size_limit, |
1027 | struct xdr_netobj *out_handle, struct xdr_netobj *out_token, | 1075 | struct xdr_netobj *out_handle, struct xdr_netobj *out_token, |
@@ -1049,7 +1097,7 @@ gss_write_resv(struct kvec *resv, size_t size_limit, | |||
1049 | * the upcall results are available, write the verifier and result. | 1097 | * the upcall results are available, write the verifier and result. |
1050 | * Otherwise, drop the request pending an answer to the upcall. | 1098 | * Otherwise, drop the request pending an answer to the upcall. |
1051 | */ | 1099 | */ |
1052 | static int svcauth_gss_handle_init(struct svc_rqst *rqstp, | 1100 | static int svcauth_gss_legacy_init(struct svc_rqst *rqstp, |
1053 | struct rpc_gss_wire_cred *gc, __be32 *authp) | 1101 | struct rpc_gss_wire_cred *gc, __be32 *authp) |
1054 | { | 1102 | { |
1055 | struct kvec *argv = &rqstp->rq_arg.head[0]; | 1103 | struct kvec *argv = &rqstp->rq_arg.head[0]; |
@@ -1089,6 +1137,278 @@ out: | |||
1089 | return ret; | 1137 | return ret; |
1090 | } | 1138 | } |
1091 | 1139 | ||
1140 | static int gss_proxy_save_rsc(struct cache_detail *cd, | ||
1141 | struct gssp_upcall_data *ud, | ||
1142 | uint64_t *handle) | ||
1143 | { | ||
1144 | struct rsc rsci, *rscp = NULL; | ||
1145 | static atomic64_t ctxhctr; | ||
1146 | long long ctxh; | ||
1147 | struct gss_api_mech *gm = NULL; | ||
1148 | time_t expiry; | ||
1149 | int status = -EINVAL; | ||
1150 | |||
1151 | memset(&rsci, 0, sizeof(rsci)); | ||
1152 | /* context handle */ | ||
1153 | status = -ENOMEM; | ||
1154 | /* the handle needs to be just a unique id, | ||
1155 | * use a static counter */ | ||
1156 | ctxh = atomic64_inc_return(&ctxhctr); | ||
1157 | |||
1158 | /* make a copy for the caller */ | ||
1159 | *handle = ctxh; | ||
1160 | |||
1161 | /* make a copy for the rsc cache */ | ||
1162 | if (dup_to_netobj(&rsci.handle, (char *)handle, sizeof(uint64_t))) | ||
1163 | goto out; | ||
1164 | rscp = rsc_lookup(cd, &rsci); | ||
1165 | if (!rscp) | ||
1166 | goto out; | ||
1167 | |||
1168 | /* creds */ | ||
1169 | if (!ud->found_creds) { | ||
1170 | /* userspace seem buggy, we should always get at least a | ||
1171 | * mapping to nobody */ | ||
1172 | dprintk("RPC: No creds found, marking Negative!\n"); | ||
1173 | set_bit(CACHE_NEGATIVE, &rsci.h.flags); | ||
1174 | } else { | ||
1175 | |||
1176 | /* steal creds */ | ||
1177 | rsci.cred = ud->creds; | ||
1178 | memset(&ud->creds, 0, sizeof(struct svc_cred)); | ||
1179 | |||
1180 | status = -EOPNOTSUPP; | ||
1181 | /* get mech handle from OID */ | ||
1182 | gm = gss_mech_get_by_OID(&ud->mech_oid); | ||
1183 | if (!gm) | ||
1184 | goto out; | ||
1185 | |||
1186 | status = -EINVAL; | ||
1187 | /* mech-specific data: */ | ||
1188 | status = gss_import_sec_context(ud->out_handle.data, | ||
1189 | ud->out_handle.len, | ||
1190 | gm, &rsci.mechctx, | ||
1191 | &expiry, GFP_KERNEL); | ||
1192 | if (status) | ||
1193 | goto out; | ||
1194 | } | ||
1195 | |||
1196 | rsci.h.expiry_time = expiry; | ||
1197 | rscp = rsc_update(cd, &rsci, rscp); | ||
1198 | status = 0; | ||
1199 | out: | ||
1200 | gss_mech_put(gm); | ||
1201 | rsc_free(&rsci); | ||
1202 | if (rscp) | ||
1203 | cache_put(&rscp->h, cd); | ||
1204 | else | ||
1205 | status = -ENOMEM; | ||
1206 | return status; | ||
1207 | } | ||
1208 | |||
1209 | static int svcauth_gss_proxy_init(struct svc_rqst *rqstp, | ||
1210 | struct rpc_gss_wire_cred *gc, __be32 *authp) | ||
1211 | { | ||
1212 | struct kvec *resv = &rqstp->rq_res.head[0]; | ||
1213 | struct xdr_netobj cli_handle; | ||
1214 | struct gssp_upcall_data ud; | ||
1215 | uint64_t handle; | ||
1216 | int status; | ||
1217 | int ret; | ||
1218 | struct net *net = rqstp->rq_xprt->xpt_net; | ||
1219 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
1220 | |||
1221 | memset(&ud, 0, sizeof(ud)); | ||
1222 | ret = gss_read_proxy_verf(rqstp, gc, authp, | ||
1223 | &ud.in_handle, &ud.in_token); | ||
1224 | if (ret) | ||
1225 | return ret; | ||
1226 | |||
1227 | ret = SVC_CLOSE; | ||
1228 | |||
1229 | /* Perform synchronous upcall to gss-proxy */ | ||
1230 | status = gssp_accept_sec_context_upcall(net, &ud); | ||
1231 | if (status) | ||
1232 | goto out; | ||
1233 | |||
1234 | dprintk("RPC: svcauth_gss: gss major status = %d\n", | ||
1235 | ud.major_status); | ||
1236 | |||
1237 | switch (ud.major_status) { | ||
1238 | case GSS_S_CONTINUE_NEEDED: | ||
1239 | cli_handle = ud.out_handle; | ||
1240 | break; | ||
1241 | case GSS_S_COMPLETE: | ||
1242 | status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle); | ||
1243 | if (status) | ||
1244 | goto out; | ||
1245 | cli_handle.data = (u8 *)&handle; | ||
1246 | cli_handle.len = sizeof(handle); | ||
1247 | break; | ||
1248 | default: | ||
1249 | ret = SVC_CLOSE; | ||
1250 | goto out; | ||
1251 | } | ||
1252 | |||
1253 | /* Got an answer to the upcall; use it: */ | ||
1254 | if (gss_write_init_verf(sn->rsc_cache, rqstp, | ||
1255 | &cli_handle, &ud.major_status)) | ||
1256 | goto out; | ||
1257 | if (gss_write_resv(resv, PAGE_SIZE, | ||
1258 | &cli_handle, &ud.out_token, | ||
1259 | ud.major_status, ud.minor_status)) | ||
1260 | goto out; | ||
1261 | |||
1262 | ret = SVC_COMPLETE; | ||
1263 | out: | ||
1264 | gssp_free_upcall_data(&ud); | ||
1265 | return ret; | ||
1266 | } | ||
1267 | |||
1268 | DEFINE_SPINLOCK(use_gssp_lock); | ||
1269 | |||
1270 | static bool use_gss_proxy(struct net *net) | ||
1271 | { | ||
1272 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
1273 | |||
1274 | if (sn->use_gss_proxy != -1) | ||
1275 | return sn->use_gss_proxy; | ||
1276 | spin_lock(&use_gssp_lock); | ||
1277 | /* | ||
1278 | * If you wanted gss-proxy, you should have said so before | ||
1279 | * starting to accept requests: | ||
1280 | */ | ||
1281 | sn->use_gss_proxy = 0; | ||
1282 | spin_unlock(&use_gssp_lock); | ||
1283 | return 0; | ||
1284 | } | ||
1285 | |||
1286 | static bool set_gss_proxy(struct net *net, int type) | ||
1287 | { | ||
1288 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
1289 | int ret = 0; | ||
1290 | |||
1291 | WARN_ON_ONCE(type != 0 && type != 1); | ||
1292 | spin_lock(&use_gssp_lock); | ||
1293 | if (sn->use_gss_proxy == -1 || sn->use_gss_proxy == type) | ||
1294 | sn->use_gss_proxy = type; | ||
1295 | else | ||
1296 | ret = -EBUSY; | ||
1297 | spin_unlock(&use_gssp_lock); | ||
1298 | wake_up(&sn->gssp_wq); | ||
1299 | return ret; | ||
1300 | } | ||
1301 | |||
1302 | static inline bool gssp_ready(struct sunrpc_net *sn) | ||
1303 | { | ||
1304 | switch (sn->use_gss_proxy) { | ||
1305 | case -1: | ||
1306 | return false; | ||
1307 | case 0: | ||
1308 | return true; | ||
1309 | case 1: | ||
1310 | return sn->gssp_clnt; | ||
1311 | } | ||
1312 | WARN_ON_ONCE(1); | ||
1313 | return false; | ||
1314 | } | ||
1315 | |||
1316 | static int wait_for_gss_proxy(struct net *net) | ||
1317 | { | ||
1318 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
1319 | |||
1320 | return wait_event_interruptible(sn->gssp_wq, gssp_ready(sn)); | ||
1321 | } | ||
1322 | |||
1323 | #ifdef CONFIG_PROC_FS | ||
1324 | |||
1325 | static ssize_t write_gssp(struct file *file, const char __user *buf, | ||
1326 | size_t count, loff_t *ppos) | ||
1327 | { | ||
1328 | struct net *net = PDE(file->f_path.dentry->d_inode)->data; | ||
1329 | char tbuf[20]; | ||
1330 | unsigned long i; | ||
1331 | int res; | ||
1332 | |||
1333 | if (*ppos || count > sizeof(tbuf)-1) | ||
1334 | return -EINVAL; | ||
1335 | if (copy_from_user(tbuf, buf, count)) | ||
1336 | return -EFAULT; | ||
1337 | |||
1338 | tbuf[count] = 0; | ||
1339 | res = kstrtoul(tbuf, 0, &i); | ||
1340 | if (res) | ||
1341 | return res; | ||
1342 | if (i != 1) | ||
1343 | return -EINVAL; | ||
1344 | res = set_gss_proxy(net, 1); | ||
1345 | if (res) | ||
1346 | return res; | ||
1347 | res = set_gssp_clnt(net); | ||
1348 | if (res) | ||
1349 | return res; | ||
1350 | return count; | ||
1351 | } | ||
1352 | |||
1353 | static ssize_t read_gssp(struct file *file, char __user *buf, | ||
1354 | size_t count, loff_t *ppos) | ||
1355 | { | ||
1356 | struct net *net = PDE(file->f_path.dentry->d_inode)->data; | ||
1357 | unsigned long p = *ppos; | ||
1358 | char tbuf[10]; | ||
1359 | size_t len; | ||
1360 | int ret; | ||
1361 | |||
1362 | ret = wait_for_gss_proxy(net); | ||
1363 | if (ret) | ||
1364 | return ret; | ||
1365 | |||
1366 | snprintf(tbuf, sizeof(tbuf), "%d\n", use_gss_proxy(net)); | ||
1367 | len = strlen(tbuf); | ||
1368 | if (p >= len) | ||
1369 | return 0; | ||
1370 | len -= p; | ||
1371 | if (len > count) | ||
1372 | len = count; | ||
1373 | if (copy_to_user(buf, (void *)(tbuf+p), len)) | ||
1374 | return -EFAULT; | ||
1375 | *ppos += len; | ||
1376 | return len; | ||
1377 | } | ||
1378 | |||
1379 | static const struct file_operations use_gss_proxy_ops = { | ||
1380 | .open = nonseekable_open, | ||
1381 | .write = write_gssp, | ||
1382 | .read = read_gssp, | ||
1383 | }; | ||
1384 | |||
1385 | static int create_use_gss_proxy_proc_entry(struct net *net) | ||
1386 | { | ||
1387 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
1388 | struct proc_dir_entry **p = &sn->use_gssp_proc; | ||
1389 | |||
1390 | sn->use_gss_proxy = -1; | ||
1391 | *p = proc_create_data("use-gss-proxy", S_IFREG|S_IRUSR|S_IWUSR, | ||
1392 | sn->proc_net_rpc, | ||
1393 | &use_gss_proxy_ops, net); | ||
1394 | if (!*p) | ||
1395 | return -ENOMEM; | ||
1396 | init_gssp_clnt(sn); | ||
1397 | return 0; | ||
1398 | } | ||
1399 | |||
1400 | static void destroy_use_gss_proxy_proc_entry(struct net *net) | ||
1401 | { | ||
1402 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
1403 | |||
1404 | if (sn->use_gssp_proc) { | ||
1405 | remove_proc_entry("use-gss-proxy", sn->proc_net_rpc); | ||
1406 | clear_gssp_clnt(sn); | ||
1407 | } | ||
1408 | } | ||
1409 | |||
1410 | #endif /* CONFIG_PROC_FS */ | ||
1411 | |||
1092 | /* | 1412 | /* |
1093 | * Accept an rpcsec packet. | 1413 | * Accept an rpcsec packet. |
1094 | * If context establishment, punt to user space | 1414 | * If context establishment, punt to user space |
@@ -1155,7 +1475,10 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) | |||
1155 | switch (gc->gc_proc) { | 1475 | switch (gc->gc_proc) { |
1156 | case RPC_GSS_PROC_INIT: | 1476 | case RPC_GSS_PROC_INIT: |
1157 | case RPC_GSS_PROC_CONTINUE_INIT: | 1477 | case RPC_GSS_PROC_CONTINUE_INIT: |
1158 | return svcauth_gss_handle_init(rqstp, gc, authp); | 1478 | if (use_gss_proxy(SVC_NET(rqstp))) |
1479 | return svcauth_gss_proxy_init(rqstp, gc, authp); | ||
1480 | else | ||
1481 | return svcauth_gss_legacy_init(rqstp, gc, authp); | ||
1159 | case RPC_GSS_PROC_DATA: | 1482 | case RPC_GSS_PROC_DATA: |
1160 | case RPC_GSS_PROC_DESTROY: | 1483 | case RPC_GSS_PROC_DESTROY: |
1161 | /* Look up the context, and check the verifier: */ | 1484 | /* Look up the context, and check the verifier: */ |
@@ -1530,7 +1853,12 @@ gss_svc_init_net(struct net *net) | |||
1530 | rv = rsi_cache_create_net(net); | 1853 | rv = rsi_cache_create_net(net); |
1531 | if (rv) | 1854 | if (rv) |
1532 | goto out1; | 1855 | goto out1; |
1856 | rv = create_use_gss_proxy_proc_entry(net); | ||
1857 | if (rv) | ||
1858 | goto out2; | ||
1533 | return 0; | 1859 | return 0; |
1860 | out2: | ||
1861 | destroy_use_gss_proxy_proc_entry(net); | ||
1534 | out1: | 1862 | out1: |
1535 | rsc_cache_destroy_net(net); | 1863 | rsc_cache_destroy_net(net); |
1536 | return rv; | 1864 | return rv; |
@@ -1539,6 +1867,7 @@ out1: | |||
1539 | void | 1867 | void |
1540 | gss_svc_shutdown_net(struct net *net) | 1868 | gss_svc_shutdown_net(struct net *net) |
1541 | { | 1869 | { |
1870 | destroy_use_gss_proxy_proc_entry(net); | ||
1542 | rsi_cache_destroy_net(net); | 1871 | rsi_cache_destroy_net(net); |
1543 | rsc_cache_destroy_net(net); | 1872 | rsc_cache_destroy_net(net); |
1544 | } | 1873 | } |
diff --git a/net/sunrpc/netns.h b/net/sunrpc/netns.h index e9f8895d70ca..7111a4c9113b 100644 --- a/net/sunrpc/netns.h +++ b/net/sunrpc/netns.h | |||
@@ -25,7 +25,10 @@ struct sunrpc_net { | |||
25 | unsigned int rpcb_users; | 25 | unsigned int rpcb_users; |
26 | 26 | ||
27 | struct mutex gssp_lock; | 27 | struct mutex gssp_lock; |
28 | wait_queue_head_t gssp_wq; | ||
28 | struct rpc_clnt *gssp_clnt; | 29 | struct rpc_clnt *gssp_clnt; |
30 | int use_gss_proxy; | ||
31 | struct proc_dir_entry *use_gssp_proc; | ||
29 | }; | 32 | }; |
30 | 33 | ||
31 | extern int sunrpc_net_id; | 34 | extern int sunrpc_net_id; |