#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
+#include <sys/fsuid.h>
#include <stdio.h>
#include <stdlib.h>
* with an index into pollarray[], and other basic data about that client.
*
* Directory structure: created by the kernel nfs client
- * /pipefsdir/clntXX : one per rpc_clnt struct in the kernel
- * /pipefsdir/clntXX/krb5 : read uid for which kernel wants
- * a context, write the resulting context
- * /pipefsdir/clntXX/info : stores info such as server name
+ * {pipefs_nfsdir}/clntXX : one per rpc_clnt struct in the kernel
+ * {pipefs_nfsdir}/clntXX/krb5 : read uid for which kernel wants
+ * a context, write the resulting context
+ * {pipefs_nfsdir}/clntXX/info : stores info such as server name
*
* Algorithm:
- * Poll all /pipefsdir/clntXX/krb5 files. When ready, data read
+ * Poll all {pipefs_nfsdir}/clntXX/krb5 files. When ready, data read
* is a uid; performs rpcsec_gss context initialization protocol to
* get a cred for that user. Writes result to corresponding krb5 file
* in a form the kernel code will understand.
* In addition, we make sure we are notified whenever anything is
- * created or destroyed in pipefsdir/ or in an of the clntXX directories,
- * and rescan the whole pipefsdir when this happens.
+ * created or destroyed in {pipefs_nfsdir} or in an of the clntXX directories,
+ * and rescan the whole {pipefs_nfsdir} when this happens.
*/
struct pollfd * pollarray;
goto fail;
close(fd);
- numfields = sscanf(buf,"RPC server: %s\n"
- "service: %s %s version %s\n"
- "address: %s\n"
- "protocol: %s\n",
+ numfields = sscanf(buf,"RPC server: %127s\n"
+ "service: %127s %15s version %15s\n"
+ "address: %127s\n"
+ "protocol: %15s\n",
dummy,
service, program, version,
address,
struct dirent **namelist;
int i, j;
- if (chdir(pipefsdir) < 0) {
+ if (chdir(pipefs_nfsdir) < 0) {
printerr(0, "ERROR: can't chdir to %s: %s\n",
- pipefsdir, strerror(errno));
+ pipefs_nfsdir, strerror(errno));
return -1;
}
- j = scandir(pipefsdir, &namelist, NULL, alphasort);
+ j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort);
if (j < 0) {
printerr(0, "ERROR: can't scandir %s: %s\n",
- pipefsdir, strerror(errno));
+ pipefs_nfsdir, strerror(errno));
return -1;
}
update_old_clients(namelist, j);
* gss context with a server.
*/
int create_auth_rpc_client(struct clnt_info *clp,
+ CLIENT **clnt_return,
AUTH **auth_return,
uid_t uid,
int authtype)
char service[64];
char *at_sign;
+ /* Create the context as the user (not as root) */
+ save_uid = geteuid();
+ if (setfsuid(uid) != 0) {
+ printerr(0, "WARNING: Failed to setfsuid for "
+ "user with uid %d\n", uid);
+ goto out_fail;
+ }
+ printerr(2, "creating context using fsuid %d (save_uid %d)\n",
+ uid, save_uid);
+
sec.qop = GSS_C_QOP_DEFAULT;
sec.svc = RPCSEC_GSS_SVC_NONE;
sec.cred = GSS_C_NO_CREDENTIAL;
#endif
}
- /* Create the context as the user (not as root) */
- save_uid = geteuid();
- if (seteuid(uid) != 0) {
- printerr(0, "WARNING: Failed to seteuid for "
- "user with uid %d\n", uid);
- goto out_fail;
- }
- printerr(2, "creating context using euid %d (save_uid %d)\n",
- geteuid(), save_uid);
-
/* create an rpc connection to the nfs server */
printerr(2, "creating %s client for server %s\n", clp->protocol,
ai_hints.ai_protocol = IPPROTO_UDP;
} else {
printerr(0, "WARNING: unrecognized protocol, '%s', requested "
- "for connection to server %s for user with uid %d",
+ "for connection to server %s for user with uid %d\n",
clp->protocol, clp->servername, uid);
goto out_fail;
}
/* extract the service name from clp->servicename */
if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
printerr(0, "WARNING: servicename (%s) not formatted as "
- "expected with service@host", clp->servicename);
+ "expected with service@host\n", clp->servicename);
goto out_fail;
}
if ((at_sign - clp->servicename) >= sizeof(service)) {
printerr(0, "WARNING: service portion of servicename (%s) "
- "is too long!", clp->servicename);
+ "is too long!\n", clp->servicename);
goto out_fail;
}
strncpy(service, clp->servicename, at_sign - clp->servicename);
errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
if (errcode) {
printerr(0, "WARNING: Error from getaddrinfo for server "
- "'%s': %s", clp->servername, gai_strerror(errcode));
+ "'%s': %s\n", clp->servername, gai_strerror(errcode));
goto out_fail;
}
if (a == NULL) {
printerr(0, "WARNING: No address information found for "
- "connection to server %s for user with uid %d",
+ "connection to server %s for user with uid %d\n",
clp->servername, uid);
goto out_fail;
}
} else {
/* Shouldn't happen! */
printerr(0, "ERROR: requested protocol '%s', but "
- "got addrinfo with protocol %d",
+ "got addrinfo with protocol %d\n",
clp->protocol, a->ai_protocol);
goto out_fail;
}
goto out_fail;
}
- /* Restore euid to original value */
- if (seteuid(save_uid) != 0) {
- printerr(0, "WARNING: Failed to restore euid"
- " to uid %d\n", save_uid);
- goto out_fail;
- }
- save_uid = -1;
-
/* Success !!! */
+ rpc_clnt->cl_auth = auth;
+ *clnt_return = rpc_clnt;
*auth_return = auth;
retval = 0;
- out_fail:
- if ((save_uid != -1) && (seteuid(save_uid) != 0)) {
- printerr(0, "WARNING: Failed to restore euid"
- " to uid %d (in error path)\n", save_uid);
- }
+ out:
if (sec.cred != GSS_C_NO_CREDENTIAL)
gss_release_cred(&min_stat, &sec.cred);
- if (rpc_clnt) clnt_destroy(rpc_clnt);
if (a != NULL) freeaddrinfo(a);
-
+ /* Restore euid to original value */
+ if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
+ printerr(0, "WARNING: Failed to restore fsuid"
+ " to uid %d from %d\n", save_uid, uid);
+ }
return retval;
+
+ out_fail:
+ /* Only destroy here if failure. Otherwise, caller is responsible */
+ if (rpc_clnt) clnt_destroy(rpc_clnt);
+
+ goto out;
}
handle_krb5_upcall(struct clnt_info *clp)
{
uid_t uid;
- AUTH *auth;
+ CLIENT *rpc_clnt = NULL;
+ AUTH *auth = NULL;
struct authgss_private_data pd;
gss_buffer_desc token;
char **credlist = NULL;
char **ccname;
+ int create_resp = -1;
printerr(1, "handling krb5 upcall\n");
token.length = 0;
token.value = NULL;
+ memset(&pd, 0, sizeof(struct authgss_private_data));
if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
printerr(0, "WARNING: failed reading uid from krb5 "
goto out;
}
- if (uid == 0) {
- int success = 0;
-
- /*
- * Get a list of credential cache names and try each
- * of them until one works or we've tried them all
- */
- if (gssd_get_krb5_machine_cred_list(&credlist)) {
- printerr(0, "WARNING: Failed to obtain machine "
- "credentials for connection to "
- "server %s\n", clp->servername);
- goto out_return_error;
- }
- for (ccname = credlist; ccname && *ccname; ccname++) {
- gssd_setup_krb5_machine_gss_ccache(*ccname);
- if ((create_auth_rpc_client(clp, &auth, uid,
- AUTHTYPE_KRB5)) == 0) {
- /* Success! */
- success++;
- break;
- }
- printerr(2, "WARNING: Failed to create krb5 context "
- "for user with uid %d with credentials "
- "cache %s for server %s\n",
- uid, *ccname, clp->servername);
- }
- gssd_free_krb5_machine_cred_list(credlist);
- if (!success) {
- printerr(0, "WARNING: Failed to create krb5 context "
- "for user with uid %d with any "
- "credentials cache for server %s\n",
- uid, clp->servername);
- goto out_return_error;
- }
- }
- else {
+ if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) {
/* Tell krb5 gss which credentials cache to use */
gssd_setup_krb5_user_gss_ccache(uid, clp->servername);
- if (create_auth_rpc_client(clp, &auth, uid, AUTHTYPE_KRB5)) {
+ create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
+ AUTHTYPE_KRB5);
+ }
+ if (create_resp != 0) {
+ if (uid == 0 && root_uses_machine_creds == 1) {
+ int success = 0;
+
+ gssd_refresh_krb5_machine_credential(clp->servername,
+ NULL);
+ /*
+ * Get a list of credential cache names and try each
+ * of them until one works or we've tried them all
+ */
+ if (gssd_get_krb5_machine_cred_list(&credlist)) {
+ printerr(0, "ERROR: No credentials found "
+ "for connection to server %s\n",
+ clp->servername);
+ goto out_return_error;
+ }
+ for (ccname = credlist; ccname && *ccname; ccname++) {
+ gssd_setup_krb5_machine_gss_ccache(*ccname);
+ if ((create_auth_rpc_client(clp, &rpc_clnt,
+ &auth, uid,
+ AUTHTYPE_KRB5)) == 0) {
+ /* Success! */
+ success++;
+ break;
+ }
+ printerr(2, "WARNING: Failed to create krb5 context "
+ "for user with uid %d with credentials "
+ "cache %s for server %s\n",
+ uid, *ccname, clp->servername);
+ }
+ gssd_free_krb5_machine_cred_list(credlist);
+ if (!success) {
+ printerr(0, "WARNING: Failed to create krb5 context "
+ "for user with uid %d with any "
+ "credentials cache for server %s\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+ } else {
printerr(0, "WARNING: Failed to create krb5 context "
- "for user with uid %d for server %s\n",
+ "for user with uid %d for server %s\n",
uid, clp->servername);
goto out_return_error;
}
do_downcall(clp->krb5_fd, uid, &pd, &token);
+out:
if (token.value)
free(token.value);
-out:
+ if (pd.pd_ctx_hndl.length != 0)
+ authgss_free_private_data(&pd);
+ if (auth)
+ AUTH_DESTROY(auth);
+ if (rpc_clnt)
+ clnt_destroy(rpc_clnt);
return;
out_return_error:
do_error_downcall(clp->krb5_fd, uid, -1);
- return;
+ goto out;
}
/*
handle_spkm3_upcall(struct clnt_info *clp)
{
uid_t uid;
- AUTH *auth;
+ CLIENT *rpc_clnt = NULL;
+ AUTH *auth = NULL;
struct authgss_private_data pd;
gss_buffer_desc token;
goto out;
}
- if (create_auth_rpc_client(clp, &auth, uid, AUTHTYPE_SPKM3)) {
+ if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
printerr(0, "WARNING: Failed to create spkm3 context for "
"user with uid %d\n", uid);
goto out_return_error;
do_downcall(clp->spkm3_fd, uid, &pd, &token);
+out:
if (token.value)
free(token.value);
-out:
+ if (auth)
+ AUTH_DESTROY(auth);
+ if (rpc_clnt)
+ clnt_destroy(rpc_clnt);
return;
out_return_error:
do_error_downcall(clp->spkm3_fd, uid, -1);
- return;
+ goto out;
}