1dfdcada3SDoug Rabson /* $NetBSD: auth_unix.c,v 1.18 2000/07/06 03:03:30 christos Exp $ */ 2dfdcada3SDoug Rabson 3dfdcada3SDoug Rabson /* 4dfdcada3SDoug Rabson * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 5dfdcada3SDoug Rabson * unrestricted use provided that this legend is included on all tape 6dfdcada3SDoug Rabson * media and as a part of the software program in whole or part. Users 7dfdcada3SDoug Rabson * may copy or modify Sun RPC without charge, but are not authorized 8dfdcada3SDoug Rabson * to license or distribute it to anyone else except as part of a product or 9dfdcada3SDoug Rabson * program developed by the user. 10dfdcada3SDoug Rabson * 11dfdcada3SDoug Rabson * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 12dfdcada3SDoug Rabson * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 13dfdcada3SDoug Rabson * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 14dfdcada3SDoug Rabson * 15dfdcada3SDoug Rabson * Sun RPC is provided with no support and without any obligation on the 16dfdcada3SDoug Rabson * part of Sun Microsystems, Inc. to assist in its use, correction, 17dfdcada3SDoug Rabson * modification or enhancement. 18dfdcada3SDoug Rabson * 19dfdcada3SDoug Rabson * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 20dfdcada3SDoug Rabson * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 21dfdcada3SDoug Rabson * OR ANY PART THEREOF. 22dfdcada3SDoug Rabson * 23dfdcada3SDoug Rabson * In no event will Sun Microsystems, Inc. be liable for any lost revenue 24dfdcada3SDoug Rabson * or profits or other special, indirect and consequential damages, even if 25dfdcada3SDoug Rabson * Sun has been advised of the possibility of such damages. 26dfdcada3SDoug Rabson * 27dfdcada3SDoug Rabson * Sun Microsystems, Inc. 28dfdcada3SDoug Rabson * 2550 Garcia Avenue 29dfdcada3SDoug Rabson * Mountain View, California 94043 30dfdcada3SDoug Rabson */ 31dfdcada3SDoug Rabson 32dfdcada3SDoug Rabson #if defined(LIBC_SCCS) && !defined(lint) 33dfdcada3SDoug Rabson static char *sccsid2 = "@(#)auth_unix.c 1.19 87/08/11 Copyr 1984 Sun Micro"; 34dfdcada3SDoug Rabson static char *sccsid = "@(#)auth_unix.c 2.2 88/08/01 4.0 RPCSRC"; 35dfdcada3SDoug Rabson #endif 36dfdcada3SDoug Rabson #include <sys/cdefs.h> 37dfdcada3SDoug Rabson __FBSDID("$FreeBSD$"); 38dfdcada3SDoug Rabson 39dfdcada3SDoug Rabson /* 40dfdcada3SDoug Rabson * auth_unix.c, Implements UNIX style authentication parameters. 41dfdcada3SDoug Rabson * 42dfdcada3SDoug Rabson * Copyright (C) 1984, Sun Microsystems, Inc. 43dfdcada3SDoug Rabson * 44dfdcada3SDoug Rabson * The system is very weak. The client uses no encryption for it's 45dfdcada3SDoug Rabson * credentials and only sends null verifiers. The server sends backs 46dfdcada3SDoug Rabson * null verifiers or optionally a verifier that suggests a new short hand 47dfdcada3SDoug Rabson * for the credentials. 48dfdcada3SDoug Rabson * 49dfdcada3SDoug Rabson */ 50dfdcada3SDoug Rabson 51dfdcada3SDoug Rabson #include <sys/param.h> 52dfdcada3SDoug Rabson #include <sys/systm.h> 53c675522fSDoug Rabson #include <sys/hash.h> 54c675522fSDoug Rabson #include <sys/kernel.h> 55dfdcada3SDoug Rabson #include <sys/lock.h> 56dfdcada3SDoug Rabson #include <sys/malloc.h> 57c675522fSDoug Rabson #include <sys/sx.h> 58dfdcada3SDoug Rabson #include <sys/ucred.h> 59dfdcada3SDoug Rabson 60dfdcada3SDoug Rabson #include <rpc/types.h> 61dfdcada3SDoug Rabson #include <rpc/xdr.h> 62dfdcada3SDoug Rabson #include <rpc/auth.h> 63dfdcada3SDoug Rabson 64ee31b83aSDoug Rabson #include <rpc/rpc_com.h> 65dfdcada3SDoug Rabson 66dfdcada3SDoug Rabson /* auth_unix.c */ 67dfdcada3SDoug Rabson static void authunix_nextverf (AUTH *); 68dfdcada3SDoug Rabson static bool_t authunix_marshal (AUTH *, XDR *); 69dfdcada3SDoug Rabson static bool_t authunix_validate (AUTH *, struct opaque_auth *); 70dfdcada3SDoug Rabson static bool_t authunix_refresh (AUTH *, void *); 71dfdcada3SDoug Rabson static void authunix_destroy (AUTH *); 72dfdcada3SDoug Rabson static void marshal_new_auth (AUTH *); 73dfdcada3SDoug Rabson 74dfdcada3SDoug Rabson static struct auth_ops authunix_ops = { 75dfdcada3SDoug Rabson .ah_nextverf = authunix_nextverf, 76dfdcada3SDoug Rabson .ah_marshal = authunix_marshal, 77dfdcada3SDoug Rabson .ah_validate = authunix_validate, 78dfdcada3SDoug Rabson .ah_refresh = authunix_refresh, 79dfdcada3SDoug Rabson .ah_destroy = authunix_destroy 80dfdcada3SDoug Rabson }; 81dfdcada3SDoug Rabson 82dfdcada3SDoug Rabson /* 83dfdcada3SDoug Rabson * This struct is pointed to by the ah_private field of an auth_handle. 84dfdcada3SDoug Rabson */ 85dfdcada3SDoug Rabson struct audata { 86c675522fSDoug Rabson TAILQ_ENTRY(audata) au_link; 87c675522fSDoug Rabson TAILQ_ENTRY(audata) au_alllink; 88c675522fSDoug Rabson int au_refs; 89c675522fSDoug Rabson struct xucred au_xcred; 90dfdcada3SDoug Rabson struct opaque_auth au_origcred; /* original credentials */ 91dfdcada3SDoug Rabson struct opaque_auth au_shcred; /* short hand cred */ 92dfdcada3SDoug Rabson u_long au_shfaults; /* short hand cache faults */ 93dfdcada3SDoug Rabson char au_marshed[MAX_AUTH_BYTES]; 94dfdcada3SDoug Rabson u_int au_mpos; /* xdr pos at end of marshed */ 95c675522fSDoug Rabson AUTH *au_auth; /* link back to AUTH */ 96dfdcada3SDoug Rabson }; 97c675522fSDoug Rabson TAILQ_HEAD(audata_list, audata); 98dfdcada3SDoug Rabson #define AUTH_PRIVATE(auth) ((struct audata *)auth->ah_private) 99dfdcada3SDoug Rabson 100c675522fSDoug Rabson #define AUTH_UNIX_HASH_SIZE 16 101c675522fSDoug Rabson #define AUTH_UNIX_MAX 256 102c675522fSDoug Rabson static struct audata_list auth_unix_cache[AUTH_UNIX_HASH_SIZE]; 103c675522fSDoug Rabson static struct audata_list auth_unix_all; 104c675522fSDoug Rabson static struct sx auth_unix_lock; 105c675522fSDoug Rabson static int auth_unix_count; 106c675522fSDoug Rabson 107c675522fSDoug Rabson static void 108c675522fSDoug Rabson authunix_init(void *dummy) 109c675522fSDoug Rabson { 110c675522fSDoug Rabson int i; 111c675522fSDoug Rabson 112c675522fSDoug Rabson for (i = 0; i < AUTH_UNIX_HASH_SIZE; i++) 113c675522fSDoug Rabson TAILQ_INIT(&auth_unix_cache[i]); 114c675522fSDoug Rabson TAILQ_INIT(&auth_unix_all); 115c675522fSDoug Rabson sx_init(&auth_unix_lock, "auth_unix_lock"); 116c675522fSDoug Rabson } 117c675522fSDoug Rabson SYSINIT(authunix_init, SI_SUB_KMEM, SI_ORDER_ANY, authunix_init, NULL); 118c675522fSDoug Rabson 119dfdcada3SDoug Rabson /* 120dfdcada3SDoug Rabson * Create a unix style authenticator. 121dfdcada3SDoug Rabson * Returns an auth handle with the given stuff in it. 122dfdcada3SDoug Rabson */ 123dfdcada3SDoug Rabson AUTH * 124dfdcada3SDoug Rabson authunix_create(struct ucred *cred) 125dfdcada3SDoug Rabson { 126c675522fSDoug Rabson uint32_t h, th; 127dfdcada3SDoug Rabson struct xucred xcr; 128dfdcada3SDoug Rabson char mymem[MAX_AUTH_BYTES]; 129dfdcada3SDoug Rabson XDR xdrs; 130dfdcada3SDoug Rabson AUTH *auth; 131c675522fSDoug Rabson struct audata *au, *tau; 132dfdcada3SDoug Rabson struct timeval now; 133dfdcada3SDoug Rabson uint32_t time; 134dfdcada3SDoug Rabson int len; 135dfdcada3SDoug Rabson 136c675522fSDoug Rabson if (auth_unix_count > AUTH_UNIX_MAX) { 137c675522fSDoug Rabson while (auth_unix_count > AUTH_UNIX_MAX) { 138c675522fSDoug Rabson sx_xlock(&auth_unix_lock); 139c675522fSDoug Rabson tau = TAILQ_FIRST(&auth_unix_all); 140c675522fSDoug Rabson th = HASHSTEP(HASHINIT, tau->au_xcred.cr_uid) 141c675522fSDoug Rabson % AUTH_UNIX_HASH_SIZE; 142c675522fSDoug Rabson TAILQ_REMOVE(&auth_unix_cache[th], tau, au_link); 143c675522fSDoug Rabson TAILQ_REMOVE(&auth_unix_all, tau, au_alllink); 144c675522fSDoug Rabson auth_unix_count--; 145c675522fSDoug Rabson sx_xunlock(&auth_unix_lock); 146c675522fSDoug Rabson AUTH_DESTROY(tau->au_auth); 147c675522fSDoug Rabson } 148c675522fSDoug Rabson } 149c675522fSDoug Rabson 150c675522fSDoug Rabson /* 151c675522fSDoug Rabson * Hash the uid to see if we already have an AUTH with this cred. 152c675522fSDoug Rabson */ 153c675522fSDoug Rabson h = HASHSTEP(HASHINIT, cred->cr_uid) % AUTH_UNIX_HASH_SIZE; 154c675522fSDoug Rabson cru2x(cred, &xcr); 155c675522fSDoug Rabson again: 156c675522fSDoug Rabson sx_slock(&auth_unix_lock); 157c675522fSDoug Rabson TAILQ_FOREACH(au, &auth_unix_cache[h], au_link) { 158c675522fSDoug Rabson if (!memcmp(&xcr, &au->au_xcred, sizeof(xcr))) { 159c675522fSDoug Rabson if (sx_try_upgrade(&auth_unix_lock)) { 160c675522fSDoug Rabson /* 161c675522fSDoug Rabson * Keep auth_unix_all LRU sorted. 162c675522fSDoug Rabson */ 163c675522fSDoug Rabson TAILQ_REMOVE(&auth_unix_all, au, au_alllink); 164c675522fSDoug Rabson TAILQ_INSERT_TAIL(&auth_unix_all, au, 165c675522fSDoug Rabson au_alllink); 166c675522fSDoug Rabson au->au_refs++; 167c675522fSDoug Rabson sx_xunlock(&auth_unix_lock); 168c675522fSDoug Rabson return (au->au_auth); 169c675522fSDoug Rabson } else { 170c675522fSDoug Rabson sx_sunlock(&auth_unix_lock); 171c675522fSDoug Rabson goto again; 172c675522fSDoug Rabson } 173c675522fSDoug Rabson } 174c675522fSDoug Rabson } 175c675522fSDoug Rabson 176dfdcada3SDoug Rabson /* 177dfdcada3SDoug Rabson * Allocate and set up auth handle 178dfdcada3SDoug Rabson */ 179dfdcada3SDoug Rabson au = NULL; 180dfdcada3SDoug Rabson auth = mem_alloc(sizeof(*auth)); 181dfdcada3SDoug Rabson au = mem_alloc(sizeof(*au)); 182dfdcada3SDoug Rabson auth->ah_ops = &authunix_ops; 183dfdcada3SDoug Rabson auth->ah_private = (caddr_t)au; 184dfdcada3SDoug Rabson auth->ah_verf = au->au_shcred = _null_auth; 185c675522fSDoug Rabson au->au_refs = 1; 186c675522fSDoug Rabson au->au_xcred = xcr; 187dfdcada3SDoug Rabson au->au_shfaults = 0; 188dfdcada3SDoug Rabson au->au_origcred.oa_base = NULL; 189c675522fSDoug Rabson au->au_auth = auth; 190dfdcada3SDoug Rabson 191dfdcada3SDoug Rabson getmicrotime(&now); 192dfdcada3SDoug Rabson time = now.tv_sec; 193dfdcada3SDoug Rabson 194dfdcada3SDoug Rabson /* 195dfdcada3SDoug Rabson * Serialize the parameters into origcred 196dfdcada3SDoug Rabson */ 197dfdcada3SDoug Rabson xdrmem_create(&xdrs, mymem, MAX_AUTH_BYTES, XDR_ENCODE); 198dfdcada3SDoug Rabson cru2x(cred, &xcr); 199dfdcada3SDoug Rabson if (! xdr_authunix_parms(&xdrs, &time, &xcr)) 200dfdcada3SDoug Rabson panic("authunix_create: failed to encode creds"); 201dfdcada3SDoug Rabson au->au_origcred.oa_length = len = XDR_GETPOS(&xdrs); 202dfdcada3SDoug Rabson au->au_origcred.oa_flavor = AUTH_UNIX; 203dfdcada3SDoug Rabson au->au_origcred.oa_base = mem_alloc((u_int) len); 204dfdcada3SDoug Rabson memcpy(au->au_origcred.oa_base, mymem, (size_t)len); 205dfdcada3SDoug Rabson 206dfdcada3SDoug Rabson /* 207dfdcada3SDoug Rabson * set auth handle to reflect new cred. 208dfdcada3SDoug Rabson */ 209dfdcada3SDoug Rabson auth->ah_cred = au->au_origcred; 210dfdcada3SDoug Rabson marshal_new_auth(auth); 211c675522fSDoug Rabson 212c675522fSDoug Rabson if (sx_try_upgrade(&auth_unix_lock)) { 213c675522fSDoug Rabson auth_unix_count++; 214c675522fSDoug Rabson TAILQ_INSERT_TAIL(&auth_unix_cache[h], au, au_link); 215c675522fSDoug Rabson TAILQ_INSERT_TAIL(&auth_unix_all, au, au_alllink); 216c675522fSDoug Rabson au->au_refs++; /* one for the cache, one for user */ 217c675522fSDoug Rabson sx_xunlock(&auth_unix_lock); 218dfdcada3SDoug Rabson return (auth); 219c675522fSDoug Rabson } else { 220c675522fSDoug Rabson sx_sunlock(&auth_unix_lock); 221c675522fSDoug Rabson AUTH_DESTROY(auth); 222c675522fSDoug Rabson goto again; 223dfdcada3SDoug Rabson } 224dfdcada3SDoug Rabson } 225dfdcada3SDoug Rabson 226dfdcada3SDoug Rabson /* 227dfdcada3SDoug Rabson * authunix operations 228dfdcada3SDoug Rabson */ 229dfdcada3SDoug Rabson 230dfdcada3SDoug Rabson /* ARGSUSED */ 231dfdcada3SDoug Rabson static void 232dfdcada3SDoug Rabson authunix_nextverf(AUTH *auth) 233dfdcada3SDoug Rabson { 234dfdcada3SDoug Rabson /* no action necessary */ 235dfdcada3SDoug Rabson } 236dfdcada3SDoug Rabson 237dfdcada3SDoug Rabson static bool_t 238dfdcada3SDoug Rabson authunix_marshal(AUTH *auth, XDR *xdrs) 239dfdcada3SDoug Rabson { 240dfdcada3SDoug Rabson struct audata *au; 241dfdcada3SDoug Rabson 242dfdcada3SDoug Rabson au = AUTH_PRIVATE(auth); 243dfdcada3SDoug Rabson return (XDR_PUTBYTES(xdrs, au->au_marshed, au->au_mpos)); 244dfdcada3SDoug Rabson } 245dfdcada3SDoug Rabson 246dfdcada3SDoug Rabson static bool_t 247dfdcada3SDoug Rabson authunix_validate(AUTH *auth, struct opaque_auth *verf) 248dfdcada3SDoug Rabson { 249dfdcada3SDoug Rabson struct audata *au; 250dfdcada3SDoug Rabson XDR xdrs; 251dfdcada3SDoug Rabson 252dfdcada3SDoug Rabson if (verf->oa_flavor == AUTH_SHORT) { 253dfdcada3SDoug Rabson au = AUTH_PRIVATE(auth); 254dfdcada3SDoug Rabson xdrmem_create(&xdrs, verf->oa_base, verf->oa_length, 255dfdcada3SDoug Rabson XDR_DECODE); 256dfdcada3SDoug Rabson 257dfdcada3SDoug Rabson if (au->au_shcred.oa_base != NULL) { 258dfdcada3SDoug Rabson mem_free(au->au_shcred.oa_base, 259dfdcada3SDoug Rabson au->au_shcred.oa_length); 260dfdcada3SDoug Rabson au->au_shcred.oa_base = NULL; 261dfdcada3SDoug Rabson } 262dfdcada3SDoug Rabson if (xdr_opaque_auth(&xdrs, &au->au_shcred)) { 263dfdcada3SDoug Rabson auth->ah_cred = au->au_shcred; 264dfdcada3SDoug Rabson } else { 265dfdcada3SDoug Rabson xdrs.x_op = XDR_FREE; 266dfdcada3SDoug Rabson (void)xdr_opaque_auth(&xdrs, &au->au_shcred); 267dfdcada3SDoug Rabson au->au_shcred.oa_base = NULL; 268dfdcada3SDoug Rabson auth->ah_cred = au->au_origcred; 269dfdcada3SDoug Rabson } 270dfdcada3SDoug Rabson marshal_new_auth(auth); 271dfdcada3SDoug Rabson } 272dfdcada3SDoug Rabson return (TRUE); 273dfdcada3SDoug Rabson } 274dfdcada3SDoug Rabson 275dfdcada3SDoug Rabson static bool_t 276dfdcada3SDoug Rabson authunix_refresh(AUTH *auth, void *dummy) 277dfdcada3SDoug Rabson { 278dfdcada3SDoug Rabson struct audata *au = AUTH_PRIVATE(auth); 279dfdcada3SDoug Rabson struct xucred xcr; 280dfdcada3SDoug Rabson uint32_t time; 281dfdcada3SDoug Rabson struct timeval now; 282dfdcada3SDoug Rabson XDR xdrs; 283dfdcada3SDoug Rabson int stat; 284dfdcada3SDoug Rabson 285dfdcada3SDoug Rabson if (auth->ah_cred.oa_base == au->au_origcred.oa_base) { 286dfdcada3SDoug Rabson /* there is no hope. Punt */ 287dfdcada3SDoug Rabson return (FALSE); 288dfdcada3SDoug Rabson } 289dfdcada3SDoug Rabson au->au_shfaults ++; 290dfdcada3SDoug Rabson 291dfdcada3SDoug Rabson /* first deserialize the creds back into a struct ucred */ 292dfdcada3SDoug Rabson xdrmem_create(&xdrs, au->au_origcred.oa_base, 293dfdcada3SDoug Rabson au->au_origcred.oa_length, XDR_DECODE); 294dfdcada3SDoug Rabson stat = xdr_authunix_parms(&xdrs, &time, &xcr); 295dfdcada3SDoug Rabson if (! stat) 296dfdcada3SDoug Rabson goto done; 297dfdcada3SDoug Rabson 298dfdcada3SDoug Rabson /* update the time and serialize in place */ 299dfdcada3SDoug Rabson getmicrotime(&now); 300dfdcada3SDoug Rabson time = now.tv_sec; 301dfdcada3SDoug Rabson xdrs.x_op = XDR_ENCODE; 302dfdcada3SDoug Rabson XDR_SETPOS(&xdrs, 0); 303dfdcada3SDoug Rabson 304dfdcada3SDoug Rabson stat = xdr_authunix_parms(&xdrs, &time, &xcr); 305dfdcada3SDoug Rabson if (! stat) 306dfdcada3SDoug Rabson goto done; 307dfdcada3SDoug Rabson auth->ah_cred = au->au_origcred; 308dfdcada3SDoug Rabson marshal_new_auth(auth); 309dfdcada3SDoug Rabson done: 310dfdcada3SDoug Rabson XDR_DESTROY(&xdrs); 311dfdcada3SDoug Rabson return (stat); 312dfdcada3SDoug Rabson } 313dfdcada3SDoug Rabson 314dfdcada3SDoug Rabson static void 315dfdcada3SDoug Rabson authunix_destroy(AUTH *auth) 316dfdcada3SDoug Rabson { 317dfdcada3SDoug Rabson struct audata *au; 318c675522fSDoug Rabson int refs; 319dfdcada3SDoug Rabson 320dfdcada3SDoug Rabson au = AUTH_PRIVATE(auth); 321c675522fSDoug Rabson 322c675522fSDoug Rabson sx_xlock(&auth_unix_lock); 323c675522fSDoug Rabson au->au_refs--; 324c675522fSDoug Rabson refs = au->au_refs; 325c675522fSDoug Rabson sx_xunlock(&auth_unix_lock); 326c675522fSDoug Rabson 327c675522fSDoug Rabson if (refs > 0) 328c675522fSDoug Rabson return; 329c675522fSDoug Rabson 330dfdcada3SDoug Rabson mem_free(au->au_origcred.oa_base, au->au_origcred.oa_length); 331dfdcada3SDoug Rabson 332dfdcada3SDoug Rabson if (au->au_shcred.oa_base != NULL) 333dfdcada3SDoug Rabson mem_free(au->au_shcred.oa_base, au->au_shcred.oa_length); 334dfdcada3SDoug Rabson 335dfdcada3SDoug Rabson mem_free(auth->ah_private, sizeof(struct audata)); 336dfdcada3SDoug Rabson 337dfdcada3SDoug Rabson if (auth->ah_verf.oa_base != NULL) 338dfdcada3SDoug Rabson mem_free(auth->ah_verf.oa_base, auth->ah_verf.oa_length); 339dfdcada3SDoug Rabson 340dfdcada3SDoug Rabson mem_free(auth, sizeof(*auth)); 341dfdcada3SDoug Rabson } 342dfdcada3SDoug Rabson 343dfdcada3SDoug Rabson /* 344dfdcada3SDoug Rabson * Marshals (pre-serializes) an auth struct. 345dfdcada3SDoug Rabson * sets private data, au_marshed and au_mpos 346dfdcada3SDoug Rabson */ 347dfdcada3SDoug Rabson static void 348dfdcada3SDoug Rabson marshal_new_auth(AUTH *auth) 349dfdcada3SDoug Rabson { 350dfdcada3SDoug Rabson XDR xdr_stream; 351dfdcada3SDoug Rabson XDR *xdrs = &xdr_stream; 352dfdcada3SDoug Rabson struct audata *au; 353dfdcada3SDoug Rabson 354dfdcada3SDoug Rabson au = AUTH_PRIVATE(auth); 355dfdcada3SDoug Rabson xdrmem_create(xdrs, au->au_marshed, MAX_AUTH_BYTES, XDR_ENCODE); 356dfdcada3SDoug Rabson if ((! xdr_opaque_auth(xdrs, &(auth->ah_cred))) || 357dfdcada3SDoug Rabson (! xdr_opaque_auth(xdrs, &(auth->ah_verf)))) 358dfdcada3SDoug Rabson printf("auth_none.c - Fatal marshalling problem"); 359dfdcada3SDoug Rabson else 360dfdcada3SDoug Rabson au->au_mpos = XDR_GETPOS(xdrs); 361dfdcada3SDoug Rabson XDR_DESTROY(xdrs); 362dfdcada3SDoug Rabson } 363