aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2012-05-25 18:09:56 -0400
committerJ. Bruce Fields <bfields@redhat.com>2013-04-26 11:41:28 -0400
commit030d794bf49855f5e2a9e8dfbfad34211d1eb08b (patch)
treeb92b6fecf0856d48d232f266d7ac7e2b0ff09a17 /net
parent1d658336b05f8697d6445834f8867f8ad5e4f735 (diff)
SUNRPC: Use gssproxy upcall for server RPCGSS authentication.
The main advantge of this new upcall mechanism is that it can handle big tickets as seen in Kerberos implementations where tickets carry authorization data like the MS-PAC buffer with AD or the Posix Authorization Data being discussed in IETF on the krbwg working group. The Gssproxy program is used to perform the accept_sec_context call on the kernel's behalf. The code is changed to also pass the input buffer straight to upcall mechanism to avoid allocating and copying many pages as tokens can be as big (potentially more in future) as 64KiB. Signed-off-by: Simo Sorce <simo@redhat.com> [bfields: containerization, negotiation api] Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'net')
-rw-r--r--net/sunrpc/auth_gss/gss_rpc_upcall.c2
-rw-r--r--net/sunrpc/auth_gss/svcauth_gss.c347
-rw-r--r--net/sunrpc/netns.h3
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
142int set_gssp_clnt(struct net *net) 143int 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
990static inline int 990static inline int
991gss_read_verf(struct rpc_gss_wire_cred *gc, 991gss_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
1014static inline int
1015gss_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 */
1048static inline int
1049gss_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
1025static inline int 1073static inline int
1026gss_write_resv(struct kvec *resv, size_t size_limit, 1074gss_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 */
1052static int svcauth_gss_handle_init(struct svc_rqst *rqstp, 1100static 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
1140static 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;
1199out:
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
1209static 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;
1263out:
1264 gssp_free_upcall_data(&ud);
1265 return ret;
1266}
1267
1268DEFINE_SPINLOCK(use_gssp_lock);
1269
1270static 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
1286static 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
1302static 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
1316static 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
1325static 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
1353static 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
1379static const struct file_operations use_gss_proxy_ops = {
1380 .open = nonseekable_open,
1381 .write = write_gssp,
1382 .read = read_gssp,
1383};
1384
1385static 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
1400static 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;
1860out2:
1861 destroy_use_gss_proxy_proc_entry(net);
1534out1: 1862out1:
1535 rsc_cache_destroy_net(net); 1863 rsc_cache_destroy_net(net);
1536 return rv; 1864 return rv;
@@ -1539,6 +1867,7 @@ out1:
1539void 1867void
1540gss_svc_shutdown_net(struct net *net) 1868gss_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
31extern int sunrpc_net_id; 34extern int sunrpc_net_id;