bool_t svcauth_gss_nextverf(struct svc_req *rqst, u_int num) { struct svc_rpc_gss_data *gd; gss_buffer_desc signbuf, checksum; OM_uint32 maj_stat, min_stat; log_debug("in svcauth_gss_nextverf()"); if (rqst->rq_xprt->xp_auth == NULL) return (FALSE); gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth); signbuf.value = # signbuf.length = sizeof(num); maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop, &signbuf, &checksum); if (maj_stat != GSS_S_COMPLETE) { log_status("gss_get_mic", maj_stat, min_stat); return (FALSE); } rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; rqst->rq_xprt->xp_verf.oa_base = (caddr_t)checksum.value; rqst->rq_xprt->xp_verf.oa_length = (u_int)checksum.length; return (TRUE); }
bool svcauth_gss_destroy(SVCAUTH *auth) { struct svc_rpc_gss_data *gd; OM_uint32 min_stat; gd = SVCAUTH_PRIVATE(auth); gss_delete_sec_context(&min_stat, &gd->ctx, GSS_C_NO_BUFFER); gss_release_buffer(&min_stat, &gd->cname); if (gd->client_name) gss_release_name(&min_stat, &gd->client_name); if (gd->flags & SVC_RPC_GSS_FLAG_MSPAC) gss_release_buffer(&min_stat, &gd->pac.ms_pac); gss_release_buffer(&min_stat, &gd->checksum); mutex_destroy(&gd->lock); mem_free(gd, sizeof(*gd)); mem_free(auth, sizeof(*auth)); return (true); }
static bool_t svc_auth_gssapi_destroy(SVCAUTH *auth) { svc_auth_gssapi_data *client_data = SVCAUTH_PRIVATE(auth); destroy_client(client_data); return TRUE; }
/* * Encrypt the serialized arguments from xdr_func applied to xdr_ptr * and write the result to xdrs. */ static bool_t svc_auth_gssapi_wrap( SVCAUTH *auth, XDR *out_xdrs, bool_t (*xdr_func)(), caddr_t xdr_ptr) { OM_uint32 gssstat, minor_stat; if (! SVCAUTH_PRIVATE(auth)->established) { PRINTF(("svc_gssapi_wrap: not established, noop\n")); return (*xdr_func)(out_xdrs, xdr_ptr); } else if (! auth_gssapi_wrap_data(&gssstat, &minor_stat, SVCAUTH_PRIVATE(auth)->context, SVCAUTH_PRIVATE(auth)->seq_num, out_xdrs, xdr_func, xdr_ptr)) { if (gssstat != GSS_S_COMPLETE) AUTH_GSSAPI_DISPLAY_STATUS(("encrypting function arguments", gssstat, minor_stat)); return FALSE; } else return TRUE; }
int copy_svc_authgss(SVCXPRT *xprt_copy, SVCXPRT *xprt_orig) { if(xprt_orig->xp_auth) { if(xprt_orig->xp_auth->svc_ah_ops == &Svc_auth_gss_ops || xprt_orig->xp_auth->svc_ah_ops == &Svc_auth_gss_copy_ops) { /* Copy GSS auth */ struct svc_rpc_gss_data *gd_o, *gd_c; gd_o = SVCAUTH_PRIVATE(xprt_orig->xp_auth); xprt_copy->xp_auth = (SVCAUTH *)Mem_Alloc(sizeof(SVCAUTH)); if(xprt_copy->xp_auth == NULL) return 0; gd_c = (struct svc_rpc_gss_data *)Mem_Alloc(sizeof(*gd_c)); if(gd_c == NULL) { Mem_Free(xprt_copy->xp_auth); xprt_copy->xp_auth = NULL; return 0; } /* Copy everything over */ memcpy(gd_c, gd_o, sizeof(*gd_c)); /* Leave the original without the various pointed to things */ gd_o->checksum.length = 0; gd_o->checksum.value = NULL; gd_o->cname.length = 0; gd_o->cname.value = NULL; gd_o->client_name = NULL; gd_o->ctx = NULL; /* fill in xp_auth */ xprt_copy->xp_auth->svc_ah_private = (void *)gd_c; xprt_copy->xp_auth->svc_ah_ops = &Svc_auth_gss_ops; } else { /* Should be Svc_auth_none */ if(xprt_orig->xp_auth != &Svc_auth_none) LogFullDebug(COMPONENT_RPCSEC_GSS, "copy_svc_authgss copying unknown xp_auth"); xprt_copy->xp_auth = xprt_orig->xp_auth; } } else xprt_copy->xp_auth = NULL; return 1; }
int nfs_rpc_req2client_cred(struct svc_req *req, nfs_client_cred_t *pcred) { /* Structure for managing basic AUTH_UNIX authentication */ struct authunix_parms *aup = NULL; /* Stuff needed for managing RPCSEC_GSS */ #ifdef _HAVE_GSSAPI struct svc_rpc_gss_data *gd = NULL; #endif pcred->flavor = req->rq_cred.oa_flavor; pcred->length = req->rq_cred.oa_length; switch (req->rq_cred.oa_flavor) { case AUTH_NONE: /* Do nothing... */ break; case AUTH_UNIX: aup = (struct authunix_parms *)(req->rq_clntcred); pcred->auth_union.auth_unix.aup_uid = aup->aup_uid; pcred->auth_union.auth_unix.aup_gid = aup->aup_gid; pcred->auth_union.auth_unix.aup_time = aup->aup_time; break; #ifdef _HAVE_GSSAPI case RPCSEC_GSS: /* Extract the information from the RPCSEC_GSS * opaque structure */ gd = SVCAUTH_PRIVATE(req->rq_auth); pcred->auth_union.auth_gss.svc = (unsigned int)(gd->sec.svc); pcred->auth_union.auth_gss.qop = (unsigned int)(gd->sec.qop); pcred->auth_union.auth_gss.gss_context_id = gd->ctx; break; #endif default: /* Unsupported authentication flavour */ return -1; break; } return 1; }
bool_t svcauth_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) { struct svc_rpc_gss_data *gd; log_debug("in svcauth_gss_unwrap()"); gd = SVCAUTH_PRIVATE(auth); if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) { return ((*xdr_func)(xdrs, xdr_ptr)); } return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr, gd->ctx, gd->sec.qop, gd->sec.svc, gd->seq)); }
bool svcauth_gss_unwrap(SVCAUTH *auth, struct svc_req *req, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) { bool result; struct svc_rpc_gss_data *gd = SVCAUTH_PRIVATE(req->rq_auth); u_int gc_seq = (u_int) (uintptr_t) req->rq_ap1; if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) return ((*xdr_func) (xdrs, xdr_ptr)); mutex_lock(&gd->lock); result = xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr, gd->ctx, gd->sec.qop, gd->sec.svc, gc_seq); mutex_unlock(&gd->lock); return (result); }
char * svcauth_gss_get_principal(SVCAUTH *auth) { struct svc_rpc_gss_data *gd; char *pname; gd = SVCAUTH_PRIVATE(auth); if (gd->cname.length == 0) return (NULL); pname = mem_alloc(gd->cname.length + 1); memcpy(pname, gd->cname.value, gd->cname.length); pname[gd->cname.length] = '\0'; return (pname); }
static bool_t Svcauth_gss_unwrap(SVCAUTH * auth, XDR * xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) { struct svc_rpc_gss_data *gd; gd = SVCAUTH_PRIVATE(auth); if(!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) { return ((*xdr_func) (xdrs, xdr_ptr)); } #ifndef DONT_USE_WRAPUNWRAP return (Xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr, gd->ctx, gd->sec.qop, gd->sec.svc, gd->seq)); #else return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr, gd->ctx, gd->sec.qop, gd->sec.svc, gd->seq)); #endif }
static bool svcauth_gss_release(SVCAUTH *auth, struct svc_req *req) { struct svc_rpc_gss_data *gd; caddr_t last_oa_base; gd = SVCAUTH_PRIVATE(auth); if (gd) unref_svc_rpc_gss_data(gd, SVC_RPC_GSS_FLAG_NONE); req->rq_auth = NULL; if ((last_oa_base = req->rq_verf.oa_base)) { /* XXX wrapper conflict: mem_free vs. gssalloc_free ? */ /* ... but this only matters for win32 | kernel */ req->rq_verf.oa_base = 0; mem_free(last_oa_base, req->rq_verf.oa_length); } return (true); }
static bool_t Svcauth_gss_destroy(SVCAUTH * auth) { struct svc_rpc_gss_data *gd; OM_uint32 min_stat; gd = SVCAUTH_PRIVATE(auth); gss_delete_sec_context(&min_stat, &gd->ctx, GSS_C_NO_BUFFER); gss_release_buffer(&min_stat, &gd->cname); gss_release_buffer(&min_stat, &gd->checksum); if(gd->client_name) gss_release_name(&min_stat, &gd->client_name); Mem_Free(gd); Mem_Free(auth); return (TRUE); }
bool_t svcauth_gss_destroy(SVCAUTH *auth) { struct svc_rpc_gss_data *gd; OM_uint32 min_stat; log_debug("in svcauth_gss_destroy()"); gd = SVCAUTH_PRIVATE(auth); gss_delete_sec_context(&min_stat, &gd->ctx, GSS_C_NO_BUFFER); gss_release_buffer(&min_stat, &gd->cname); if (gd->client_name) gss_release_name(&min_stat, &gd->client_name); mem_free(gd, sizeof(*gd)); mem_free(auth, sizeof(*auth)); return (TRUE); }
static bool_t svc_auth_gssapi_unwrap( SVCAUTH *auth, XDR *in_xdrs, bool_t (*xdr_func)(), caddr_t xdr_ptr) { svc_auth_gssapi_data *client_data = SVCAUTH_PRIVATE(auth); OM_uint32 gssstat, minor_stat; if (! client_data->established) { PRINTF(("svc_gssapi_unwrap: not established, noop\n")); return (*xdr_func)(in_xdrs, (auth_gssapi_init_arg *)(void *) xdr_ptr); } else if (! auth_gssapi_unwrap_data(&gssstat, &minor_stat, client_data->context, client_data->seq_num-1, in_xdrs, xdr_func, xdr_ptr)) { if (gssstat != GSS_S_COMPLETE) AUTH_GSSAPI_DISPLAY_STATUS(("decrypting function arguments", gssstat, minor_stat)); return FALSE; } else return TRUE; }
/** * * get_req_uid_gid: * * * * @param ptr_req [IN] incoming request. * @param pexport_client [IN] related export client * @param pexport [IN] related export entry * @param user_credentials [OUT] Filled in structure with uid and gids * * @return TRUE if successful, FALSE otherwise * */ int get_req_uid_gid(struct svc_req *ptr_req, exportlist_client_entry_t * pexport_client, exportlist_t * pexport, struct user_cred *user_credentials) { struct authunix_parms *punix_creds = NULL; unsigned int rpcxid = 0; #ifdef _HAVE_GSSAPI struct svc_rpc_gss_data *gd = NULL; char principal[MAXNAMLEN]; #endif if (user_credentials == NULL) return FALSE; rpcxid = get_rpc_xid(ptr_req); switch (ptr_req->rq_cred.oa_flavor) { case AUTH_NONE: /* Nothing to be done here... */ LogFullDebug(COMPONENT_DISPATCH, "Request xid=%u has authentication AUTH_NONE", rpcxid); break; case AUTH_UNIX: LogFullDebug(COMPONENT_DISPATCH, "Request xid=%u has authentication AUTH_UNIX", rpcxid); /* We map the rq_cred to Authunix_parms */ punix_creds = (struct authunix_parms *)ptr_req->rq_clntcred; /* Get the uid/gid couple */ user_credentials->caller_uid = punix_creds->aup_uid; user_credentials->caller_gid = punix_creds->aup_gid; user_credentials->caller_glen = punix_creds->aup_len; user_credentials->caller_garray = punix_creds->aup_gids; LogFullDebug(COMPONENT_DISPATCH, "----> Uid=%u Gid=%u", (unsigned int)user_credentials->caller_uid, (unsigned int)user_credentials->caller_gid); break; #ifdef _HAVE_GSSAPI case RPCSEC_GSS: LogFullDebug(COMPONENT_DISPATCH, "Request xid=%u has authentication RPCSEC_GSS", rpcxid); /* Get the gss data to process them */ gd = SVCAUTH_PRIVATE(ptr_req->rq_xprt->xp_auth); if(isFullDebug(COMPONENT_RPCSEC_GSS)) { OM_uint32 maj_stat = 0; OM_uint32 min_stat = 0; char ptr[256]; gss_buffer_desc oidbuff; LogFullDebug(COMPONENT_RPCSEC_GSS, "----> RPCSEC_GSS svc=%u RPCSEC_GSS_SVC_NONE=%u RPCSEC_GSS_SVC_INTEGRITY=%u RPCSEC_GSS_SVC_PRIVACY=%u", gd->sec.svc, RPCSEC_GSS_SVC_NONE, RPCSEC_GSS_SVC_INTEGRITY, RPCSEC_GSS_SVC_PRIVACY); memcpy(&ptr, (void *)gd->ctx + 4, 4); LogFullDebug(COMPONENT_RPCSEC_GSS, "----> Client=%s length=%lu Qop=%u established=%u gss_ctx_id=%p|%p", (char *)gd->cname.value, gd->cname.length, gd->established, gd->sec.qop, gd->ctx, ptr); if((maj_stat = gss_oid_to_str(&min_stat, gd->sec.mech, &oidbuff)) != GSS_S_COMPLETE) { LogFullDebug(COMPONENT_DISPATCH, "Error in gss_oid_to_str: %u|%u", maj_stat, min_stat); } else { LogFullDebug(COMPONENT_RPCSEC_GSS, "----> Client mech=%s len=%lu", (char *)oidbuff.value, oidbuff.length); // Release the string (void)gss_release_buffer(&min_stat, &oidbuff); } } LogFullDebug(COMPONENT_RPCSEC_GSS, "Mapping principal %s to uid/gid", (char *)gd->cname.value); memcpy(principal, gd->cname.value, gd->cname.length); principal[gd->cname.length] = 0; /* Convert to uid */ if(!principal2uid(principal, &user_credentials->caller_uid)) { LogWarn(COMPONENT_IDMAPPER, "WARNING: Could not map principal to uid; mapping principal " "to anonymous uid/gid"); /* For compatibility with Linux knfsd, we set the uid/gid * to anonymous when a name->uid mapping can't be found. */ user_credentials->caller_uid = pexport->anonymous_uid; user_credentials->caller_gid = pexport->anonymous_gid; /* No alternate groups for "nobody" */ user_credentials->caller_glen = 0 ; user_credentials->caller_garray = NULL ; return TRUE; } if(uidgidmap_get(user_credentials->caller_uid, &user_credentials->caller_gid) != ID_MAPPER_SUCCESS) { LogMajor(COMPONENT_DISPATCH, "FAILURE: Could not resolve uidgid map for %u", user_credentials->caller_uid); user_credentials->caller_gid = -1; } LogFullDebug(COMPONENT_DISPATCH, "----> Uid=%u Gid=%u", (unsigned int)user_credentials->caller_uid, (unsigned int)user_credentials->caller_gid); user_credentials->caller_glen = 0; user_credentials->caller_garray = 0; break; #endif /* _USE_GSSRPC */ default: LogFullDebug(COMPONENT_DISPATCH, "FAILURE: Request xid=%u, has unsupported authentication %d", rpcxid, ptr_req->rq_cred.oa_flavor); /* Reject the request for weak authentication and return to worker */ return FALSE; break; } /* switch( ptr_req->rq_cred.oa_flavor ) */ return TRUE; }
static bool_t Svcauth_gss_accept_sec_context(struct svc_req *rqst, struct rpc_gss_init_res *gr) { struct svc_rpc_gss_data *gd; struct rpc_gss_cred *gc; gss_buffer_desc recv_tok, seqbuf; gss_OID mech; OM_uint32 maj_stat = 0, min_stat = 0, ret_flags, seq; gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth); gc = (struct rpc_gss_cred *)rqst->rq_clntcred; memset(gr, 0, sizeof(*gr)); /* Deserialize arguments. */ memset(&recv_tok, 0, sizeof(recv_tok)); if(!svc_getargs(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_args, (caddr_t) & recv_tok)) return (FALSE); gr->gr_major = gss_accept_sec_context(&gr->gr_minor, &gd->ctx, svcauth_gss_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &gd->client_name, &mech, &gr->gr_token, &ret_flags, NULL, NULL); svc_freeargs(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_args, (caddr_t) & recv_tok); if(gr->gr_major != GSS_S_COMPLETE && gr->gr_major != GSS_S_CONTINUE_NEEDED) { sockaddr_t addr; char ipstring[SOCK_NAME_MAX]; copy_xprt_addr(&addr, rqst->rq_xprt); sprint_sockaddr(&addr, ipstring, sizeof(ipstring)); LogWarn(COMPONENT_RPCSEC_GSS, "Bad authentication major=%u minor=%u addr=%s", gr->gr_major, gr->gr_minor, ipstring); gd->ctx = GSS_C_NO_CONTEXT; goto errout; } /* * ANDROS: krb5 mechglue returns ctx of size 8 - two pointers, * one to the mechanism oid, one to the internal_ctx_id */ if((gr->gr_ctx.value = Mem_Alloc(sizeof(gss_union_ctx_id_desc))) == NULL) { LogCrit(COMPONENT_RPCSEC_GSS, "svcauth_gss_accept_context: out of memory"); goto errout; } memcpy(gr->gr_ctx.value, gd->ctx, sizeof(gss_union_ctx_id_desc)); gr->gr_ctx.length = sizeof(gss_union_ctx_id_desc); /* gr->gr_win = 0x00000005; ANDROS: for debugging linux kernel version... */ gr->gr_win = sizeof(gd->seqmask) * 8; /* Save client info. */ gd->sec.mech = mech; gd->sec.qop = GSS_C_QOP_DEFAULT; gd->sec.svc = gc->gc_svc; gd->seq = gc->gc_seq; gd->win = gr->gr_win; if(gr->gr_major == GSS_S_COMPLETE) { #ifdef SPKM /* spkm3: no src_name (anonymous) */ if(!g_OID_equal(gss_mech_spkm3, mech)) { #endif maj_stat = gss_display_name(&min_stat, gd->client_name, &gd->cname, &gd->sec.mech); LogFullDebug(COMPONENT_RPCSEC_GSS, "cname.val: %s cname.len: %d", (char *)gd->cname.value, (int)gd->cname.length); #ifdef SPKM } #endif if(maj_stat != GSS_S_COMPLETE) { } #ifdef HAVE_HEIMDAL #else if(isFullDebug(COMPONENT_RPCSEC_GSS)) { gss_buffer_desc mechname; gss_oid_to_str(&min_stat, mech, &mechname); gss_release_buffer(&min_stat, &mechname); } #endif seq = htonl(gr->gr_win); seqbuf.value = &seq; seqbuf.length = sizeof(seq); gss_release_buffer(&min_stat, &gd->checksum); LogFullDebug(COMPONENT_RPCSEC_GSS, "gss_sign in sec_accept_context"); maj_stat = gss_sign(&min_stat, gd->ctx, GSS_C_QOP_DEFAULT, &seqbuf, &gd->checksum); if(maj_stat != GSS_S_COMPLETE) { goto errout; } rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; rqst->rq_xprt->xp_verf.oa_base = gd->checksum.value; rqst->rq_xprt->xp_verf.oa_length = gd->checksum.length; } return (TRUE); errout: gss_release_buffer(&min_stat, &gr->gr_token); return (FALSE); }
enum auth_stat Gssrpc__svcauth_gss(struct svc_req *rqst, struct rpc_msg *msg, bool_t * no_dispatch) { enum auth_stat retstat; XDR xdrs; SVCAUTH *auth; struct svc_rpc_gss_data *gd; struct rpc_gss_cred *gc; struct rpc_gss_init_res gr; int call_stat, offset; OM_uint32 min_stat; gss_union_ctx_id_desc *gss_ctx_data; char ctx_str[64]; /* Used to update the hashtable entries. * These should not be used for purposes other than updating * hashtable entries. */ bool_t *p_established = NULL; u_int *p_seqlast = NULL; uint32_t *p_seqmask = NULL; /* Initialize reply. */ LogFullDebug(COMPONENT_RPCSEC_GSS, "Gssrpc__svcauth_gss called"); /* Allocate and set up server auth handle. */ if(rqst->rq_xprt->xp_auth == NULL || rqst->rq_xprt->xp_auth == &Svc_auth_none) { if((auth = (SVCAUTH *)Mem_Calloc(1, sizeof(*auth))) == NULL) { LogCrit(COMPONENT_RPCSEC_GSS, "svcauth_gss: out_of_memory"); return (AUTH_FAILED); } if((gd = (struct svc_rpc_gss_data *)Mem_Calloc(1, sizeof(*gd))) == NULL) { LogCrit(COMPONENT_RPCSEC_GSS, "svcauth_gss: out_of_memory"); Mem_Free(auth); return (AUTH_FAILED); } auth->svc_ah_ops = &Svc_auth_gss_ops; auth->svc_ah_private = (void *)gd; rqst->rq_xprt->xp_auth = auth; } else gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth); /* Deserialize client credentials. */ if(rqst->rq_cred.oa_length <= 0) return (AUTH_BADCRED); gc = (struct rpc_gss_cred *)rqst->rq_clntcred; memset(gc, 0, sizeof(*gc)); xdrmem_create(&xdrs, rqst->rq_cred.oa_base, rqst->rq_cred.oa_length, XDR_DECODE); if(!xdr_rpc_gss_cred(&xdrs, gc)) { XDR_DESTROY(&xdrs); return (AUTH_BADCRED); } XDR_DESTROY(&xdrs); if(gc->gc_ctx.length != 0) gss_ctx_data = (gss_union_ctx_id_desc *)(gc->gc_ctx.value); else gss_ctx_data = NULL; if(isFullDebug(COMPONENT_RPCSEC_GSS)) { sprint_ctx(ctx_str, (char *)gc->gc_ctx.value, gc->gc_ctx.length); LogFullDebug(COMPONENT_RPCSEC_GSS, "Gssrpc__svcauth_gss gc_proc (%u) %s context %s", gc->gc_proc, str_gc_proc(gc->gc_proc), ctx_str); } /* If we do not retrieve gss data from the cache, then this important * variables could not possibly be meaningful. */ gd->seqlast = 0; gd->seqmask = 0; gd->established = 0; /** @todo Think about restoring the correct lines */ //if( gd->established == 0 && gc->gc_proc == RPCSEC_GSS_DATA ) if(gc->gc_proc == RPCSEC_GSS_DATA || gc->gc_proc == RPCSEC_GSS_DESTROY) { if(isFullDebug(COMPONENT_RPCSEC_GSS)) { LogFullDebug(COMPONENT_RPCSEC_GSS, "Dump context hash table"); Gss_ctx_Hash_Print(); } LogFullDebug(COMPONENT_RPCSEC_GSS, "Getting gss data struct from hashtable."); /* Fill in svc_rpc_gss_data from cache */ if(!Gss_ctx_Hash_Get(gss_ctx_data, gd, &p_established, &p_seqlast, &p_seqmask)) { LogCrit(COMPONENT_RPCSEC_GSS, "Could not find gss context "); ret_freegc(AUTH_REJECTEDCRED); } else { /* If you 'mount -o sec=krb5i' you will have gc->gc_proc > RPCSEC_GSS_SVN_NONE, but the * negociation will have been made as if option was -o sec=krb5, the value of sec.svc has to be updated * id the stored gd that we got fromn the hash */ if(gc->gc_svc != gd->sec.svc) gd->sec.svc = gc->gc_svc; } } if(isFullDebug(COMPONENT_RPCSEC_GSS)) { char ctx_str_2[64]; sprint_ctx(ctx_str_2, (unsigned char *)gd->ctx, sizeof(gss_ctx_data)); sprint_ctx(ctx_str, (unsigned char *)gc->gc_ctx.value, gc->gc_ctx.length); LogFullDebug(COMPONENT_RPCSEC_GSS, "Call to Gssrpc__svcauth_gss ----> Client=%s length=%lu (GD: established=%u ctx=%s) (RQ:sock=%u) (GC: Proc=%u Svc=%u ctx=%s)", (char *)gd->cname.value, (long unsigned int)gd->cname.length, gd->established, ctx_str_2, rqst->rq_xprt->XP_SOCK, gc->gc_proc, gc->gc_svc, ctx_str); } retstat = AUTH_FAILED; /* Check version. */ if(gc->gc_v != RPCSEC_GSS_VERSION) { LogDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: bad GSS version."); ret_freegc(AUTH_BADCRED); } /* Check RPCSEC_GSS service. */ if(gc->gc_svc != RPCSEC_GSS_SVC_NONE && gc->gc_svc != RPCSEC_GSS_SVC_INTEGRITY && gc->gc_svc != RPCSEC_GSS_SVC_PRIVACY) { LogDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: bad GSS service (krb5, krb5i, krb5p)"); ret_freegc(AUTH_BADCRED); } /* Check sequence number. */ if(gd->established) { /* Sequence should be less than the max sequence number */ if(gc->gc_seq > MAXSEQ) { LogDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: max sequence number exceeded."); ret_freegc(RPCSEC_GSS_CTXPROBLEM); } /* Check the difference between the current sequence number * and the last sequence number. */ LogFullDebug(COMPONENT_RPCSEC_GSS, "seqlast: %d seqnum: %d offset: %d seqwin: %d seqmask: %x", gd->seqlast, gc->gc_seq, gd->seqlast - gc->gc_seq, gd->win, gd->seqmask); if((offset = gd->seqlast - gc->gc_seq) < 0) { gd->seqlast = gc->gc_seq; offset = 0 - offset; gd->seqmask <<= offset; offset = 0; } else if((unsigned int)offset >= gd->win || (gd->seqmask & (1 << (unsigned int)offset))) { if ((unsigned int)offset >= gd->win) LogDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: the current seqnum is lower" " than seqlast by %d and out of the seq window of size %d.", offset, gd->win); else LogDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: the current seqnum has already been used."); *no_dispatch = TRUE; ret_freegc(RPCSEC_GSS_CTXPROBLEM); } gd->seq = gc->gc_seq; gd->seqmask |= (1 << offset); } if(gd->established) { rqst->rq_clntname = (char *)gd->client_name; #ifndef _USE_TIRPC rqst->rq_svccred = (char *)gd->ctx; #else rqst->rq_svcname = (char *)gd->ctx; #endif } /* Handle RPCSEC_GSS control procedure. */ switch (gc->gc_proc) { case RPCSEC_GSS_INIT: LogFullDebug(COMPONENT_RPCSEC_GSS, "Reached RPCSEC_GSS_INIT:"); case RPCSEC_GSS_CONTINUE_INIT: LogFullDebug(COMPONENT_RPCSEC_GSS, "Reached RPCSEC_GSS_CONTINUE_INIT:"); if(rqst->rq_proc != NULLPROC) { LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: request proc != NULL during INIT request"); ret_freegc(AUTH_FAILED); /* XXX ? */ } if(!Svcauth_gss_acquire_cred()) { LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Can't acquire credentials from RPC request."); ret_freegc(AUTH_FAILED); } if(!Svcauth_gss_accept_sec_context(rqst, &gr)) { LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Can't accept the security context."); ret_freegc(AUTH_REJECTEDCRED); } if(!Svcauth_gss_nextverf(rqst, htonl(gr.gr_win))) { gss_release_buffer(&min_stat, &gr.gr_token); Mem_Free(gr.gr_ctx.value); LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Checksum verification failed"); ret_freegc(AUTH_FAILED); } *no_dispatch = TRUE; if(isFullDebug(COMPONENT_RPCSEC_GSS)) { sprint_ctx(ctx_str, (unsigned char *)gr.gr_ctx.value, gr.gr_ctx.length); LogFullDebug(COMPONENT_RPCSEC_GSS, "Call to Gssrpc__svcauth_gss ----> Client=%s length=%lu (GD: established=%u) (RQ:sock=%u) (GR: maj=%u min=%u ctx=%s)", (char *)gd->cname.value, (long unsigned int)gd->cname.length, gd->established, rqst->rq_xprt->XP_SOCK, gr.gr_major, gr.gr_minor, ctx_str); } call_stat = svc_sendreply(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_res, (caddr_t) & gr); gss_release_buffer(&min_stat, &gr.gr_token); gss_release_buffer(&min_stat, &gd->checksum); Mem_Free(gr.gr_ctx.value); if(!call_stat) { LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: svc_sendreply failed."); ret_freegc(AUTH_FAILED); } if(gr.gr_major == GSS_S_COMPLETE) { gss_union_ctx_id_desc *gss_ctx_data2 = (gss_union_ctx_id_desc *)gd->ctx; gd->established = TRUE; /* Keep the gss context in a hash, gr.gr_ctx.value is used as key */ (void) Gss_ctx_Hash_Set(gss_ctx_data2, gd); } break; case RPCSEC_GSS_DATA: LogFullDebug(COMPONENT_RPCSEC_GSS, "Reached RPCSEC_GSS_DATA:"); if(!Svcauth_gss_validate(rqst, gd, msg)) { LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Couldn't validate request."); ret_freegc(RPCSEC_GSS_CREDPROBLEM); } if(!Svcauth_gss_nextverf(rqst, htonl(gc->gc_seq))) { LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Checksum verification failed."); ret_freegc(AUTH_FAILED); } /* Update a few important values in the hashtable entry */ if ( p_established != NULL) *p_established = gd->established; if ( p_seqlast != NULL) *p_seqlast = gd->seqlast; if (p_seqmask != NULL) *p_seqmask = gd->seqmask; break; case RPCSEC_GSS_DESTROY: LogFullDebug(COMPONENT_RPCSEC_GSS, "Reached RPCSEC_GSS_DESTROY:"); if(rqst->rq_proc != NULLPROC) ret_freegc(AUTH_FAILED); /* XXX ? */ if(!Svcauth_gss_validate(rqst, gd, msg)) ret_freegc(RPCSEC_GSS_CREDPROBLEM); if(!Svcauth_gss_nextverf(rqst, htonl(gc->gc_seq))) { LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Checksum verification failed."); ret_freegc(AUTH_FAILED); } *no_dispatch = TRUE; call_stat = svc_sendreply(rqst->rq_xprt, (xdrproc_t)xdr_void, (caddr_t) NULL); if(!Gss_ctx_Hash_Del(gss_ctx_data)) { LogCrit(COMPONENT_RPCSEC_GSS, "Could not delete Gss Context from hash"); } else LogFullDebug(COMPONENT_RPCSEC_GSS, "Gss_ctx_Hash_Del OK"); if(!Svcauth_gss_release_cred()) { LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Failed to release credentials."); ret_freegc(AUTH_FAILED); } if(rqst->rq_xprt->xp_auth) SVCAUTH_DESTROY(rqst->rq_xprt->xp_auth); rqst->rq_xprt->xp_auth = &Svc_auth_none; break; default: LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Request is not INIT, INIT_CONTINUE, DATA, OR DESTROY."); ret_freegc(AUTH_REJECTEDCRED); break; } LogFullDebug(COMPONENT_RPCSEC_GSS, "Call to Gssrpc__svcauth_gss - OK ---> (RQ:sock=%u)", rqst->rq_xprt->XP_SOCK); retstat = AUTH_OK; freegc: if(retstat != AUTH_OK) LogCrit(COMPONENT_RPCSEC_GSS, "Call to Gssrpc__svcauth_gss - FAILED ---> (RQ:sock=%u)", rqst->rq_xprt->XP_SOCK); xdr_free((xdrproc_t)xdr_rpc_gss_cred, gc); return (retstat); }
/** * @brief Get numeric credentials from request * * @todo This MUST be refactored to not use TI-RPC private structures. * Instead, export appropriate functions from lib(n)tirpc. * * fills out creds in op_ctx * * @param[in] req Incoming request. * * @return NFS4_OK if successful, NFS4ERR_ACCESS otherwise. * */ nfsstat4 nfs_req_creds(struct svc_req *req) { unsigned int i; const char *auth_label = "UNKNOWN"; gid_t **garray_copy = &op_ctx->caller_garray_copy; #ifdef _HAVE_GSSAPI struct svc_rpc_gss_data *gd = NULL; char principal[MAXNAMLEN + 1]; #endif /* Make sure we clear out all the cred_flags except CREDS_LOADED and * CREDS_ANON. */ op_ctx->cred_flags &= CREDS_LOADED | CREDS_ANON; switch (req->rq_cred.oa_flavor) { case AUTH_NONE: /* Nothing to be done here... */ op_ctx->cred_flags |= CREDS_LOADED | CREDS_ANON; auth_label = "AUTH_NONE"; break; case AUTH_SYS: if ((op_ctx->cred_flags & CREDS_LOADED) == 0) { struct authunix_parms *creds = NULL; /* We map the rq_cred to Authunix_parms */ creds = (struct authunix_parms *) req->rq_clntcred; op_ctx->original_creds.caller_uid = creds->aup_uid; op_ctx->original_creds.caller_gid = creds->aup_gid; op_ctx->original_creds.caller_glen = creds->aup_len; op_ctx->original_creds.caller_garray = creds->aup_gids; op_ctx->cred_flags |= CREDS_LOADED; } /* Copy original_creds creds */ *op_ctx->creds = op_ctx->original_creds; /* Do we trust AUTH_SYS creds for groups or not ? */ if ((op_ctx->export_perms->options & EXPORT_OPTION_MANAGE_GIDS) != 0) { op_ctx->cred_flags |= MANAGED_GIDS; garray_copy = &op_ctx->managed_garray_copy; } auth_label = "AUTH_SYS"; break; #ifdef _HAVE_GSSAPI case RPCSEC_GSS: if ((op_ctx->cred_flags & CREDS_LOADED) == 0) { /* Get the gss data to process them */ gd = SVCAUTH_PRIVATE(req->rq_auth); memcpy(principal, gd->cname.value, gd->cname.length); principal[gd->cname.length] = 0; LogMidDebug(COMPONENT_DISPATCH, "Mapping RPCSEC_GSS principal %s to uid/gid", principal); /* Convert to uid */ #if _MSPAC_SUPPORT if (!principal2uid(principal, &op_ctx->original_creds.caller_uid, &op_ctx->original_creds.caller_gid, gd)) { #else if (!principal2uid(principal, &op_ctx->original_creds.caller_uid, &op_ctx->original_creds.caller_gid) ) { #endif LogWarn(COMPONENT_IDMAPPER, "Could not map principal %s to uid", principal); /* For compatibility with Linux knfsd, we set * the uid/gid to anonymous when a name->uid * mapping can't be found. */ op_ctx->cred_flags |= CREDS_ANON | CREDS_LOADED; auth_label = "RPCSEC_GSS (no mapping)"; break; } op_ctx->cred_flags |= CREDS_LOADED; } auth_label = "RPCSEC_GSS"; op_ctx->cred_flags |= MANAGED_GIDS; garray_copy = &op_ctx->managed_garray_copy; break; #endif /* _USE_GSSRPC */ default: LogMidDebug(COMPONENT_DISPATCH, "FAILURE: Request xid=%u, has unsupported authentication %d", req->rq_xid, req->rq_cred.oa_flavor); /* Reject the request for weak authentication and * return to worker */ return NFS4ERR_ACCESS; break; } /****************************************************************/ /* Now check for anon creds or id squashing */ /****************************************************************/ if ((op_ctx->cred_flags & CREDS_ANON) != 0 || ((op_ctx->export_perms->options & EXPORT_OPTION_ALL_ANONYMOUS) != 0) || ((op_ctx->export_perms->options & EXPORT_OPTION_ROOT) == 0 && op_ctx->original_creds.caller_uid == 0)) { op_ctx->creds->caller_uid = op_ctx->export_perms->anonymous_uid; op_ctx->creds->caller_gid = op_ctx->export_perms->anonymous_gid; op_ctx->creds->caller_glen = 0; LogMidDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "%s creds squashed to uid=%u, gid=%u", auth_label, op_ctx->creds->caller_uid, op_ctx->creds->caller_gid); op_ctx->cred_flags |= UID_SQUASHED | GID_SQUASHED; return NFS4_OK; } /* Now we will use the original_creds uid from original credential */ op_ctx->creds->caller_uid = op_ctx->original_creds.caller_uid; /****************************************************************/ /* Now sqush group or use original_creds gid */ /****************************************************************/ if ((op_ctx->export_perms->options & EXPORT_OPTION_ROOT) == 0 && op_ctx->original_creds.caller_gid == 0) { /* Squash gid */ op_ctx->creds->caller_gid = op_ctx->export_perms->anonymous_gid; op_ctx->cred_flags |= GID_SQUASHED; } else { /* Use original_creds gid */ op_ctx->creds->caller_gid = op_ctx->original_creds.caller_gid; } /****************************************************************/ /* Check if we have manage_gids. */ /****************************************************************/ if ((op_ctx->cred_flags & MANAGED_GIDS) != 0) { /* Fetch the group data if required */ if (op_ctx->caller_gdata == NULL && !uid2grp(op_ctx->original_creds.caller_uid, &op_ctx->caller_gdata)) { /** @todo: do we really want to bail here? */ LogCrit(COMPONENT_DISPATCH, "Attempt to fetch managed_gids failed"); return NFS4ERR_ACCESS; } op_ctx->creds->caller_glen = op_ctx->caller_gdata->nbgroups; op_ctx->creds->caller_garray = op_ctx->caller_gdata->groups; } else { /* Use the original_creds group list */ op_ctx->creds->caller_glen = op_ctx->original_creds.caller_glen; op_ctx->creds->caller_garray = op_ctx->original_creds.caller_garray; } /****************************************************************/ /* Check the garray for gid 0 to squash */ /****************************************************************/ /* If no root squashing in caller_garray, return now */ if ((op_ctx->export_perms->options & EXPORT_OPTION_ROOT) != 0 || op_ctx->creds->caller_glen == 0) goto out; for (i = 0; i < op_ctx->creds->caller_glen; i++) { if (op_ctx->creds->caller_garray[i] == 0) { /* Meed to make a copy, or use the old copy */ if ((*garray_copy) == NULL) { /* Make a copy of the active garray */ (*garray_copy) = gsh_malloc(op_ctx->creds->caller_glen * sizeof(gid_t)); memcpy((*garray_copy), op_ctx->creds->caller_garray, op_ctx->creds->caller_glen * sizeof(gid_t)); } /* Now squash the root id. Since the original copy is * always the same, any root ids in it were still in * the same place, so even if using a copy that had a * different anonymous_gid, we're fine. */ (*garray_copy)[i] = op_ctx->export_perms->anonymous_gid; /* Indicate we squashed the caller_garray */ op_ctx->cred_flags |= GARRAY_SQUASHED; } } /* If we squashed the caller_garray, use the squashed copy */ if ((op_ctx->cred_flags & GARRAY_SQUASHED) != 0) op_ctx->creds->caller_garray = *garray_copy; out: LogMidDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "%s creds mapped to uid=%u, gid=%u%s, glen=%d%s", auth_label, op_ctx->creds->caller_uid, op_ctx->creds->caller_gid, (op_ctx->cred_flags & GID_SQUASHED) != 0 ? " (squashed)" : "", op_ctx->creds->caller_glen, (op_ctx->cred_flags & MANAGED_GIDS) != 0 ? ((op_ctx->cred_flags & GARRAY_SQUASHED) != 0 ? " (managed and squashed)" : " (managed)") : ((op_ctx->cred_flags & GARRAY_SQUASHED) != 0 ? " (squashed)" : "")); return NFS4_OK; } /** * @brief Initialize request context and credentials. * */ void init_credentials(void) { memset(op_ctx->creds, 0, sizeof(*op_ctx->creds)); memset(&op_ctx->original_creds, 0, sizeof(op_ctx->original_creds)); op_ctx->creds->caller_uid = op_ctx->export_perms->anonymous_uid; op_ctx->creds->caller_gid = op_ctx->export_perms->anonymous_gid; op_ctx->caller_gdata = NULL; op_ctx->caller_garray_copy = NULL; op_ctx->managed_garray_copy = NULL; op_ctx->cred_flags = 0; }
enum auth_stat _svcauth_gss(struct svc_req *rqst, struct rpc_msg *msg, bool_t *no_dispatch) { XDR xdrs; SVCAUTH *auth; struct svc_rpc_gss_data *gd; struct rpc_gss_cred *gc; struct rpc_gss_init_res gr; int call_stat, offset; log_debug("in svcauth_gss()"); /* Initialize reply. */ rqst->rq_xprt->xp_verf = _null_auth; /* Allocate and set up server auth handle. */ if (rqst->rq_xprt->xp_auth == NULL || rqst->rq_xprt->xp_auth == &svc_auth_none) { if ((auth = calloc(sizeof(*auth), 1)) == NULL) { fprintf(stderr, "svcauth_gss: out_of_memory\n"); return (AUTH_FAILED); } if ((gd = calloc(sizeof(*gd), 1)) == NULL) { fprintf(stderr, "svcauth_gss: out_of_memory\n"); return (AUTH_FAILED); } auth->svc_ah_ops = &svc_auth_gss_ops; auth->svc_ah_private = (caddr_t) gd; rqst->rq_xprt->xp_auth = auth; } else gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth); /* Deserialize client credentials. */ if (rqst->rq_cred.oa_length <= 0) return (AUTH_BADCRED); gc = (struct rpc_gss_cred *)rqst->rq_clntcred; memset(gc, 0, sizeof(*gc)); xdrmem_create(&xdrs, rqst->rq_cred.oa_base, rqst->rq_cred.oa_length, XDR_DECODE); if (!xdr_rpc_gss_cred(&xdrs, gc)) { XDR_DESTROY(&xdrs); return (AUTH_BADCRED); } XDR_DESTROY(&xdrs); /* Check version. */ if (gc->gc_v != RPCSEC_GSS_VERSION) return (AUTH_BADCRED); /* Check RPCSEC_GSS service. */ if (gc->gc_svc != RPCSEC_GSS_SVC_NONE && gc->gc_svc != RPCSEC_GSS_SVC_INTEGRITY && gc->gc_svc != RPCSEC_GSS_SVC_PRIVACY) return (AUTH_BADCRED); /* Check sequence number. */ if (gd->established) { if (gc->gc_seq > MAXSEQ) return (RPCSEC_GSS_CTXPROBLEM); if ((offset = gd->seqlast - gc->gc_seq) < 0) { gd->seqlast = gc->gc_seq; offset = 0 - offset; gd->seqmask <<= offset; offset = 0; } else if (offset >= gd->win || (gd->seqmask & (1 << offset))) { *no_dispatch = 1; return (RPCSEC_GSS_CTXPROBLEM); } gd->seq = gc->gc_seq; gd->seqmask |= (1 << offset); } if (gd->established) { rqst->rq_clntname = (char *)gd->client_name; rqst->rq_svcname = (char *)gd->ctx; } /* Handle RPCSEC_GSS control procedure. */ switch (gc->gc_proc) { case RPCSEC_GSS_INIT: case RPCSEC_GSS_CONTINUE_INIT: if (rqst->rq_proc != NULLPROC) return (AUTH_FAILED); /* XXX ? */ if (_svcauth_gss_name == NULL) { if (!svcauth_gss_import_name("nfs")) return (AUTH_FAILED); } if (!svcauth_gss_acquire_cred()) return (AUTH_FAILED); if (!svcauth_gss_accept_sec_context(rqst, &gr)) return (AUTH_REJECTEDCRED); if (!svcauth_gss_nextverf(rqst, htonl(gr.gr_win))) return (AUTH_FAILED); *no_dispatch = TRUE; call_stat = svc_sendreply(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_res, (caddr_t)&gr); if (!call_stat) return (AUTH_FAILED); if (gr.gr_major == GSS_S_COMPLETE) gd->established = TRUE; break; case RPCSEC_GSS_DATA: if (!svcauth_gss_validate(gd, msg)) return (RPCSEC_GSS_CREDPROBLEM); if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq))) return (AUTH_FAILED); break; case RPCSEC_GSS_DESTROY: if (rqst->rq_proc != NULLPROC) return (AUTH_FAILED); /* XXX ? */ if (!svcauth_gss_validate(gd, msg)) return (RPCSEC_GSS_CREDPROBLEM); if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq))) return (AUTH_FAILED); if (!svcauth_gss_release_cred()) return (AUTH_FAILED); SVCAUTH_DESTROY(rqst->rq_xprt->xp_auth); rqst->rq_xprt->xp_auth = &svc_auth_none; break; default: return (AUTH_REJECTEDCRED); break; } return (AUTH_OK); }
static bool_t svcauth_gss_accept_sec_context(struct svc_req *rqst, struct rpc_gss_init_res *gr) { struct svc_rpc_gss_data *gd; struct rpc_gss_cred *gc; gss_buffer_desc recv_tok, seqbuf; gss_OID mech; OM_uint32 maj_stat = 0, min_stat = 0, ret_flags, seq; log_debug("in svcauth_gss_accept_context()"); gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth); gc = (struct rpc_gss_cred *)rqst->rq_clntcred; memset(gr, 0, sizeof(*gr)); /* Deserialize arguments. */ memset(&recv_tok, 0, sizeof(recv_tok)); if (!svc_getargs(rqst->rq_xprt, xdr_rpc_gss_init_args, (caddr_t)&recv_tok)) return (FALSE); gr->gr_major = gss_accept_sec_context(&gr->gr_minor, &gd->ctx, svcauth_gss_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &gd->client_name, &mech, &gr->gr_token, &ret_flags, NULL, NULL); svc_freeargs(rqst->rq_xprt, xdr_rpc_gss_init_args, (caddr_t)&recv_tok); log_status("accept_sec_context", gr->gr_major, gr->gr_minor); if (gr->gr_major != GSS_S_COMPLETE && gr->gr_major != GSS_S_CONTINUE_NEEDED) { badauth(gr->gr_major, gr->gr_minor, rqst->rq_xprt); gd->ctx = GSS_C_NO_CONTEXT; goto errout; } gr->gr_ctx.value = "xxxx"; gr->gr_ctx.length = 4; /* gr->gr_win = 0x00000005; ANDROS: for debugging linux kernel version... */ gr->gr_win = sizeof(gd->seqmask) * 8; /* Save client info. */ gd->sec.mech = mech; gd->sec.qop = GSS_C_QOP_DEFAULT; gd->sec.svc = gc->gc_svc; gd->seq = gc->gc_seq; gd->win = gr->gr_win; if (gr->gr_major == GSS_S_COMPLETE) { #ifdef SPKM /* spkm3: no src_name (anonymous) */ if(!g_OID_equal(gss_mech_spkm3, mech)) { #endif maj_stat = gss_display_name(&min_stat, gd->client_name, &gd->cname, &gd->sec.mech); #ifdef SPKM } #endif if (maj_stat != GSS_S_COMPLETE) { log_status("display_name", maj_stat, min_stat); goto errout; } #ifdef DEBUG #ifdef HAVE_HEIMDAL log_debug("accepted context for %.*s with " "<mech {}, qop %d, svc %d>", gd->cname.length, (char *)gd->cname.value, gd->sec.qop, gd->sec.svc); #else { gss_buffer_desc mechname; gss_oid_to_str(&min_stat, mech, &mechname); log_debug("accepted context for %.*s with " "<mech %.*s, qop %d, svc %d>", gd->cname.length, (char *)gd->cname.value, mechname.length, (char *)mechname.value, gd->sec.qop, gd->sec.svc); gss_release_buffer(&min_stat, &mechname); } #endif #endif /* DEBUG */ seq = htonl(gr->gr_win); seqbuf.value = &seq; seqbuf.length = sizeof(seq); gss_release_buffer(&min_stat, &gd->checksum); maj_stat = gss_sign(&min_stat, gd->ctx, GSS_C_QOP_DEFAULT, &seqbuf, &gd->checksum); if (maj_stat != GSS_S_COMPLETE) { goto errout; } rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; rqst->rq_xprt->xp_verf.oa_base = gd->checksum.value; rqst->rq_xprt->xp_verf.oa_length = gd->checksum.length; } return (TRUE); errout: gss_release_buffer(&min_stat, &gr->gr_token); return (FALSE); }
static bool_t svcauth_gss_accept_sec_context(struct svc_req *rqst, struct rpc_gss_init_res *gr) { struct svc_rpc_gss_data *gd; struct rpc_gss_cred *gc; gss_buffer_desc recv_tok, seqbuf, checksum; gss_OID mech; OM_uint32 maj_stat = 0, min_stat = 0, ret_flags, seq; log_debug("in svcauth_gss_accept_context()"); gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth); gc = (struct rpc_gss_cred *)rqst->rq_clntcred; memset(gr, 0, sizeof(*gr)); /* Deserialize arguments. */ memset(&recv_tok, 0, sizeof(recv_tok)); if (!svc_getargs(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_args, (caddr_t)&recv_tok)) return (FALSE); gr->gr_major = gss_accept_sec_context(&gr->gr_minor, &gd->ctx, _svcauth_gss_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &gd->client_name, &mech, &gr->gr_token, &ret_flags, NULL, NULL); if (gr->gr_major != GSS_S_COMPLETE && gr->gr_major != GSS_S_CONTINUE_NEEDED) { log_status("accept_sec_context", gr->gr_major, gr->gr_minor); gd->ctx = GSS_C_NO_CONTEXT; gss_release_buffer(&min_stat, &gr->gr_token); return (FALSE); } /* ANDROS: krb5 mechglue returns ctx of size 8 - two pointers, * one to the mechanism oid, one to the internal_ctx_id */ if ((gr->gr_ctx.value = mem_alloc(sizeof(gss_union_ctx_id_desc))) == NULL) { fprintf(stderr, "svcauth_gss_accept_context: out of memory\n"); return (FALSE); } memcpy(gr->gr_ctx.value, gd->ctx, sizeof(gss_union_ctx_id_desc)); gr->gr_ctx.length = sizeof(gss_union_ctx_id_desc); /* ANDROS: change for debugging linux kernel version... gr->gr_win = sizeof(gd->seqmask) * 8; */ gr->gr_win = 0x00000005; /* Save client info. */ gd->sec.mech = mech; gd->sec.qop = GSS_C_QOP_DEFAULT; gd->sec.svc = gc->gc_svc; gd->seq = gc->gc_seq; gd->win = gr->gr_win; if (gr->gr_major == GSS_S_COMPLETE) { maj_stat = gss_display_name(&min_stat, gd->client_name, &gd->cname, &gd->sec.mech); if (maj_stat != GSS_S_COMPLETE) { log_status("display_name", maj_stat, min_stat); return (FALSE); } #ifdef DEBUG #ifdef HAVE_KRB5 { gss_buffer_desc mechname; gss_oid_to_str(&min_stat, mech, &mechname); log_debug("accepted context for %.*s with " "<mech %.*s, qop %d, svc %d>", gd->cname.length, (char *)gd->cname.value, mechname.length, (char *)mechname.value, gd->sec.qop, gd->sec.svc); gss_release_buffer(&min_stat, &mechname); } #elif HAVE_HEIMDAL log_debug("accepted context for %.*s with " "<mech {}, qop %d, svc %d>", gd->cname.length, (char *)gd->cname.value, gd->sec.qop, gd->sec.svc); #endif #endif /* DEBUG */ seq = htonl(gr->gr_win); seqbuf.value = &seq; seqbuf.length = sizeof(seq); maj_stat = gss_sign(&min_stat, gd->ctx, GSS_C_QOP_DEFAULT, &seqbuf, &checksum); if (maj_stat != GSS_S_COMPLETE) return (FALSE); rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; rqst->rq_xprt->xp_verf.oa_base = checksum.value; rqst->rq_xprt->xp_verf.oa_length = checksum.length; } return (TRUE); }
int nfs_rpc_req2client_cred(struct svc_req *reqp, nfs_client_cred_t * pcred) { /* Structure for managing basic AUTH_UNIX authentication */ struct authunix_parms *aup = NULL; /* Stuff needed for managing RPCSEC_GSS */ #ifdef _HAVE_GSSAPI OM_uint32 maj_stat = 0; OM_uint32 min_stat = 0; struct svc_rpc_gss_data *gd = NULL; gss_buffer_desc oidbuff; #endif if(reqp == NULL || pcred == NULL) return -1; pcred->flavor = reqp->rq_cred.oa_flavor; pcred->length = reqp->rq_cred.oa_length; switch (reqp->rq_cred.oa_flavor) { case AUTH_NONE: /* Do nothing... because there seems like nothing is to be done... */ break; case AUTH_UNIX: aup = (struct authunix_parms *)(reqp->rq_clntcred); pcred->auth_union.auth_unix.aup_uid = aup->aup_uid; pcred->auth_union.auth_unix.aup_gid = aup->aup_gid; pcred->auth_union.auth_unix.aup_time = aup->aup_time; break; #ifdef _HAVE_GSSAPI case RPCSEC_GSS: /* Extract the information from the RPCSEC_GSS opaque structure */ gd = SVCAUTH_PRIVATE(reqp->rq_xprt->xp_auth); pcred->auth_union.auth_gss.svc = (unsigned int)(gd->sec.svc); pcred->auth_union.auth_gss.qop = (unsigned int)(gd->sec.qop); pcred->auth_union.auth_gss.gss_context_id = gd->ctx; strncpy(pcred->auth_union.auth_gss.cname, gd->cname.value, NFS_CLIENT_NAME_LEN); if((maj_stat = gss_oid_to_str(&min_stat, gd->sec.mech, &oidbuff)) != GSS_S_COMPLETE) { char errbuff[1024]; log_sperror_gss(errbuff, maj_stat, min_stat); LogCrit(COMPONENT_DISPATCH, "GSSAPI ERROR: %u|%u = %s", maj_stat, min_stat, errbuff); return -1; } strncpy(pcred->auth_union.auth_gss.stroid, oidbuff.value, NFS_CLIENT_NAME_LEN); /* Je fais le menage derriere moi */ (void)gss_release_buffer(&min_stat, &oidbuff); break; #endif default: /* Unsupported authentication flavour */ return -1; break; } return 1; } /* nfs_rpc_req2client_cred */
enum auth_stat gssrpc__svcauth_gss(struct svc_req *rqst, struct rpc_msg *msg, bool_t *no_dispatch) { enum auth_stat retstat; XDR xdrs; SVCAUTH *auth; struct svc_rpc_gss_data *gd; struct rpc_gss_cred *gc; struct rpc_gss_init_res gr; int call_stat, offset; OM_uint32 min_stat; log_debug("in svcauth_gss()"); /* Initialize reply. */ rqst->rq_xprt->xp_verf = gssrpc__null_auth; /* Allocate and set up server auth handle. */ if (rqst->rq_xprt->xp_auth == NULL || rqst->rq_xprt->xp_auth == &svc_auth_none) { if ((auth = calloc(sizeof(*auth), 1)) == NULL) { fprintf(stderr, "svcauth_gss: out_of_memory\n"); return (AUTH_FAILED); } if ((gd = calloc(sizeof(*gd), 1)) == NULL) { fprintf(stderr, "svcauth_gss: out_of_memory\n"); return (AUTH_FAILED); } auth->svc_ah_ops = &svc_auth_gss_ops; SVCAUTH_PRIVATE(auth) = gd; rqst->rq_xprt->xp_auth = auth; } else gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth); log_debug("xp_auth=%p, gd=%p", rqst->rq_xprt->xp_auth, gd); /* Deserialize client credentials. */ if (rqst->rq_cred.oa_length <= 0) return (AUTH_BADCRED); gc = (struct rpc_gss_cred *)rqst->rq_clntcred; memset(gc, 0, sizeof(*gc)); log_debug("calling xdrmem_create()"); log_debug("oa_base=%p, oa_length=%u", rqst->rq_cred.oa_base, rqst->rq_cred.oa_length); xdrmem_create(&xdrs, rqst->rq_cred.oa_base, rqst->rq_cred.oa_length, XDR_DECODE); log_debug("xdrmem_create() returned"); if (!xdr_rpc_gss_cred(&xdrs, gc)) { log_debug("xdr_rpc_gss_cred() failed"); XDR_DESTROY(&xdrs); return (AUTH_BADCRED); } XDR_DESTROY(&xdrs); retstat = AUTH_FAILED; #define ret_freegc(code) do { retstat = code; goto freegc; } while (0) /* Check version. */ if (gc->gc_v != RPCSEC_GSS_VERSION) ret_freegc (AUTH_BADCRED); /* Check RPCSEC_GSS service. */ if (gc->gc_svc != RPCSEC_GSS_SVC_NONE && gc->gc_svc != RPCSEC_GSS_SVC_INTEGRITY && gc->gc_svc != RPCSEC_GSS_SVC_PRIVACY) ret_freegc (AUTH_BADCRED); /* Check sequence number. */ if (gd->established) { if (gc->gc_seq > MAXSEQ) ret_freegc (RPCSEC_GSS_CTXPROBLEM); if ((offset = gd->seqlast - gc->gc_seq) < 0) { gd->seqlast = gc->gc_seq; offset = 0 - offset; gd->seqmask <<= offset; offset = 0; } else if ((u_int)offset >= gd->win || (gd->seqmask & (1 << offset))) { *no_dispatch = 1; ret_freegc (RPCSEC_GSS_CTXPROBLEM); } gd->seq = gc->gc_seq; gd->seqmask |= (1 << offset); } if (gd->established) { rqst->rq_clntname = (char *)gd->client_name; rqst->rq_svccred = (char *)gd->ctx; } /* Handle RPCSEC_GSS control procedure. */ switch (gc->gc_proc) { case RPCSEC_GSS_INIT: case RPCSEC_GSS_CONTINUE_INIT: if (rqst->rq_proc != NULLPROC) ret_freegc (AUTH_FAILED); /* XXX ? */ if (!svcauth_gss_acquire_cred()) ret_freegc (AUTH_FAILED); if (!svcauth_gss_accept_sec_context(rqst, &gr)) ret_freegc (AUTH_REJECTEDCRED); if (!svcauth_gss_nextverf(rqst, htonl(gr.gr_win))) { gss_release_buffer(&min_stat, &gr.gr_token); ret_freegc (AUTH_FAILED); } *no_dispatch = TRUE; call_stat = svc_sendreply(rqst->rq_xprt, xdr_rpc_gss_init_res, (caddr_t)&gr); gss_release_buffer(&min_stat, &gr.gr_token); gss_release_buffer(&min_stat, &gd->checksum); if (!call_stat) ret_freegc (AUTH_FAILED); if (gr.gr_major == GSS_S_COMPLETE) gd->established = TRUE; break; case RPCSEC_GSS_DATA: if (!svcauth_gss_validate(rqst, gd, msg)) ret_freegc (RPCSEC_GSS_CREDPROBLEM); if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq))) ret_freegc (AUTH_FAILED); break; case RPCSEC_GSS_DESTROY: if (rqst->rq_proc != NULLPROC) ret_freegc (AUTH_FAILED); /* XXX ? */ if (!svcauth_gss_validate(rqst, gd, msg)) ret_freegc (RPCSEC_GSS_CREDPROBLEM); if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq))) ret_freegc (AUTH_FAILED); *no_dispatch = TRUE; call_stat = svc_sendreply(rqst->rq_xprt, xdr_void, (caddr_t)NULL); log_debug("sendreply in destroy: %d", call_stat); if (!svcauth_gss_release_cred()) ret_freegc (AUTH_FAILED); SVCAUTH_DESTROY(rqst->rq_xprt->xp_auth); rqst->rq_xprt->xp_auth = &svc_auth_none; break; default: ret_freegc (AUTH_REJECTEDCRED); break; } retstat = AUTH_OK; freegc: xdr_free(xdr_rpc_gss_cred, gc); log_debug("returning %d from svcauth_gss()", retstat); return (retstat); }