예제 #1
0
static Npuser *
_alloc_user (Npsrv *srv, struct passwd *pwd)
{
    Npuser *u;

    if (!(u = malloc (sizeof (*u)))) {
        np_uerror (ENOMEM);
        np_logerr (srv, "_alloc_user: %s", pwd->pw_name);
        goto error;
    }
    u->sg = NULL;
    u->nsg = 0;
    if (!(u->uname = strdup (pwd->pw_name))) {
        np_uerror (ENOMEM);
        np_logerr (srv, "_alloc_user: %s", pwd->pw_name);
        goto error;
    }
    u->uid = pwd->pw_uid;
    u->gid = pwd->pw_gid;
    if (u->uid != 0 && _getgrouplist(srv, u) < 0)
        goto error;
    pthread_mutex_init (&u->lock, NULL);
    u->refcount = 0;
    u->t = time (NULL);
    u->next = NULL;
    if (srv->flags & SRV_FLAGS_DEBUG_USER)
        np_logmsg (srv, "user lookup: %d", u->uid);
    return u;
error:
    if (u)
        _free_user (u);
    return NULL;
}
예제 #2
0
/* This needs to be called with the usercache lock held.
 * I don't think it's thread safe. -jg
 */
static int
_getgrouplist (Npsrv *srv, Npuser *u)
{
    int i, ret = -1;
    gid_t *sgcpy;

    u->nsg = sysconf(_SC_NGROUPS_MAX);
    if (u->nsg < 65536)
        u->nsg = 65536;
    if (!(u->sg = malloc (u->nsg * sizeof (gid_t)))) {
        np_uerror (ENOMEM);
        np_logerr (srv, "_alloc_user: %s", u->uname);
        goto done;
    }
    if (getgrouplist(u->uname, u->gid, u->sg, &u->nsg) == -1) {
        np_logerr (srv, "_alloc_user: %s: getgrouplist", u->uname);
        if (np_rerror () == 0)
            np_uerror (EPERM);
        goto done;
    }
    if ((sgcpy = malloc (u->nsg * sizeof (gid_t)))) {
        for (i = 0; i < u->nsg; i++)
            sgcpy[i] = u->sg[i];
        free (u->sg);
        u->sg = sgcpy;
    }
    ret = 0;
done:
    return ret;
}
예제 #3
0
Npfcall *
np_auth(Npreq *req, Npfcall *tc)
{
	Npconn *conn = req->conn;
	Npsrv *srv = conn->srv;
	Npfid *afid = req->fid;
	Npfcall *rc = NULL;
	Npqid aqid;
	char a[128];
	int auth_required = _authrequired(srv, &tc->u.tauth.uname,
					       tc->u.tauth.n_uname,
					       &tc->u.tauth.aname);

	if (tc->u.tauth.n_uname != P9_NONUNAME) {
		snprintf (a, sizeof(a), "auth(%d@%s:%.*s)",
			  tc->u.tauth.n_uname,
			  np_conn_get_client_id (conn), 
			  tc->u.tauth.aname.len, tc->u.tauth.aname.str);
	} else {
		snprintf (a, sizeof(a), "auth(%.*s@%s:%.*s)",
			  tc->u.tauth.uname.len, tc->u.tauth.uname.str,
			  np_conn_get_client_id (conn),
			  tc->u.tauth.aname.len, tc->u.tauth.aname.str);
	}
	if (!auth_required) {
		if (!(rc = np_create_rlerror(0))) {
			np_uerror(ENOMEM);
			np_logerr (srv, "%s: creating response", a);
		}
		goto error;
	}
	if (!afid) {
		np_uerror (EIO);
		np_logerr (srv, "%s: invalid afid (%d)", a, tc->u.tauth.afid);
		goto error;
	}
	np_fid_incref(afid);
	if (!(afid->user = np_attach2user (srv, &tc->u.tauth.uname,
				     		 tc->u.tauth.n_uname))) {
		np_logerr (srv, "%s: user lookup", a);
		goto error;
	}
	afid->type = P9_QTAUTH;
	if (!srv->auth->startauth(afid, afid->aname, &aqid)) {
		np_logerr (srv, "%s: startauth", a);
		goto error;
	}
	assert((aqid.type & P9_QTAUTH));
	if (!(rc = np_create_rauth(&aqid))) {
		np_uerror(ENOMEM);
		np_logerr (srv, "%s: creating response", a);
		goto error;
	}
error:
	return rc;
}
예제 #4
0
static void
np_tpool_destroy(Nptpool *tp)
{
	Npsrv *srv = tp->srv;
	Npwthread *wt, *next;
	void *retval;
	int err, i;

	for(wt = tp->wthreads; wt != NULL; wt = wt->next) {
		wt->shutdown = 1;
	}
	xpthread_cond_broadcast(&tp->reqcond);
	for (i = 0, wt = tp->wthreads; wt != NULL; wt = next, i++) {
		next = wt->next;
		if ((err = pthread_join (wt->thread, &retval))) {
			np_uerror (err);
			np_logerr(srv, "%s: join thread %d", tp->name, i);
		} else if (retval == PTHREAD_CANCELED) {
			np_logmsg(srv, "%s: join thread %d: cancelled",
					tp->name, i);
		} else if (retval != NULL) {
			np_logmsg(srv, "%s: join thread %d: non-NULL return",
					tp->name, i);
		}
		free (wt);
	}
	pthread_cond_destroy (&tp->reqcond);
	pthread_mutex_destroy (&tp->lock);
	if (tp->name)
		free (tp->name);
	free (tp);
}
예제 #5
0
파일: srv.c 프로젝트: lowfatcomputing/diod
void
np_tpool_select (Npreq *req)
{
	Npsrv *srv = req->conn->srv;
	Nptpool *tp;

	if ((srv->flags & SRV_FLAGS_TPOOL_SINGLE))
		return;
	if (!req->fid || !req->fid->aname || *req->fid->aname != '/')
		return;
	if (req->fid->tpool)
		return;

	xpthread_mutex_lock (&srv->lock);
	for (tp = srv->tpool; tp != NULL; tp = tp->next) {
		if (!strcmp (req->fid->aname, tp->name))
			break;
	}
	if (!tp) {
		tp = np_tpool_create(srv, req->fid->aname);
		if (tp) {
			assert (srv->tpool); /* default tpool */
			tp->next = srv->tpool->next;
			srv->tpool->next = tp;
		} else
			np_logerr (srv, "np_tpool_create %s", req->fid->aname);
	}
	if (tp) {
		np_tpool_incref (tp);
		req->fid->tpool = tp;
	}
	xpthread_mutex_unlock (&srv->lock);
}
예제 #6
0
/* Create an Npuser struct for a user, without requiring
 * that user to be in the password/group file.
 * N.B. gid is assumed to be same as uid.
 */
static Npuser *
_alloc_nouserdb (Npsrv *srv, uid_t uid, char *name)
{
    Npuser *u = NULL;
    char ustr[32] = "root";

    if (name) {
        if (strcmp (name, "root") != 0) {
            np_uerror (EPERM);
            goto error;
        }
        uid = 0;
    }
    if (uid != 0)
        snprintf (ustr, sizeof (ustr), "%d", uid);
    if (!(u = malloc (sizeof (*u)))) {
        np_uerror (ENOMEM);
        np_logerr (srv, "_alloc_nouserdb: %s", ustr);
        goto error;
    }
    u->sg = NULL;
    if (!(u->uname = strdup (ustr))) {
        np_uerror (ENOMEM);
        np_logerr (srv, "_alloc_nouserdb: %s", ustr);
        goto error;
    }
    u->uid = uid;
    u->gid = (gid_t)uid;
    u->nsg = 1;
    if (!(u->sg = malloc (sizeof (gid_t) * u->nsg))) {
        np_uerror (ENOMEM);
        np_logerr (srv, "_alloc_nouserdb: %s", ustr);
        goto error;
    }
    u->sg[0] = u->gid;
    pthread_mutex_init (&u->lock, NULL);
    if (srv->flags & SRV_FLAGS_DEBUG_USER)
        np_logmsg (srv, "user lookup: %d", u->uid);
    u->refcount = 0;
    u->t = time (NULL);
    u->next = NULL;
    return u;
error:
    if (u)
        _free_user (u);
    return NULL;
}
예제 #7
0
/* When handling requests on connections authenticated as root, we consider
 * it safe to disable DAC checks on the server and presume the client is
 * doing it.  This is only done if the server sets SRV_FLAGS_DAC_BYPASS.
 */
static int
_chg_privcap (Npsrv *srv, cap_flag_value_t val)
{
    cap_t cap;
    cap_flag_value_t cur;
    cap_value_t cf[] = { CAP_DAC_OVERRIDE, CAP_CHOWN, CAP_FOWNER };
    int need_set = 0;
    int i, ret = -1;

    if (!(cap = cap_get_proc ())) {
        np_uerror (errno);
        np_logerr (srv, "cap_get_proc failed");
        goto done;
    }
    for (i = 0; i < sizeof(cf) / sizeof(cf[0]); i++) {
        if (cap_get_flag (cap, cf[i], CAP_EFFECTIVE, &cur) < 0) {
            np_uerror (errno);
            np_logerr (srv, "cap_get_flag failed");
            goto done;
        }
        if (cur == val)
            continue;
        need_set = 1;
        if (cap_set_flag (cap, CAP_EFFECTIVE, 1, &cf[i], val) < 0) {
            np_uerror (errno);
            np_logerr (srv, "cap_set_flag failed");
            goto done;
        }
    }
    if (need_set && cap_set_proc (cap) < 0) {
        np_uerror (errno);
        np_logerr (srv, "cap_set_proc failed");
        goto done;
    }
    ret = 0;
done:
    if (cap != NULL && cap_free (cap) < 0) {
        np_uerror (errno);
        np_logerr (srv, "cap_free failed");
    }
    return ret;
}
예제 #8
0
static Npuser *
_real_lookup_byname (Npsrv *srv, char *uname)
{
    Npuser *u;
    int err, len;
    struct passwd pw, *pwd = NULL;
    char *buf = NULL;

    if (srv->flags & SRV_FLAGS_NOUSERDB) {
        if (!(u = _alloc_nouserdb (srv, P9_NONUNAME, uname)))
            goto error;
    } else {
        len= sysconf(_SC_GETPW_R_SIZE_MAX);
        if (len < 4096)
            len = 4096;
        if (!(buf = malloc (len))) {
            np_uerror (ENOMEM);
            np_logerr (srv, "uname2user: %s", uname);
            goto error;
        }
        if ((err = getpwnam_r (uname, &pw, buf, len, &pwd)) != 0) {
            np_uerror (err);
            np_logerr (srv, "uname2user: %s: getpwnam_r", uname);
            goto error;
        }
        if (!pwd) {
            np_logmsg (srv, "uname2user: %s lookup failure", uname);
            np_uerror (EPERM);
            goto error;
        }
        if (!(u = _alloc_user (srv, pwd)))
            goto error;
        free (buf);
    }
    return u;
error:
    if (buf)
        free (buf);
    return NULL;
}
예제 #9
0
static Npuser *
_real_lookup_byuid (Npsrv *srv, uid_t uid)
{
    Npuser *u;
    int err, len;
    struct passwd pw, *pwd;
    char *buf = NULL;

    if (srv->flags & SRV_FLAGS_NOUSERDB) {
        if (!(u = _alloc_nouserdb (srv, uid, NULL)))
            goto error;
    } else {
        len = sysconf(_SC_GETPW_R_SIZE_MAX);
        if (len < 4096)
            len = 4096;
        if (!(buf = malloc (len))) {
            np_uerror (ENOMEM);
            np_logerr (srv, "uid2user");
            goto error;
        }
        if ((err = getpwuid_r (uid, &pw, buf, len, &pwd)) != 0) {
            np_uerror (err);
            np_logerr (srv, "uid2user: unable to lookup %d", uid);
            goto error;
        }
        if (!pwd) {
            np_logmsg (srv, "uid2user: unable to lookup %d", uid);
            np_uerror (EPERM);
            goto error;
        }
        if (!(u = _alloc_user (srv, pwd)))
            goto error;
        free (buf);
    }
    return u;
error:
    if (buf)
        free (buf);
    return NULL;
}
예제 #10
0
파일: conn.c 프로젝트: EuroCorp/diod
void
np_conn_respond(Npreq *req)
{
    int n;
    Npconn *conn = req->conn;
    Npsrv *srv = conn->srv;
    Npfcall *rc = req->rcall;

    _debug_trace (srv, rc);
    xpthread_mutex_lock(&conn->wlock);
    n = np_trans_send(conn->trans, rc);
    xpthread_mutex_unlock(&conn->wlock);
    if (n < 0)
        np_logerr (srv, "send to '%s'", conn->client_id);
}
예제 #11
0
파일: conn.c 프로젝트: lowfatcomputing/diod
/* Per-connection read thread.
 */
static void *
np_conn_read_proc(void *a)
{
	int i, n, size;
	Npsrv *srv;
	Npconn *conn = (Npconn *)a;
	Nptrans *trans;
	Npreq *req;
	Npfcall *fc, *fc1;

	pthread_detach(pthread_self());
	np_conn_incref(conn);
	srv = conn->srv;
	fc = _alloc_npfcall(conn->msize);
	n = 0;
	while (fc && conn->trans && (i = np_trans_read(conn->trans, fc->pkt + n, conn->msize - n)) > 0) {
		n += i;
again:
		size = np_peek_size (fc->pkt, n);
		if (size == 0 || n < size)
			continue;

		/* Corruption on the transport, unhandled op, etc.
		 * is fatal to the connection.  We could consider returning
		 * an error to the client here.   However, various kernels
		 * may not handle that well, depending on where it happens.
		 */
		if (!np_deserialize(fc, fc->pkt)) {
			_debug_trace (srv, fc);
			np_logerr (srv, "protocol error - "
				   "dropping connection to '%s'",
				   conn->client_id);
			break;
		}
		if ((srv->flags & SRV_FLAGS_DEBUG_9PTRACE))
			_debug_trace (srv, fc);

		/* Replace fc, and copy any data past the current packet
		 * to the replacement.
		 */
		fc1 = _alloc_npfcall(conn->msize);
		if (!fc1) {
			np_logerr (srv, "out of memory in receive path - "
				   "dropping connection to '%s'",
				   conn->client_id);
			break;
		}
		if (n > size)
			memmove(fc1->pkt, fc->pkt + size, n - size);
		n -= size;

		/* Encapsulate fc in a request and hand to srv worker threads.
		 * In np_req_alloc, req->fid is looked up/initialized.
		 */
		req = np_req_alloc(conn, fc);
		if (!req) {
			np_logerr (srv, "out of memory in receive path - "
				   "dropping connection to '%s'",
				   conn->client_id);
			break;
		}
		np_srv_add_req(srv, req);
		xpthread_mutex_lock(&conn->lock);
		conn->reqs_in++;
		xpthread_mutex_unlock(&conn->lock);
		fc = fc1;
		if (n > 0)
			goto again;

	}
	/* Just got EOF on read, or some other fatal error for the
	 * connection like out of memory.
	 */

	xpthread_mutex_lock(&conn->lock);
	trans = conn->trans;
	conn->trans = NULL;
	if (fc)
		_free_npfcall(fc);
	xpthread_mutex_unlock(&conn->lock);

	np_srv_remove_conn(conn->srv, conn);
	np_conn_reset(conn);

	if (trans)
		np_trans_destroy(trans);

	np_conn_decref(conn);
	return NULL;
}
예제 #12
0
/* Note: it is possible for setfsuid/setfsgid to fail silently,
 * e.g. if user doesn't have CAP_SETUID/CAP_SETGID.
 * That should be checked at server startup.
 */
int
np_setfsid (Npreq *req, Npuser *u, u32 gid_override)
{
    Npwthread *wt = req->wthread;
    Npsrv *srv = req->conn->srv;
    int i, n, ret = -1;
    u32 gid;
    uid_t authuid;
    int dumpable = prctl (PR_GET_DUMPABLE, 0, 0, 0, 0);
    int dumpclrd = 0;

    if (np_conn_get_authuser(req->conn, &authuid) < 0)
        authuid = P9_NONUNAME;

    if ((srv->flags & SRV_FLAGS_SETFSID)) {
        /* gid_override must be one of user's suppl. groups unless
         * connection was originally authed as root (trusted).
         */
        if (gid_override != -1 && u->uid != 0 && authuid != 0
                && !(srv->flags & SRV_FLAGS_NOUSERDB)) {
            for (i = 0; i < u->nsg; i++) {
                if (u->sg[i] == gid_override)
                    break;
            }
            if (i == u->nsg) {
                np_uerror (EPERM);
                np_logerr (srv, "np_setfsid(%s): gid_override "
                           "%d not in user's sg list",
                           u->uname, gid_override);
                goto done;
            }
        }
        gid = (gid_override == -1 ? u->gid : gid_override);
        if (wt->fsgid != gid) {
            dumpclrd = 1;
            if ((n = setfsgid (gid)) < 0) {
                np_uerror (errno);
                np_logerr (srv, "setfsgid(%s) gid=%d failed",
                           u->uname, gid);
                wt->fsgid = P9_NONUNAME;
                goto done;
            }
            if (n != wt->fsgid) {
                np_uerror (errno);
                np_logerr (srv, "setfsgid(%s) gid=%d failed"
                           "returned %d, expected %d",
                           u->uname, gid, n, wt->fsgid);
                wt->fsgid = P9_NONUNAME;
                goto done;
            }
            wt->fsgid = gid;
        }
        if (wt->fsuid != u->uid) {
            dumpclrd = 1;
            if ((n = setfsuid (u->uid)) < 0) {
                np_uerror (errno);
                np_logerr (srv, "setfsuid(%s) uid=%d failed",
                           u->uname, u->uid);
                wt->fsuid = P9_NONUNAME;
                goto done;
            }
            if (n != wt->fsuid) {
                np_uerror (EPERM);
                np_logerr (srv, "setfsuid(%s) uid=%d failed: "
                           "returned %d, expected %d",
                           u->uname, u->uid, n, wt->fsuid);
                wt->fsuid = P9_NONUNAME;
                goto done;
            }
            /* Track CAP side effects of setfsuid.
             */
            if (u->uid == 0)
                wt->privcap = 1; /* transiton to 0 sets caps */
            else if (wt->fsuid == 0)
                wt->privcap = 0; /* trans from 0 clears caps */

            /* Suppl groups need to be part of cred for NFS
             * forwarding even with DAC_BYPASS.  However only
             * do it if kernel treats sg's per-thread not process.
             * Addendum: late model glibc attempts to make this
             * per-process, so for now bypass glibc. See issue 53.
             */
            if ((srv->flags & SRV_FLAGS_SETGROUPS)) {
                if (syscall(SYS_setgroups, u->nsg, u->sg) < 0) {
                    np_uerror (errno);
                    np_logerr (srv, "setgroups(%s) nsg=%d failed",
                               u->uname, u->nsg);
                    wt->fsuid = P9_NONUNAME;
                    goto done;
                }
            }
            wt->fsuid = u->uid;
        }
    }
#if HAVE_LIBCAP
    if ((srv->flags & SRV_FLAGS_DAC_BYPASS) && wt->fsuid != 0) {
        if (!wt->privcap && authuid == 0) {
            if (_chg_privcap (srv, CAP_SET) < 0)
                goto done;
            wt->privcap = 1;
            dumpclrd = 1;
        } else if (wt->privcap && authuid != 0) {
            if (_chg_privcap (srv, CAP_CLEAR) < 0)
                goto done;
            wt->privcap = 0;
            dumpclrd = 1;
        }
    }
#endif
    ret = 0;
done:
    if (dumpable && dumpclrd && prctl (PR_SET_DUMPABLE, 1, 0, 0, 0) < 0)
        np_logerr (srv, "prctl PR_SET_DUMPABLE failed");
    return ret;
}
예제 #13
0
파일: conn.c 프로젝트: EuroCorp/diod
/* Per-connection read thread.
 */
static void *
np_conn_read_proc(void *a)
{
    Npconn *conn = (Npconn *)a;
    Npsrv *srv = conn->srv;
    Npreq *req;
    Npfcall *fc;

    pthread_detach(pthread_self());

    for (;;) {
        if (np_trans_recv(conn->trans, &fc, conn->msize) < 0) {
            np_logerr (srv, "recv error - "
                       "dropping connection to '%s'",
                       conn->client_id);
            break;
        }
        if (!fc) /* EOF */
            break;
        _debug_trace (srv, fc);

        /* Encapsulate fc in a request and hand to srv worker threads.
         * In np_req_alloc, req->fid is looked up/initialized.
         */
        req = np_req_alloc(conn, fc);
        if (!req) {
            np_logmsg (srv, "out of memory in receive path - "
                       "dropping connection to '%s'",
                       conn->client_id);
            free (fc);
            break;
        }

        /* Enqueue request for processing by next available worker
         * thread, except P9_TFLUSH which is handled immediately.
         */
        if (fc->type == P9_TFLUSH) {
            if (np_flush (req, fc)) {
                np_req_respond_flush (req);
                np_req_unref(req);
            }
            xpthread_mutex_lock (&srv->lock);
            srv->tpool->stats.nreqs[P9_TFLUSH]++;
            xpthread_mutex_unlock (&srv->lock);
        } else {
            xpthread_mutex_lock(&srv->lock);
            np_srv_add_req(srv, req);
            xpthread_mutex_unlock(&srv->lock);
        }
    }
    /* Just got EOF on read, or some other fatal error for the
     * connection like out of memory.
     */

    np_conn_flush (conn);

    xpthread_mutex_lock(&conn->lock);
    while (conn->refcount > 0)
        xpthread_cond_wait(&conn->refcond, &conn->lock);
    xpthread_mutex_unlock(&conn->lock);
    np_conn_destroy(conn);

    return NULL;
}
예제 #14
0
Npfcall *
np_attach(Npreq *req, Npfcall *tc)
{
	Npconn *conn = req->conn;
	Npsrv *srv = conn->srv;
	Npfid *fid = req->fid;
	Npfid *afid = NULL;
	Npfcall *rc = NULL;
	char a[128];
	int auth_required = _authrequired(srv, &tc->u.tattach.uname,
					       tc->u.tattach.n_uname,
					       &tc->u.tattach.aname);

	if (tc->u.tattach.n_uname != P9_NONUNAME) {
		snprintf (a, sizeof(a), "attach(%d@%s:%.*s)",
			  tc->u.tattach.n_uname,
			  np_conn_get_client_id (conn), 
			  tc->u.tattach.aname.len, tc->u.tattach.aname.str);
	} else {
		snprintf (a, sizeof(a), "attach(%.*s@%s:%.*s)",
			  tc->u.tattach.uname.len, tc->u.tattach.uname.str,
			  np_conn_get_client_id (conn),
			  tc->u.tattach.aname.len, tc->u.tattach.aname.str);
	}
	if (!fid) {
		np_uerror (EIO);
		np_logerr (srv, "%s: invalid fid (%d)", a, tc->u.tattach.fid);
		goto error;
	}
	if (tc->u.tattach.afid != P9_NOFID) {
		if (!(afid = np_fid_find(conn, tc->u.tattach.afid))) {
			np_uerror(EPERM);
			np_logerr (srv, "%s: invalid afid (%d)", a,
				   tc->u.tattach.afid);
			goto error;
		}
		np_fid_incref(afid);
		if (!(afid->type & P9_QTAUTH)) {
			np_uerror(EPERM);
			np_logerr (srv, "%s: invalid afid type", a);
			goto error;
		}
	}
	if (auth_required) {
		if (afid) {
			fid->user = np_afid2user (afid, &tc->u.tattach.uname,
						  tc->u.tattach.n_uname);
			if (!fid->user) {
				np_logerr (srv, "%s: invalid afid user", a);
				goto error;
			}
			if (srv->auth->checkauth(fid, afid, fid->aname) == 0) {
				np_logerr (srv, "%s: checkauth", a);
				goto error;
			}
			np_conn_set_authuser(conn, fid->user->uid);
		} else {
			u32 uid;

			fid->user = np_attach2user (srv, &tc->u.tattach.uname,
						    tc->u.tattach.n_uname);
			if (!fid->user) {
				np_logerr (srv, "%s: user lookup", a);
				goto error;
			}
			if (!(srv->flags & SRV_FLAGS_AUTHCONN)) {
				np_uerror(EPERM);
				np_logerr (srv, "%s: auth required", a);
				goto error;
			}
			if (np_conn_get_authuser(conn, &uid) < 0) {
				np_uerror(EPERM);
				np_logerr (srv, "%s: prior auth required", a);
				goto error;
			}
			if (uid != 0 && uid != fid->user->uid) {
				np_uerror(EPERM);
				np_logerr (srv, "%s: insufficient auth", a);
				goto error;
			}
		}
	}

	if (srv->remapuser) { /* squash user handling */
		if (srv->remapuser(fid, &tc->u.tattach.uname,
				        tc->u.tattach.n_uname,
				        &tc->u.tattach.aname) < 0) {
			np_logerr (srv, "%s: error remapping user", a);
			goto error;
		}
	}
	if (!fid->user) {
		fid->user = np_attach2user (srv, &tc->u.tattach.uname,
					          tc->u.tattach.n_uname);
		if (!fid->user) {
			np_logerr (srv, "%s: user lookup", a);
			goto error;
		}
	}

	if (!strcmp (fid->aname, "ctl")) {
		rc = np_ctl_attach (fid, afid, fid->aname);
	} else {
		if (!srv->attach) {
			np_uerror (EIO);
			goto error;
		}
		rc = (*srv->attach)(fid, afid, &tc->u.tattach.aname);
	}
error:
	if (afid)
		np_fid_decref(afid);
	return rc;
}