/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2012 by Ronnie Sahlberg This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, see . */ /* * This file contains definitions for the built in ZDR implementation. * This is a very limited ZDR subset that can only marshal to/from a momory buffer, * i.e. zdrmem_create() buffers. * It aims to be compatible with normal rpcgen generated functions. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef WIN32 #include #endif #ifdef AROS #include "aros_compat.h" #endif #ifdef PS2_EE #include "ps2_compat.h" #endif #ifdef PS3_PPU #include "ps3_compat.h" #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include "libnfs-zdr.h" #include "libnfs.h" #include "libnfs-raw.h" #include "libnfs-private.h" struct zdr_mem { struct zdr_mem *next; uint32_t size; char buf[1]; }; struct opaque_auth _null_auth; bool_t libnfs_zdr_setpos(ZDR *zdrs, uint32_t pos) { zdrs->pos = pos; return TRUE; } uint32_t libnfs_zdr_getpos(ZDR *zdrs) { return zdrs->pos; } void libnfs_zdrmem_create(ZDR *zdrs, const caddr_t addr, uint32_t size, enum zdr_op xop) { zdrs->x_op = xop; zdrs->buf = addr; zdrs->size = size; zdrs->pos = 0; zdrs->mem = NULL; } void *zdr_malloc(ZDR *zdrs, uint32_t size) { struct zdr_mem *mem; int mem_size; mem_size = offsetof(struct zdr_mem, buf) + size; mem = malloc(mem_size); mem->next = zdrs->mem; mem->size = size; zdrs->mem = mem; return &mem->buf[0]; } void libnfs_zdr_destroy(ZDR *zdrs) { while (zdrs->mem != NULL) { struct zdr_mem *mem = zdrs->mem->next; free(zdrs->mem); zdrs->mem = mem; } } bool_t libnfs_zdr_u_int(ZDR *zdrs, uint32_t *u) { if (zdrs->pos + 4 > zdrs->size) { return FALSE; } switch (zdrs->x_op) { case ZDR_ENCODE: *(uint32_t *)(void *)&zdrs->buf[zdrs->pos] = htonl(*u); zdrs->pos += 4; return TRUE; case ZDR_DECODE: *u = ntohl(*(uint32_t *)(void *)&zdrs->buf[zdrs->pos]); zdrs->pos += 4; return TRUE; } return FALSE; } bool_t libnfs_zdr_int(ZDR *zdrs, int32_t *i) { return libnfs_zdr_u_int(zdrs, (uint32_t *)i); } bool_t libnfs_zdr_uint64_t(ZDR *zdrs, uint64_t *u) { if (zdrs->pos + 8 > zdrs->size) { return FALSE; } switch (zdrs->x_op) { case ZDR_ENCODE: *(uint32_t *)(void *)&zdrs->buf[zdrs->pos] = htonl((*u >> 32)); zdrs->pos += 4; *(uint32_t *)(void *)&zdrs->buf[zdrs->pos] = htonl((*u & 0xffffffff)); zdrs->pos += 4; return TRUE; case ZDR_DECODE: *u = ntohl(*(uint32_t *)(void *)&zdrs->buf[zdrs->pos]); zdrs->pos += 4; *u <<= 32; *u |= (uint32_t)ntohl(*(uint32_t *)(void *)&zdrs->buf[zdrs->pos]); zdrs->pos += 4; return TRUE; } return FALSE; } bool_t libnfs_zdr_int64_t(ZDR *zdrs, int64_t *i) { return libnfs_zdr_uint64_t(zdrs, (uint64_t *)i); } bool_t libnfs_zdr_bytes(ZDR *zdrs, char **bufp, uint32_t *size, uint32_t maxsize) { uint32_t zero = 0; int pad; if (!libnfs_zdr_u_int(zdrs, size)) { return FALSE; } if (zdrs->pos + (int)*size > zdrs->size) { return FALSE; } switch (zdrs->x_op) { case ZDR_ENCODE: memcpy(&zdrs->buf[zdrs->pos], *bufp, *size); zdrs->pos += *size; pad = (4 - (zdrs->pos & 0x03)) & 0x03; if (pad) { /* Make valgrind happy again */ memcpy(&zdrs->buf[zdrs->pos], &zero, pad); zdrs->pos += pad; } return TRUE; case ZDR_DECODE: *bufp = &zdrs->buf[zdrs->pos]; zdrs->pos += *size; zdrs->pos = (zdrs->pos + 3) & ~3; return TRUE; } return FALSE; } bool_t libnfs_zdr_enum(ZDR *zdrs, enum_t *e) { bool_t ret; int32_t i = *e; ret = libnfs_zdr_u_int(zdrs, (uint32_t *)&i); *e = i; return ret; } bool_t libnfs_zdr_bool(ZDR *zdrs, bool_t *b) { return libnfs_zdr_u_int(zdrs, (uint32_t *)b); } bool_t libnfs_zdr_void(ZDR *zdrs, void *v) { return TRUE; } bool_t libnfs_zdr_pointer(ZDR *zdrs, char **objp, uint32_t size, zdrproc_t proc) { bool_t more_data; more_data = (*objp != NULL); if (!libnfs_zdr_bool(zdrs, &more_data)) { return FALSE; } if (more_data == 0) { *objp = NULL; return TRUE; } if (zdrs->x_op == ZDR_DECODE) { *objp = zdr_malloc(zdrs, size); if (*objp == NULL) { return FALSE; } memset(*objp, 0, size); } return proc(zdrs, *objp); } bool_t libnfs_zdr_opaque(ZDR *zdrs, char *objp, uint32_t size) { switch (zdrs->x_op) { case ZDR_ENCODE: memcpy(&zdrs->buf[zdrs->pos], objp, size); zdrs->pos += size; if (zdrs->pos & 3) { memset(&zdrs->buf[zdrs->pos], 0x00, 4 - (zdrs->pos & 3)); } zdrs->pos = (zdrs->pos + 3) & ~3; return TRUE; case ZDR_DECODE: memcpy(objp, &zdrs->buf[zdrs->pos], size); zdrs->pos += size; zdrs->pos = (zdrs->pos + 3) & ~3; return TRUE; } return FALSE; } bool_t libnfs_zdr_string(ZDR *zdrs, char **strp, uint32_t maxsize) { uint32_t size; if (zdrs->x_op == ZDR_ENCODE) { size = strlen(*strp); } if (!libnfs_zdr_u_int(zdrs, &size)) { return FALSE; } if (zdrs->pos + (int)size > zdrs->size) { return FALSE; } switch (zdrs->x_op) { case ZDR_ENCODE: return libnfs_zdr_opaque(zdrs, *strp, size); case ZDR_DECODE: /* If the we string is null terminated we can just return it * in place. */ if (zdrs->size > zdrs->pos + (int)size && zdrs->buf[zdrs->pos + size] == 0) { *strp = &zdrs->buf[zdrs->pos]; (*strp)[size] = 0; zdrs->pos += size; zdrs->pos = (zdrs->pos + 3) & ~3; return TRUE; } /* Crap. The string is not null terminated in the rx buffer. * we have to allocate a buffer so we can add the null byte. */ *strp = zdr_malloc(zdrs, size + 1); if (*strp == NULL) { return FALSE; } (*strp)[size] = 0; return libnfs_zdr_opaque(zdrs, *strp, size); } return FALSE; } bool_t libnfs_zdr_array(ZDR *zdrs, char **arrp, uint32_t *size, uint32_t maxsize, uint32_t elsize, zdrproc_t proc) { int i; uint32_t s; if (!libnfs_zdr_u_int(zdrs, size)) { return FALSE; } if (*size > UINT32_MAX/elsize) { return FALSE; } s = *size * elsize; if (zdrs->x_op == ZDR_DECODE) { *arrp = zdr_malloc(zdrs, s); if (*arrp == NULL) { return FALSE; } memset(*arrp, 0, s); } for (i = 0; i < (int)*size; i++) { if (!proc(zdrs, *arrp + i * elsize)) { return FALSE; } } return TRUE; } void libnfs_zdr_free(zdrproc_t proc, char *objp) { } static bool_t libnfs_opaque_auth(ZDR *zdrs, struct opaque_auth *auth) { if (!libnfs_zdr_u_int(zdrs, &auth->oa_flavor)) { return FALSE; } if (!libnfs_zdr_bytes(zdrs, &auth->oa_base, &auth->oa_length, auth->oa_length)) { return FALSE; } return TRUE; } static bool_t libnfs_rpc_call_body(struct rpc_context *rpc, ZDR *zdrs, struct call_body *cmb) { if (!libnfs_zdr_u_int(zdrs, &cmb->rpcvers)) { rpc_set_error(rpc, "libnfs_rpc_call_body failed to encode " "RPCVERS"); return FALSE; } if (!libnfs_zdr_u_int(zdrs, &cmb->prog)) { rpc_set_error(rpc, "libnfs_rpc_call_body failed to encode " "PROG"); return FALSE; } if (!libnfs_zdr_u_int(zdrs, &cmb->vers)) { rpc_set_error(rpc, "libnfs_rpc_call_body failed to encode " "VERS"); return FALSE; } if (!libnfs_zdr_u_int(zdrs, &cmb->proc)) { rpc_set_error(rpc, "libnfs_rpc_call_body failed to encode " "PROC"); return FALSE; } if (!libnfs_opaque_auth(zdrs, &cmb->cred)) { rpc_set_error(rpc, "libnfs_rpc_call_body failed to encode " "CRED"); return FALSE; } if (!libnfs_opaque_auth(zdrs, &cmb->verf)) { rpc_set_error(rpc, "libnfs_rpc_call_body failed to encode " "VERF"); return FALSE; } return TRUE; } static bool_t libnfs_accepted_reply(ZDR *zdrs, struct accepted_reply *ar) { if (!libnfs_opaque_auth(zdrs, &ar->verf)) { return FALSE; } if (!libnfs_zdr_u_int(zdrs, &ar->stat)) { return FALSE; } switch (ar->stat) { case SUCCESS: if (!ar->reply_data.results.proc(zdrs, ar->reply_data.results.where)) { return FALSE; } return TRUE; case PROG_MISMATCH: if (!libnfs_zdr_u_int(zdrs, &ar->reply_data.mismatch_info.low)) { return FALSE; } if (!libnfs_zdr_u_int(zdrs, &ar->reply_data.mismatch_info.high)) { return FALSE; } return TRUE; default: return TRUE; } return FALSE; } static bool_t libnfs_rejected_reply(ZDR *zdrs, struct rejected_reply *rr) { if (!libnfs_zdr_u_int(zdrs, (uint32_t *)&rr->stat)) { return FALSE; } switch (rr->stat) { case RPC_MISMATCH: if (!libnfs_zdr_u_int(zdrs, &rr->reject_data.mismatch_info.low)) { return FALSE; } if (!libnfs_zdr_u_int(zdrs, &rr->reject_data.mismatch_info.high)) { return FALSE; } return TRUE; case AUTH_ERROR: if (!libnfs_zdr_u_int(zdrs, (uint32_t *)&rr->reject_data.stat)) { return FALSE; } return TRUE; default: return TRUE; } return FALSE; } static bool_t libnfs_rpc_reply_body(struct rpc_context *rpc, ZDR *zdrs, struct reply_body *rmb) { if (!libnfs_zdr_u_int(zdrs, &rmb->stat)) { rpc_set_error(rpc, "libnfs_rpc_reply_body failed to decode " "STAT"); return FALSE; } switch (rmb->stat) { case MSG_ACCEPTED: if (!libnfs_accepted_reply(zdrs, &rmb->reply.areply)) { rpc_set_error(rpc, "libnfs_rpc_reply_body failed to " "decode ACCEPTED"); return FALSE; } return TRUE; case MSG_DENIED: if (!libnfs_rejected_reply(zdrs, &rmb->reply.rreply)) { rpc_set_error(rpc, "libnfs_rpc_reply_body failed to " "decode DENIED"); return FALSE; } return TRUE; } rpc_set_error(rpc, "libnfs_rpc_reply_body failed to " "decode. Neither ACCEPTED nor DENIED"); return FALSE; } static bool_t libnfs_rpc_msg(struct rpc_context *rpc, ZDR *zdrs, struct rpc_msg *msg) { int ret; if (!libnfs_zdr_u_int(zdrs, &msg->xid)) { rpc_set_error(rpc, "libnfs_rpc_msg failed to decode XID"); return FALSE; } if (!libnfs_zdr_u_int(zdrs, &msg->direction)) { rpc_set_error(rpc, "libnfs_rpc_msg failed to decode DIRECTION"); return FALSE; } switch (msg->direction) { case CALL: ret = libnfs_rpc_call_body(rpc, zdrs, &msg->body.cbody); if (!ret) { rpc_set_error(rpc, "libnfs_rpc_msg failed to encode " "CALL, ret=%d: %s", ret, rpc_get_error(rpc)); } return ret; case REPLY: ret = libnfs_rpc_reply_body(rpc, zdrs, &msg->body.rbody); if (!ret) { rpc_set_error(rpc, "libnfs_rpc_msg failed to decode " "REPLY, ret=%d: %s", ret, rpc_get_error(rpc)); } return ret; default: rpc_set_error(rpc, "libnfs_rpc_msg failed to decode. " "Neither CALL not REPLY"); return FALSE; } } bool_t libnfs_zdr_callmsg(struct rpc_context *rpc, ZDR *zdrs, struct rpc_msg *msg) { return libnfs_rpc_msg(rpc, zdrs, msg); } bool_t libnfs_zdr_replymsg(struct rpc_context *rpc, ZDR *zdrs, struct rpc_msg *msg) { return libnfs_rpc_msg(rpc, zdrs, msg); } struct AUTH *authnone_create(void) { struct AUTH *auth; auth = malloc(sizeof(struct AUTH)); auth->ah_cred.oa_flavor = AUTH_NONE; auth->ah_cred.oa_length = 0; auth->ah_cred.oa_base = NULL; auth->ah_verf.oa_flavor = AUTH_NONE; auth->ah_verf.oa_length = 0; auth->ah_verf.oa_base = NULL; auth->ah_private = NULL; return auth; } struct AUTH *libnfs_authunix_create(const char *host, uint32_t uid, uint32_t gid, uint32_t len, uint32_t *groups) { struct AUTH *auth; int size; uint32_t *buf; int idx; size = 4 + 4 + ((strlen(host) + 3) & ~3) + 4 + 4 + 4 + len * 4; auth = malloc(sizeof(struct AUTH)); memset(auth, 0x00, sizeof(struct AUTH)); auth->ah_cred.oa_flavor = AUTH_UNIX; auth->ah_cred.oa_length = size; auth->ah_cred.oa_base = malloc(size); memset(auth->ah_cred.oa_base, 0x00, size); buf = (uint32_t *)(void *)auth->ah_cred.oa_base; idx = 0; buf[idx++] = htonl((uint32_t)rpc_current_time()); buf[idx++] = htonl(strlen(host)); memcpy(&buf[2], host, strlen(host)); idx += (strlen(host) + 3) >> 2; buf[idx++] = htonl(uid); buf[idx++] = htonl(gid); buf[idx++] = htonl(len); while (len-- > 0) { buf[idx++] = htonl(*groups++); } auth->ah_verf.oa_flavor = AUTH_NONE; auth->ah_verf.oa_length = 0; auth->ah_verf.oa_base = NULL; auth->ah_private = NULL; return auth; } struct AUTH *libnfs_authunix_create_default(void) { #if defined(WIN32) || defined(PS3_PPU) return libnfs_authunix_create("libnfs", 65534, 65534, 0, NULL); #else return libnfs_authunix_create("libnfs", getuid(), getgid(), 0, NULL); #endif } void libnfs_auth_destroy(struct AUTH *auth) { if (auth->ah_cred.oa_base) { free(auth->ah_cred.oa_base); } if (auth->ah_verf.oa_base) { free(auth->ah_verf.oa_base); } free(auth); }