diff options
author | thurlow <none@none> | 2008-02-13 19:51:22 -0800 |
---|---|---|
committer | thurlow <none@none> | 2008-02-13 19:51:22 -0800 |
commit | 4bff34e37def8a90f9194d81bc345c52ba20086a (patch) | |
tree | 7bf2710d9da099e3b07fea38e12788bfd565f3c5 /usr/src/lib/libsmbfs | |
parent | a916d99c7b27a531bf37c57f83b0b74120fd05bb (diff) | |
download | illumos-joyent-4bff34e37def8a90f9194d81bc345c52ba20086a.tar.gz |
PSARC 2005/695 CIFS Client on Solaris
PSARC 2007/303 pam_smb_login
PSARC 2008/073 CIFS Client on Solaris - Updates
6651904 CIFS Client - PSARC 2005/695
Diffstat (limited to 'usr/src/lib/libsmbfs')
49 files changed, 12781 insertions, 0 deletions
diff --git a/usr/src/lib/libsmbfs/Makefile b/usr/src/lib/libsmbfs/Makefile new file mode 100644 index 0000000000..0ab1f6c137 --- /dev/null +++ b/usr/src/lib/libsmbfs/Makefile @@ -0,0 +1,72 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libsmbfs/Makefile +# + +include $(SRC)/lib/Makefile.lib + +# ISA targets +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +# conditional assignments +all := TARGET= all +install := TARGET= install +clean := TARGET= clean +clobber := TARGET= clobber +lint := TARGET= lint + +POFILE = libsmbfs.po + +.KEEP_STATE: + +all install clean clobber lint: $(SUBDIRS) + +include $(SRC)/Makefile.msg.targ + +MSGFILES=smb/cfopt.c smb/charsets.c smb/charsets.h smb/ctx.c smb/derparse.c \ + smb/derparse.h smb/file.c smb/keychain.c smb/mbuf.c smb/nb.c \ + smb/nb_name.c smb/nb_net.c smb/nbns_rq.c smb/netshareenum.c \ + smb/nls.c smb/print.c smb/queue.h smb/rap.c smb/rcfile.c \ + smb/rcfile_priv.h smb/rq.c smb/spnego.c smb/spnego.h \ + smb/spnegoparse.c smb/spnegoparse.h smb/subr.c smb/ui-sun.c + +_msg: $(MSGDOMAINPOFILE) + +$(MSGDOMAINPOFILE): $(POFILE) + +$(POFILE): $(MSGFILES) + $(BUILDPO.msgfiles) + +install: $(ROOTLIBS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/libsmbfs/Makefile.com b/usr/src/lib/libsmbfs/Makefile.com new file mode 100644 index 0000000000..76e2eac287 --- /dev/null +++ b/usr/src/lib/libsmbfs/Makefile.com @@ -0,0 +1,98 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsmbfs/Makefile.com + +LIBRARY= libsmbfs.a +VERS= .1 + +# leaving out: kiconv.o + +OBJECTS=\ + charsets.o \ + cfopt.o \ + ctx.o \ + derparse.o \ + file.o \ + keychain.o \ + mbuf.o \ + nb.o \ + nb_name.o \ + nb_net.o \ + nbns_rq.o \ + netshareenum.o \ + nls.o \ + print.o \ + rap.o \ + rcfile.o \ + rq.o \ + spnego.o \ + spnegoparse.o \ + subr.o \ + ui-sun.o + +include $(SRC)/lib/Makefile.lib + +LIBS = $(DYNLIB) $(LINTLIB) + +SRCDIR= ../smb + +SRCS= $(OBJECTS:%.o=../smb/%.c) + +$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) + +C99MODE= $(C99_ENABLE) + +LDLIBS += -lsocket -lnsl -lc -lkrb5 + +# normal warnings... +CFLAGS += $(CCVERBOSE) + +CPPFLAGS += -D__EXTENSIONS__ -D_REENTRANT -DMIA \ + -I$(SRCDIR) -I.. -I$(SRC)/uts/common + +# uncomment these if you want to use dbx +#COPTFLAG = -g +#CTF_FLAGS = +#CTFCONVERT_O= +#CTFMERGE_LIB= + +# disable some of the less important lint +LINTCHECKFLAGS += -erroff=E_FUNC_ARG_UNUSED +LINTCHECKFLAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2 +LINTCHECKFLAGS += -erroff=E_FUNC_RET_MAYBE_IGNORED2 +LINTCHECKFLAGS += -erroff=E_FUNC_VAR_UNUSED +LINTCHECKFLAGS += -erroff=E_STATIC_UNUSED +LINTCHECKFLAGS += -erroff=E_CONSTANT_CONDITION +LINTCHECKFLAGS += -erroff=E_TRUE_LOGICAL_EXPR + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../Makefile.targ diff --git a/usr/src/lib/libsmbfs/amd64/Makefile b/usr/src/lib/libsmbfs/amd64/Makefile new file mode 100644 index 0000000000..7443ee1806 --- /dev/null +++ b/usr/src/lib/libsmbfs/amd64/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libsmbfs/cflib.h b/usr/src/lib/libsmbfs/cflib.h new file mode 100644 index 0000000000..0b6941931e --- /dev/null +++ b/usr/src/lib/libsmbfs/cflib.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: cflib.h,v 1.1.1.1 2001/06/09 00:28:11 zarzycki Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef _CFLIB_H_ +#define _CFLIB_H_ + +struct rcfile; + +/* + * A unified options parser + */ +enum opt_argtype {OPTARG_STR, OPTARG_INT, OPTARG_BOOL}; + +struct opt_args; + +typedef int opt_callback_t (struct opt_args *); + +#define OPTFL_NONE 0x0000 +#define OPTFL_HAVEMIN 0x0001 +#define OPTFL_HAVEMAX 0x0002 +#define OPTFL_MINMAX NAFL_HAVEMIN | NAFL_HAVEMAX + +struct opt_args { + enum opt_argtype type; + int opt; /* command line option */ + char *name; /* rc file equiv */ + int flag; /* OPTFL_* */ + int ival; /* int/bool values, or max len for str value */ + char *str; /* string value */ + int min; /* min for ival */ + int max; /* max for ival */ + opt_callback_t *fn; /* call back to validate */ +}; +typedef struct opt_args opt_args_t; + +extern int cf_opterr, cf_optind, cf_optopt, cf_optreset; +extern const char *cf_optarg; + +#ifdef __cplusplus +extern "C" { +#endif + +int opt_args_parse(struct rcfile *, struct opt_args *, const char *, + opt_callback_t *); +int opt_args_parseopt(struct opt_args *, int, char *, opt_callback_t *); + +int cf_getopt(int, char * const *, const char *); + +int rc_open(const char *, const char *, struct rcfile **); +int rc_close(struct rcfile *); +int rc_merge(const char *, struct rcfile **); +int rc_merge_pipe(const char *, struct rcfile **); +int rc_getstringptr(struct rcfile *, const char *, const char *, char **); +int rc_getstring(struct rcfile *, const char *, const char *, size_t, char *); +int rc_getint(struct rcfile *, const char *, const char *, int *); +int rc_getbool(struct rcfile *, const char *, const char *, int *); + +#ifdef __cplusplus +} +#endif + +#endif /* _CFLIB_H_ */ diff --git a/usr/src/lib/libsmbfs/i386/Makefile b/usr/src/lib/libsmbfs/i386/Makefile new file mode 100644 index 0000000000..23b74c3928 --- /dev/null +++ b/usr/src/lib/libsmbfs/i386/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libsmbfs/netsmb/nb_lib.h b/usr/src/lib/libsmbfs/netsmb/nb_lib.h new file mode 100644 index 0000000000..1c4100e17a --- /dev/null +++ b/usr/src/lib/libsmbfs/netsmb/nb_lib.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: nb_lib.h,v 1.4 2004/12/11 05:23:58 lindak Exp $ + */ + +#ifndef _NETSMB_NB_LIB_H_ +#define _NETSMB_NB_LIB_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Error codes + */ +#define NBERR_INVALIDFORMAT 0x0001 +#define NBERR_SRVFAILURE 0x0002 +#define NBERR_NAMENOTFOUND 0x0003 +#define NBERR_IMP 0x0004 +#define NBERR_REFUSED 0x0005 +#define NBERR_ACTIVE 0x0006 +#define NBERR_HOSTNOTFOUND 0x0101 +#define NBERR_TOOMANYREDIRECTS 0x0102 +#define NBERR_INVALIDRESPONSE 0x0103 +#define NBERR_NAMETOOLONG 0x0104 +#define NBERR_NOBCASTIFS 0x0105 +#define NBERR_MAX 0x0106 +#define NBERROR(e) ((e) | SMB_NB_ERROR) + +#define NBCF_RESOLVED 0x0001 +#define NBCF_NS_ENABLE 0x0002 /* any NetBIOS lookup */ +#define NBCF_BC_ENABLE 0x0004 /* lookup via broadcast */ + +/* + * nb environment + */ +struct nb_ctx { + int nb_flags; + int nb_timo; + char *nb_scope; /* NetBIOS scope */ + in_addr_t nb_wins1; /* primary WINS */ + in_addr_t nb_wins2; /* secondary WINS (unused now) */ + struct sockaddr_in nb_lastns; /* see cmd:lookup.c */ +}; +typedef struct nb_ctx nb_ctx_t; + +/* + * resource record + */ +struct nbns_rr { + uchar_t *rr_name; /* compressed NETBIOS name */ + uint16_t rr_type; + uint16_t rr_class; + uint32_t rr_ttl; + uint16_t rr_rdlength; + uchar_t *rr_data; +}; +typedef struct nbns_rr nfns_rr_t; + +/* + * NetBIOS name return + */ +struct nbns_nr { + char ns_name[NB_NAMELEN]; + uint16_t ns_flags; +}; +typedef struct nbns_nr nbns_nr_t; + +#define NBRQF_POINT 0x0000 +#define NBRQF_BROADCAST 0x0001 + +#define NBNS_GROUPFLG 0x8000 + +/* + * nbns request + */ +struct nbns_rq { + int nr_opcode; + int nr_nmflags; + int nr_rcode; + int nr_qdcount; + int nr_ancount; + int nr_nscount; + int nr_arcount; + struct nb_name *nr_qdname; + uint16_t nr_qdtype; + uint16_t nr_qdclass; + struct in_addr nr_dest; /* receiver of query */ + struct sockaddr_in nr_sender; /* sender of response */ + int nr_rpnmflags; + int nr_rprcode; + uint16_t nr_rpancount; + uint16_t nr_rpnscount; + uint16_t nr_rparcount; + uint16_t nr_trnid; + struct nb_ctx *nr_nbd; + struct mbdata nr_rq; + struct mbdata nr_rp; + struct nb_ifdesc *nr_if; + int nr_flags; + int nr_fd; + int nr_maxretry; +}; +typedef struct nbns_rq nbns_rq_t; + +struct nb_ifdesc { + int id_flags; + struct in_addr id_addr; + struct in_addr id_mask; + char id_name[16]; /* actually IFNAMSIZ */ + struct nb_ifdesc *id_next; +}; +typedef struct nb_ifdesc nb_ifdesc_t; + +struct sockaddr; + +#ifdef __cplusplus +extern "C" { +#endif + +int nb_name_len(struct nb_name *); +/* new flag UCflag. 1=uppercase,0=don't */ +int nb_name_encode(struct nb_name *, uchar_t *); +int nb_encname_len(const uchar_t *); + +int nb_snballoc(int namelen, struct sockaddr_nb **); +void nb_snbfree(struct sockaddr *); +int nb_sockaddr(struct sockaddr *, struct nb_name *, struct sockaddr_nb **); + +int nb_resolvehost_in(const char *, struct sockaddr **); +int nbns_resolvename(const char *, struct nb_ctx *, struct sockaddr **); +int nbns_getnodestatus(struct sockaddr *targethost, + struct nb_ctx *ctx, char *system, char *workgroup); +int nb_getlocalname(char *name, size_t maxlen); +int nb_enum_if(struct nb_ifdesc **); + +const char *nb_strerror(int error); + +int nb_ctx_create(struct nb_ctx **); +void nb_ctx_done(struct nb_ctx *); +int nb_ctx_setns(struct nb_ctx *, const char *); +int nb_ctx_setscope(struct nb_ctx *, const char *); +int nb_ctx_resolve(struct nb_ctx *); +int nb_ctx_readrcsection(struct rcfile *, struct nb_ctx *, const char *, int); + +#ifdef __cplusplus +} +#endif + +#endif /* !_NETSMB_NB_LIB_H_ */ diff --git a/usr/src/lib/libsmbfs/netsmb/smb_keychain.h b/usr/src/lib/libsmbfs/netsmb/smb_keychain.h new file mode 100644 index 0000000000..9e4113a225 --- /dev/null +++ b/usr/src/lib/libsmbfs/netsmb/smb_keychain.h @@ -0,0 +1,82 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMB_KEYCHAIN_H +#define _SMB_KEYCHAIN_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * External interface to the libsmbfs/netsmb keychain + * storage mechanism. This interface is consumed by + * the "smbutil" commands: login, logout, ... + * and by the SMBFS PAM module. + */ + +#define SMB_KEYCHAIN_SUCCESS 0 +#define SMB_KEYCHAIN_BADPASSWD 300 +#define SMB_KEYCHAIN_BADDOMAIN 301 +#define SMB_KEYCHAIN_BADUSER 302 +#define SMB_KEYCHAIN_NODRIVER 303 +#define SMB_KEYCHAIN_UNKNOWN 304 + +/* Add a password to the keychain. */ +int smbfs_keychain_add(uid_t uid, const char *domain, const char *user, + const char *password); + +/* Delete a password from the keychain. */ +int smbfs_keychain_del(uid_t uid, const char *domain, const char *user); + +/* + * Check for existence of a keychain entry. + * Returns 0 if it exists, else ENOENT. + */ +int smbfs_keychain_chk(const char *domain, const char *user); + +/* + * Delete all keychain entries owned by the caller. + */ +int smbfs_keychain_del_owner(void); + +/* + * Delete all keychain entries (regardless of owner). + * Requires super-user privliege. + */ +int smbfs_keychain_del_everyone(void); + +/* + * This is not really part of the keychain library, + * but is typically needed in code that wants to + * provide (editable) defaults for domain/user + * + * Get default domain and user names + * Server name is optional. + */ +int +smbfs_default_dom_usr(const char *home, const char *server, + char *dom, int maxdom, char *usr, int maxusr); + +#endif /* _SMB_KEYCHAIN_H */ diff --git a/usr/src/lib/libsmbfs/netsmb/smb_lib.h b/usr/src/lib/libsmbfs/netsmb/smb_lib.h new file mode 100644 index 0000000000..90bfda6313 --- /dev/null +++ b/usr/src/lib/libsmbfs/netsmb/smb_lib.h @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: smb_lib.h,v 1.21.82.2 2005/06/02 00:55:39 lindak Exp $ + */ + +#ifndef _NETSMB_SMB_LIB_H_ +#define _NETSMB_SMB_LIB_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/byteorder.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_dev.h> + +#define SMB_CFG_FILE "/etc/nsmb.conf" +#define OLD_SMB_CFG_FILE "/usr/local/etc/nsmb.conf" + +#define STDPARAM_ARGS \ + 'A':case 'B':case 'C':case 'E':case 'I':case 'L':case \ + 'M':case 'N':case 'U':case 'R':case 'S':case 'T':case \ + 'W':case 'O':case 'P' + +#define STDPARAM_OPT "ABCE:I:L:M:NO:P:U:R:S:T:W:" + +/* + * bits to indicate the source of error + */ +#define SMB_ERRTYPE_MASK 0xf0000 +#define SMB_SYS_ERROR 0x00000 +#define SMB_RAP_ERROR 0x10000 +#define SMB_NB_ERROR 0x20000 + +/* + * These get/set macros do not handle mis-aligned data. + * The data are all supposed to be aligned, but that's + * up to the server. If we ever encounter a server that + * doesn't obey this rule, a "strict alignment" client + * (i.e. SPARC) may get an alignment trap in one of these. + * If that ever happens, make these macros into functions + * that can handle mis-aligned data. (Or catch traps.) + */ +#define getb(buf, ofs) (((const uint8_t *)(buf))[ofs]) +#define setb(buf, ofs, val) (((uint8_t *)(buf))[ofs]) = val +#define getbw(buf, ofs) ((uint16_t)(getb(buf, ofs))) +#define getw(buf, ofs) (*((uint16_t *)(&((uint8_t *)(buf))[ofs]))) +#define getdw(buf, ofs) (*((uint32_t *)(&((uint8_t *)(buf))[ofs]))) + +#ifdef _LITTLE_ENDIAN + +#define getwle(buf, ofs) (*((uint16_t *)(&((uint8_t *)(buf))[ofs]))) +#define getdle(buf, ofs) (*((uint32_t *)(&((uint8_t *)(buf))[ofs]))) +#define getwbe(buf, ofs) (ntohs(getwle(buf, ofs))) +#define getdbe(buf, ofs) (ntohl(getdle(buf, ofs))) + +#define setwle(buf, ofs, val) getwle(buf, ofs) = val +#define setwbe(buf, ofs, val) getwle(buf, ofs) = htons(val) +#define setdle(buf, ofs, val) getdle(buf, ofs) = val +#define setdbe(buf, ofs, val) getdle(buf, ofs) = htonl(val) + +#else /* _LITTLE_ENDIAN */ + +#define getwbe(buf, ofs) (*((uint16_t *)(&((uint8_t *)(buf))[ofs]))) +#define getdbe(buf, ofs) (*((uint32_t *)(&((uint8_t *)(buf))[ofs]))) +#define getwle(buf, ofs) (BSWAP_16(getwbe(buf, ofs))) +#define getdle(buf, ofs) (BSWAP_32(getdbe(buf, ofs))) + +#define setwbe(buf, ofs, val) getwbe(buf, ofs) = val +#define setwle(buf, ofs, val) getwbe(buf, ofs) = BSWAP_16(val) +#define setdbe(buf, ofs, val) getdbe(buf, ofs) = val +#define setdle(buf, ofs, val) getdbe(buf, ofs) = BSWAP_32(val) + +#endif /* _LITTLE_ENDIAN */ + +/* + * SMB work context. Used to store all values which are necessary + * to establish connection to an SMB server. + */ +struct smb_ctx { + int ct_flags; /* SMBCF_ */ + int ct_fd; /* handle of connection */ + int ct_parsedlevel; + int ct_minlevel; + int ct_maxlevel; + char *ct_fullserver; /* original server name from cmd line */ + char *ct_srvaddr; /* hostname or IP address of server */ + struct sockaddr_in ct_srvinaddr; /* IP address of server */ + char ct_locname[SMB_MAXUSERNAMELEN + 1]; + struct nb_ctx *ct_nb; + struct smbioc_ossn ct_ssn; + struct smbioc_oshare ct_sh; + char *ct_origshare; + char *ct_home; +#ifdef APPLE + /* temporary automount hack */ + char **ct_xxx; + int ct_maxxxx; /* max # to mount (-x arg) */ +#endif + void *ct_secblob; + int ct_secbloblen; + /* krb5 stuff: all anonymous struct pointers here. */ + struct _krb5_context *ct_krb5ctx; + struct _krb5_ccache *ct_krb5cc; /* credentials cache */ + struct krb5_principal_data *ct_krb5cp; /* client principal */ +}; +typedef struct smb_ctx smb_ctx_t; + +#define SMBCF_NOPWD 0x0001 /* don't ask for a password */ +#define SMBCF_SRIGHTS 0x0002 /* share access rights supplied */ +#define SMBCF_LOCALE 0x0004 /* use current locale */ +#define SMBCF_CMD_DOM 0x0010 /* CMD specified domain */ +#define SMBCF_CMD_USR 0x0020 /* CMD specified user */ +#define SMBCF_CMD_PW 0x0040 /* CMD specified password */ +#define SMBCF_RESOLVED 0x8000 /* structure has been verified */ +#define SMBCF_KCBAD 0x00080000 /* keychain password failed */ +#define SMBCF_KCFOUND 0x00100000 /* password is from keychain */ +#define SMBCF_BROWSEOK 0x00200000 /* browser dialogue may be used */ +#define SMBCF_AUTHREQ 0x00400000 /* auth. dialog requested */ +#define SMBCF_KCSAVE 0x00800000 /* add to keychain requested */ +#define SMBCF_XXX 0x01000000 /* mount-all, a very bad thing */ +#define SMBCF_SSNACTIVE 0x02000000 /* session setup succeeded */ +#define SMBCF_KCDOMAIN 0x04000000 /* use domain in KC lookup */ + +/* + * access modes (see also smb_dev.h) + */ +#define SMBM_READ S_IRUSR /* read conn attrs. (like list shares) */ +#define SMBM_WRITE S_IWUSR /* modify conn attrs */ +#define SMBM_EXEC S_IXUSR /* can send SMB requests */ +#define SMBM_READGRP S_IRGRP +#define SMBM_WRITEGRP S_IWGRP +#define SMBM_EXECGRP S_IXGRP +#define SMBM_READOTH S_IROTH +#define SMBM_WRITEOTH S_IWOTH +#define SMBM_EXECOTH S_IXOTH +#define SMBM_ALL S_IRWXU +#define SMBM_DEFAULT S_IRWXU + + +/* + * Share type for smb_ctx_init + */ +#define SMB_ST_DISK STYPE_DISKTREE +#define SMB_ST_PRINTER STYPE_PRINTQ +#define SMB_ST_COMM STYPE_DEVICE +#define SMB_ST_PIPE STYPE_IPC +#define SMB_ST_ANY STYPE_UNKNOWN +#define SMB_ST_MAX STYPE_UNKNOWN +#define SMB_ST_NONE 0xff /* not a part of protocol */ + + +/* + * request handling structures + */ +struct mbuf { + int m_len; + int m_maxlen; + char *m_data; + struct mbuf *m_next; +}; +typedef struct mbuf mbuf_t; + +struct mbdata { + struct mbuf *mb_top; + struct mbuf *mb_cur; + char *mb_pos; + int mb_count; +}; +typedef struct mbdata mbdata_t; + +#define M_ALIGNFACTOR (sizeof (long)) +#define M_ALIGN(len) (((len) + M_ALIGNFACTOR - 1) & ~(M_ALIGNFACTOR - 1)) +#define M_BASESIZE (sizeof (struct mbuf)) +#define M_MINSIZE (256 - M_BASESIZE) +#define M_TOP(m) ((char *)(m) + M_BASESIZE) +#define M_TRAILINGSPACE(m) ((m)->m_maxlen - (m)->m_len) +#define mtod(m, t) ((t)(m)->m_data) + +struct smb_rq { + uchar_t rq_cmd; + struct mbdata rq_rq; + struct mbdata rq_rp; + struct smb_ctx *rq_ctx; + int rq_wcount; + int rq_bcount; +}; +typedef struct smb_rq smb_rq_t; + +struct smb_bitname { + uint_t bn_bit; + char *bn_name; +}; +typedef struct smb_bitname smb_bitname_t; + +extern int smb_debug, smb_verbose; +extern struct rcfile *smb_rc; + +#ifdef __cplusplus +extern "C" { +#endif + +struct sockaddr; + +int smb_lib_init(void); +int smb_open_driver(void); +int smb_open_rcfile(struct smb_ctx *ctx); +void smb_error(const char *, int, ...); +char *smb_printb(char *, int, const struct smb_bitname *); + +/* + * Context management + */ +int smb_ctx_init(struct smb_ctx *, int, char *[], int, int, int); +void smb_ctx_done(struct smb_ctx *); +int smb_ctx_parseunc(struct smb_ctx *, const char *, int, const char **); +int smb_ctx_setcharset(struct smb_ctx *, const char *); +int smb_ctx_setfullserver(struct smb_ctx *, const char *); +void smb_ctx_setserver(struct smb_ctx *, const char *); +int smb_ctx_setuser(struct smb_ctx *, const char *, int); +int smb_ctx_setshare(struct smb_ctx *, const char *, int); +int smb_ctx_setscope(struct smb_ctx *, const char *); +int smb_ctx_setworkgroup(struct smb_ctx *, const char *, int); +int smb_ctx_setpassword(struct smb_ctx *, const char *, int); +int smb_ctx_setsrvaddr(struct smb_ctx *, const char *); +int smb_ctx_opt(struct smb_ctx *, int, const char *); +int smb_ctx_negotiate(struct smb_ctx *, int, int, char *); +int smb_ctx_tdis(struct smb_ctx *ctx); +int smb_ctx_lookup(struct smb_ctx *, int, int); +int smb_ctx_login(struct smb_ctx *); +int smb_ctx_readrc(struct smb_ctx *); +int smb_ctx_resolve(struct smb_ctx *); +int smb_ctx_setflags(struct smb_ctx *, int, int, int); +int smb_ctx_flags2(struct smb_ctx *); + +int smb_smb_open_print_file(struct smb_ctx *, int, int, const char *, smbfh*); +int smb_smb_close_print_file(struct smb_ctx *, smbfh); + +int smb_read(struct smb_ctx *, smbfh, off_t, size_t, char *); +int smb_write(struct smb_ctx *, smbfh, off_t, size_t, const char *); + +#define smb_rq_getrequest(rqp) (&(rqp)->rq_rq) +#define smb_rq_getreply(rqp) (&(rqp)->rq_rp) + +int smb_rq_init(struct smb_ctx *, uchar_t, size_t, struct smb_rq **); +void smb_rq_done(struct smb_rq *); +void smb_rq_wend(struct smb_rq *); +int smb_rq_simple(struct smb_rq *); +int smb_rq_dmem(struct mbdata *, const char *, size_t); +int smb_rq_dstring(struct mbdata *, const char *); + +int smb_t2_request(struct smb_ctx *, int, uint16_t *, const char *, + int, void *, int, void *, int *, void *, int *, void *, int *); + +void smb_simplecrypt(char *dst, const char *src); +int smb_simpledecrypt(char *dst, const char *src); + +int m_getm(struct mbuf *, size_t, struct mbuf **); +int m_lineup(struct mbuf *, struct mbuf **); +int mb_init(struct mbdata *, size_t); +int mb_initm(struct mbdata *, struct mbuf *); +int mb_done(struct mbdata *); +int mb_fit(struct mbdata *mbp, size_t size, char **pp); +int mb_put_uint8(struct mbdata *, uint8_t); +int mb_put_uint16be(struct mbdata *, uint16_t); +int mb_put_uint16le(struct mbdata *, uint16_t); +int mb_put_uint32be(struct mbdata *, uint32_t); +int mb_put_uint32le(struct mbdata *, uint32_t); +int mb_put_uint64be(struct mbdata *, uint64_t); +int mb_put_uint64le(struct mbdata *, uint64_t); +int mb_put_mem(struct mbdata *, const char *, size_t); +int mb_put_pstring(struct mbdata *mbp, const char *s); +int mb_put_mbuf(struct mbdata *, struct mbuf *); + +int mb_get_uint8(struct mbdata *, uint8_t *); +int mb_get_uint16(struct mbdata *, uint16_t *); +int mb_get_uint16le(struct mbdata *, uint16_t *); +int mb_get_uint16be(struct mbdata *, uint16_t *); +int mb_get_uint32(struct mbdata *, uint32_t *); +int mb_get_uint32be(struct mbdata *, uint32_t *); +int mb_get_uint32le(struct mbdata *, uint32_t *); +int mb_get_uint64(struct mbdata *, uint64_t *); +int mb_get_uint64be(struct mbdata *, uint64_t *); +int mb_get_uint64le(struct mbdata *, uint64_t *); +int mb_get_mem(struct mbdata *, char *, size_t); + +extern uchar_t nls_lower[256], nls_upper[256]; + +int nls_setrecode(const char *, const char *); +int nls_setlocale(const char *); +char *nls_str_toext(char *, const char *); +char *nls_str_toloc(char *, const char *); +void *nls_mem_toext(void *, const void *, int); +void *nls_mem_toloc(void *, const void *, int); +char *nls_str_upper(char *, const char *); +char *nls_str_lower(char *, const char *); + +int smb_get_authentication(char *, size_t, char *, size_t, char *, size_t, + const char *, struct smb_ctx *); +int smb_browse(struct smb_ctx *, int); +void smb_save2keychain(struct smb_ctx *); +#define smb_autherr(e) ((e) == EAUTH || (e) == EACCES || (e) == EPERM) +char *smb_strerror(int); +char *smb_getprogname(); +#define __progname smb_getprogname() + +extern char *unpercent(char *component); + +#ifdef __cplusplus +} +#endif + +#endif /* _NETSMB_SMB_LIB_H_ */ diff --git a/usr/src/lib/libsmbfs/netsmb/smb_netshareenum.h b/usr/src/lib/libsmbfs/netsmb/smb_netshareenum.h new file mode 100644 index 0000000000..14f2594df7 --- /dev/null +++ b/usr/src/lib/libsmbfs/netsmb/smb_netshareenum.h @@ -0,0 +1,18 @@ + +#ifndef _NETSMB_SMB_NETSHAREENUM_H_ +#define _NETSMB_SMB_NETSHAREENUM_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* This is from Apple. See ../smb/netshareenum.c */ + +struct share_info { + uint16_t type; + char *netname; + char *remark; +}; +typedef struct share_info share_info_t; + +int smb_netshareenum(struct smb_ctx *, int *, int *, struct share_info **); + +#endif /* _NETSMB_SMB_NETSHAREENUM_H_ */ diff --git a/usr/src/lib/libsmbfs/netsmb/smb_rap.h b/usr/src/lib/libsmbfs/netsmb/smb_rap.h new file mode 100644 index 0000000000..118861f633 --- /dev/null +++ b/usr/src/lib/libsmbfs/netsmb/smb_rap.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: smb_rap.h,v 1.1.1.1 2001/07/06 22:38:38 conrad Exp $ + */ + +#ifndef _NETSMB_SMB_RAP_H_ +#define _NETSMB_SMB_RAP_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +struct smb_rap { + char *r_sparam; + char *r_nparam; + char *r_sdata; + char *r_ndata; + char *r_pbuf; /* rq parameters */ + int r_plen; /* rq param len */ + char *r_npbuf; + char *r_dbuf; /* rq data */ + int r_dlen; /* rq data len */ + char *r_ndbuf; + uint32_t r_result; + char *r_rcvbuf; + int r_rcvbuflen; + int r_entries; +}; + +struct smb_share_info_1 { + char shi1_netname[13]; + char shi1_pad; + uint16_t shi1_type; + uint32_t shi1_remark; /* char * */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int smb_rap_create(int, const char *, const char *, struct smb_rap **); +void smb_rap_done(struct smb_rap *); +int smb_rap_request(struct smb_rap *, struct smb_ctx *); +int smb_rap_setNparam(struct smb_rap *, int); +int smb_rap_setPparam(struct smb_rap *, void *); +int smb_rap_error(struct smb_rap *, int); + +int smb_rap_NetShareEnum(struct smb_ctx *, int, void *, int *, int *, int *); + +#ifdef __cplusplus +} +#endif + +#endif /* _NETSMB_SMB_RAP_H_ */ diff --git a/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.apple b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.apple new file mode 100644 index 0000000000..a979c99b4f --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.apple @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2001 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ diff --git a/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.apple.descrip b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.apple.descrip new file mode 100644 index 0000000000..aa3d6ad7e1 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.apple.descrip @@ -0,0 +1 @@ +PORTIONS OF LIBSMBFS IN CIFS CLIENT diff --git a/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.boris_popov b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.boris_popov new file mode 100644 index 0000000000..583923c355 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.boris_popov @@ -0,0 +1,29 @@ + Copyright (c) 2000, 2001 Boris Popov + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by Boris Popov. + 4. Neither the name of the author nor the names of any co-contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. diff --git a/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.boris_popov.descrip b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.boris_popov.descrip new file mode 100644 index 0000000000..d7cc9ebbd7 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.boris_popov.descrip @@ -0,0 +1 @@ +CIFS CLIENT SOFTWARE diff --git a/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.bsd4 b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.bsd4 new file mode 100644 index 0000000000..56ec482ec5 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.bsd4 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 1982, 1986, 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.bsd4.descrip b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.bsd4.descrip new file mode 100644 index 0000000000..aa3d6ad7e1 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.bsd4.descrip @@ -0,0 +1 @@ +PORTIONS OF LIBSMBFS IN CIFS CLIENT diff --git a/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.microsoft b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.microsoft new file mode 100644 index 0000000000..afbd224c30 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.microsoft @@ -0,0 +1,10 @@ +/* +// Copyright (C) 2002 Microsoft Corporation +// All rights reserved. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" +// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +// OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY +// AND/OR FITNESS FOR A PARTICULAR PURPOSE. +*/ diff --git a/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.microsoft.descrip b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.microsoft.descrip new file mode 100644 index 0000000000..aa3d6ad7e1 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.microsoft.descrip @@ -0,0 +1 @@ +PORTIONS OF LIBSMBFS IN CIFS CLIENT diff --git a/usr/src/lib/libsmbfs/smb/cfopt.c b/usr/src/lib/libsmbfs/smb/cfopt.c new file mode 100644 index 0000000000..094682f6d8 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/cfopt.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: cfopt.c,v 1.1.1.1 2001/06/09 00:28:12 zarzycki Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> + +#include <stdio.h> +#include <string.h> +#include <libintl.h> + +#include <cflib.h> +#include <netsmb/smb_lib.h> + +int cf_opterr = 1, /* if error message should be printed */ + cf_optind = 1, /* index into parent argv vector */ + cf_optopt, /* character checked for validity */ + cf_optreset; /* reset getopt */ +const char *cf_optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +int +cf_getopt(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + static const char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + int tmpind; + + if (cf_optreset || !*place) { /* update scanning pointer */ + cf_optreset = 0; + tmpind = cf_optind; + while (1) { + if (tmpind >= nargc) { + place = EMSG; + return (-1); + } + if (*(place = nargv[tmpind]) != '-') { + tmpind++; + continue; /* lookup next option */ + } + if (place[1] && *++place == '-') { /* found "--" */ + cf_optind = ++tmpind; + place = EMSG; + return (-1); + } + cf_optind = tmpind; + break; + } + } /* option letter okay? */ + if ((cf_optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, cf_optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (cf_optopt == (int)'-') + return (-1); + if (!*place) + ++cf_optind; + if (cf_opterr && *ostr != ':') + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, + "%s: illegal option -- %c\n"), + __progname, cf_optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + cf_optarg = NULL; + if (!*place) + ++cf_optind; + } else { /* need an argument */ + if (*place) /* no white space */ + cf_optarg = place; + else if (nargc <= ++cf_optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (cf_opterr) + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, + "%s: option requires an argument -- %c\n"), + __progname, cf_optopt); + return (BADCH); + } else /* white space */ + cf_optarg = nargv[cf_optind]; + place = EMSG; + ++cf_optind; + } + return (cf_optopt); /* dump back option letter */ +} diff --git a/usr/src/lib/libsmbfs/smb/charsets.c b/usr/src/lib/libsmbfs/smb/charsets.c new file mode 100644 index 0000000000..7000be5416 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/charsets.c @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2001 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* @(#)charsets.c * + * (c) 2004 Apple Computer, Inc. All Rights Reserved + * + * + * charsets.c -- Routines converting between UTF-8, 16-bit + * little-endian Unicode, and various Windows + * code pages. + * + * MODIFICATION HISTORY: + * 28-Nov-2004 Guy Harris New today + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <iconv.h> +#include <langinfo.h> +#include <strings.h> + +#ifdef NOTPORTED +#include <CoreFoundation/CoreFoundation.h> +#include <CoreFoundation/CFStringDefaultEncoding.h> +#include <CoreFoundation/CFStringEncodingConverter.h> +#include <sys/mchain.h> +#endif /* NOTPORTED */ + +#include <netsmb/smb_lib.h> +#include <netsmb/mchain.h> + +#include "charsets.h" + +#ifdef NOTPORTED +extern uid_t real_uid,eff_uid; +#endif /* NOTPORTED */ + +/* + * On Solaris, we will need to do some rewriting to use our iconv + * routines for the conversions. For now, we're effectively + * stubbing out code, leaving the details of what happens on + * Darwin in case it's useful as a guide later. + */ + +static unsigned +xtoi(char u) +{ + if (isdigit(u)) + return (u - '0'); + else if (islower(u)) + return (10 + u - 'a'); + else if (isupper(u)) + return (10 + u - 'A'); + return (16); +} + + +/* Removes the "%" escape sequences from a URL component. + * See IETF RFC 2396. + */ +char * +unpercent(char * component) +{ + char c, *s; + unsigned hi, lo; + + if (component) + for (s = component; (c = *s) != 0; s++) { + if (c != '%') + continue; + if ((hi = xtoi(s[1])) > 15 || (lo = xtoi(s[2])) > 15) + continue; /* ignore invalid escapes */ + s[0] = hi*16 + lo; + /* + * This was strcpy(s + 1, s + 3); + * But nowadays leftward overlapping copies are + * officially undefined in C. Ours seems to + * work or not depending upon alignment. + */ + memmove(s+1, s+3, strlen(s+3) + 1); + } + return (component); +} + +#ifdef NOTPORTED +static CFStringEncoding +get_windows_encoding_equivalent( void ) +{ + + CFStringEncoding encoding; + uint32_t index,region; + + /* important! use root ID so you can read the config file! */ + seteuid(eff_uid); + __CFStringGetInstallationEncodingAndRegion(&index,®ion); + seteuid(real_uid); + + switch ( index ) + { + case kCFStringEncodingMacRoman: + if (region) /* anything nonzero is not US */ + encoding = kCFStringEncodingDOSLatin1; + else /* US region */ + encoding = kCFStringEncodingDOSLatinUS; + break; + + case kCFStringEncodingMacJapanese: + encoding = kCFStringEncodingDOSJapanese; + break; + + case kCFStringEncodingMacChineseTrad: + encoding = kCFStringEncodingDOSChineseTrad; + break; + + case kCFStringEncodingMacKorean: + encoding = kCFStringEncodingDOSKorean; + break; + + case kCFStringEncodingMacArabic: + encoding = kCFStringEncodingDOSArabic; + break; + + case kCFStringEncodingMacHebrew: + encoding = kCFStringEncodingDOSHebrew; + break; + + case kCFStringEncodingMacGreek: + encoding = kCFStringEncodingDOSGreek; + break; + + case kCFStringEncodingMacCyrillic: + encoding = kCFStringEncodingDOSCyrillic; + break; + + case kCFStringEncodingMacThai: + encoding = kCFStringEncodingDOSThai; + break; + + case kCFStringEncodingMacChineseSimp: + encoding = kCFStringEncodingDOSChineseSimplif; + break; + + case kCFStringEncodingMacCentralEurRoman: + encoding = kCFStringEncodingDOSLatin2; + break; + + case kCFStringEncodingMacTurkish: + encoding = kCFStringEncodingDOSTurkish; + break; + + case kCFStringEncodingMacCroatian: + encoding = kCFStringEncodingDOSLatin2; + break; + + case kCFStringEncodingMacIcelandic: + encoding = kCFStringEncodingDOSIcelandic; + break; + + case kCFStringEncodingMacRomanian: + encoding = kCFStringEncodingDOSLatin2; + break; + + case kCFStringEncodingMacFarsi: + encoding = kCFStringEncodingDOSArabic; + break; + + case kCFStringEncodingMacUkrainian: + encoding = kCFStringEncodingDOSCyrillic; + break; + + default: + encoding = kCFStringEncodingDOSLatin1; + break; + } + + return encoding; +} +#endif /* NOTPORTED */ + +/* + * XXX - NLS, or CF? We should probably use the same routine for all + * conversions. + */ +char * +convert_wincs_to_utf8(const char *windows_string) +{ +#ifdef NOTPORTED + CFStringRef s; + CFIndex maxlen; + char *result; + + s = CFStringCreateWithCString(NULL, windows_string, + get_windows_encoding_equivalent()); + if (s == NULL) { + smb_error("CFStringCreateWithCString for Windows code page failed on \"%s\" ", -1, + windows_string); + + /* kCFStringEncodingMacRoman should always succeed */ + s = CFStringCreateWithCString(NULL, windows_string, + kCFStringEncodingMacRoman); + if (s == NULL) { + smb_error("CFStringCreateWithCString for Windows code page failed on \"%s\" with kCFStringEncodingMacRoman - skipping", + -1, windows_string); + return NULL; + } + } + + maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(s), + kCFStringEncodingUTF8) + 1; + result = malloc(maxlen); + if (result == NULL) { + smb_error("Couldn't allocate buffer for UTF-8 string for \"%s\" - skipping", -1, + windows_string); + CFRelease(s); + return NULL; + } + if (!CFStringGetCString(s, result, maxlen, kCFStringEncodingUTF8)) { + smb_error("CFStringGetCString for UTF-8 failed on \"%s\" - skipping", + -1, windows_string); + CFRelease(s); + return NULL; + } + CFRelease(s); + return result; +#else /* NOTPORTED */ + return ((char*)windows_string); +#endif /* NOTPORTED */ +} + +/* + * XXX - NLS, or CF? We should probably use the same routine for all + * conversions. + */ +char * +convert_utf8_to_wincs(const char *utf8_string) +{ +#ifdef NOTPORTED + CFStringRef s; + CFIndex maxlen; + char *result; + + s = CFStringCreateWithCString(NULL, utf8_string, + kCFStringEncodingUTF8); + if (s == NULL) { + smb_error("CFStringCreateWithCString for UTF-8 failed on \"%s\"", -1, + utf8_string); + return NULL; + } + + maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(s), + get_windows_encoding_equivalent()) + 1; + result = malloc(maxlen); + if (result == NULL) { + smb_error("Couldn't allocate buffer for Windows code page string for \"%s\" - skipping", -1, + utf8_string); + CFRelease(s); + return NULL; + } + if (!CFStringGetCString(s, result, maxlen, + get_windows_encoding_equivalent())) { + smb_error("CFStringGetCString for Windows code page failed on \"%s\" - skipping", + -1, utf8_string); + CFRelease(s); + return NULL; + } + CFRelease(s); + return result; +#else /* NOTPORTED */ + return ((char*)utf8_string); +#endif /* NOTPORTED */ +} + +/* + * Convert little-endian Unicode string to UTF-8. + * Converts the Unicode string to host byte order in place. + */ +char * +convert_leunicode_to_utf8(unsigned short *unicode_string) +{ + unsigned short *unicode_charp, unicode_char; + int len = 0; + + for (unicode_charp = unicode_string; + (unicode_char = *unicode_charp) != 0; + unicode_charp++) { + *unicode_charp = letohs(unicode_char); + len = len + 2; + } + return (convert_unicode_to_utf8(unicode_string, len)); +} + +char * +convert_unicode_to_utf8(unsigned short *unicode_string, int len) +{ + iconv_t cd; + char from[BUFSIZ], to[BUFSIZ]; + char *tptr = NULL; + const char *fptr; + size_t ileft, oleft, ret; + + cd = iconv_open("UTF-8", "UTF-16"); + if (cd != (iconv_t)-1) { + ileft = len; + bcopy((char *)unicode_string, from, ileft); + fptr = from; + oleft = BUFSIZ; + tptr = to; + ret = iconv(cd, &fptr, &ileft, &tptr, &oleft); + if (ret != (size_t)-1) { + to[BUFSIZ-oleft] = '\0'; + tptr = to; + } else { + tptr = NULL; + } + (void) iconv_close(cd); + } + return (tptr); +} + +/* + * Convert UTF-8 string to little-endian Unicode. + */ +unsigned short * +convert_utf8_to_leunicode(const char *utf8_string) +{ +#ifdef NOTPORTED + CFStringRef s; + CFIndex maxlen; + unsigned short *result; + CFRange range; + int i; + + s = CFStringCreateWithCString(NULL, utf8_string, + kCFStringEncodingUTF8); + if (s == NULL) { + smb_error("CFStringCreateWithCString for UTF-8 failed on \"%s\"", -1, + utf8_string); + return NULL; + } + + maxlen = CFStringGetLength(s); + result = malloc(2*(maxlen + 1)); + if (result == NULL) { + smb_error("Couldn't allocate buffer for Unicode string for \"%s\" - skipping", -1, + utf8_string); + CFRelease(s); + return NULL; + } + range.location = 0; + range.length = maxlen; + CFStringGetCharacters(s, range, result); + for (i = 0; i < maxlen; i++) + result[i] = CFSwapInt16HostToLittle(result[i]); + result[maxlen] = 0; + CFRelease(s); + return result; +#else /* NOTPORTED */ + /* LINTED */ /* XXX Really need to fix this! */ + return ((ushort_t *)utf8_string); /* XXX */ +#endif /* NOTPORTED */ +} diff --git a/usr/src/lib/libsmbfs/smb/charsets.h b/usr/src/lib/libsmbfs/smb/charsets.h new file mode 100644 index 0000000000..5518afc783 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/charsets.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2001 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * @(#)charsets.h + * (c) 2004 Apple Computer, Inc. All Rights Reserved + * + * + * charsets.h -- Routines converting between UTF-8, 16-bit + * little-endian Unicode, 16-bit host-byte-order + * Unicode, and various Windows code pages. + * + * MODIFICATION HISTORY: + * 28-Nov-2004 Guy Harris New today + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef __CHARSETS_H__ +#define __CHARSETS_H__ + +extern char *convert_wincs_to_utf8(const char *windows_string); +extern char *convert_utf8_to_wincs(const char *utf8_string); +extern char *convert_leunicode_to_utf8(unsigned short *windows_string); +extern char *convert_unicode_to_utf8(unsigned short *windows_string, int len); +extern unsigned short *convert_utf8_to_leunicode(const char *utf8_string); + +#endif /* __CHARSETS_H__ */ diff --git a/usr/src/lib/libsmbfs/smb/ctx.c b/usr/src/lib/libsmbfs/smb/ctx.c new file mode 100644 index 0000000000..51896bf128 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/ctx.c @@ -0,0 +1,2140 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ctx.c,v 1.32.70.2 2005/06/02 00:55:40 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/byteorder.h> + +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#include <pwd.h> +#include <grp.h> +#include <unistd.h> +#include <libintl.h> +#include <assert.h> +#include <nss_dbdefs.h> + +#include <kerberosv5/krb5.h> +#include <kerberosv5/com_err.h> + +extern uid_t real_uid, eff_uid; + +#define NB_NEEDRESOLVER + +#include <netsmb/smb_lib.h> +#include <netsmb/netbios.h> +#include <netsmb/nb_lib.h> +#include <netsmb/smb_dev.h> +#include <cflib.h> +#include <charsets.h> + +#include <spnego.h> +#include "derparse.h" + +extern MECH_OID g_stcMechOIDList []; + +#define POWEROF2(x) (((x) & ((x)-1)) == 0) + +/* These two may be set by commands. */ +int smb_debug, smb_verbose; + +/* + * This used to call the DCE/RPC code. + * We want more strict layering than this. + * The redirector should simply export a + * remote pipe API, comsumed by dce rpc. + * Make it a no-op for now. + */ +#if 0 +#include <rpc_cleanup.h> +#else +static void +rpc_cleanup_smbctx(struct smb_ctx *ctx) +{ +} +#endif + +void +dump_ctx_flags(int flags) +{ + printf(" Flags: "); + if (flags == 0) + printf("0"); + if (flags & SMBCF_NOPWD) + printf("NOPWD "); + if (flags & SMBCF_SRIGHTS) + printf("SRIGHTS "); + if (flags & SMBCF_LOCALE) + printf("LOCALE "); + if (flags & SMBCF_CMD_DOM) + printf("CMD_DOM "); + if (flags & SMBCF_CMD_USR) + printf("CMD_USR "); + if (flags & SMBCF_CMD_PW) + printf("CMD_PW "); + if (flags & SMBCF_RESOLVED) + printf("RESOLVED "); + if (flags & SMBCF_KCBAD) + printf("KCBAD "); + if (flags & SMBCF_KCFOUND) + printf("KCFOUND "); + if (flags & SMBCF_BROWSEOK) + printf("BROWSEOK "); + if (flags & SMBCF_AUTHREQ) + printf("AUTHREQ "); + if (flags & SMBCF_KCSAVE) + printf("KCSAVE "); + if (flags & SMBCF_XXX) + printf("XXX "); + if (flags & SMBCF_SSNACTIVE) + printf("SSNACTIVE "); + if (flags & SMBCF_KCDOMAIN) + printf("KCDOMAIN "); + printf("\n"); +} + +void +dump_ctx_ssn(struct smbioc_ossn *ssn) +{ + printf(" srvname=\"%s\", dom=\"%s\", user=\"%s\", password=%s\n", + ssn->ioc_srvname, ssn->ioc_workgroup, ssn->ioc_user, + ssn->ioc_password[0] ? "(non-null)" : "NULL"); + printf(" timeout=%d, retry=%d, owner=%d, group=%d\n", + ssn->ioc_timeout, ssn->ioc_retrycount, + ssn->ioc_owner, ssn->ioc_group); +} + +void +dump_ctx_sh(struct smbioc_oshare *sh) +{ + printf(" share_name=\"%s\", share_pw=\"%s\"\n", + sh->ioc_share, sh->ioc_password); +} + +void +dump_ctx(char *where, struct smb_ctx *ctx) +{ + printf("context %s:\n", where); + dump_ctx_flags(ctx->ct_flags); + + printf(" localname=\"%s\"", ctx->ct_locname); + + if (ctx->ct_fullserver) + printf(" fullserver=\"%s\"", ctx->ct_fullserver); + else + printf(" fullserver=NULL"); + + if (ctx->ct_srvaddr) + printf(" srvaddr=\"%s\"\n", ctx->ct_srvaddr); + else + printf(" srvaddr=NULL\n"); + + dump_ctx_ssn(&ctx->ct_ssn); + dump_ctx_sh(&ctx->ct_sh); +} + +/* + * Initialize an smb_ctx struct. + * + * The sequence for getting all the members filled in + * has some tricky aspects. Here's how it works: + * + * The search order for options is as follows: + * command line options + * values parsed from UNC path (cmd) + * values from RC file (per-user) + * values from SMF (system-wide) + * built-in defaults + * + * Normally, one would simply get all the values starting with + * the bottom of the above list and working to the top, and + * overwriting values as you go. But we need an exception. + * + * In this function, we parse the UNC path and command line options, + * because we need (at least) the server name when we're getting the + * SMF and RC file values. However, values we get from the command + * should not be overwritten by SMF or RC file parsing, so we mark + * values from the command as "from CMD" and the RC file parser + * leaves in place any values so marked. See: SMBCF_CMD_* + * + * The semantics of these flags are: "This value came from the + * current command instance, not from sources that may apply to + * multiple commands." (Different from the old "FROMUSR" flag.) + * + * Note that smb_ctx_opt() is called later to handle the + * remaining options, which should be ignored here. + * The (magic) leading ":" in cf_getopt() makes it + * ignore options not in the options string. + */ +int +smb_ctx_init(struct smb_ctx *ctx, int argc, char *argv[], + int minlevel, int maxlevel, int sharetype) +{ + int opt, error = 0; + const char *arg, *cp; + struct passwd pw; + char pwbuf[NSS_BUFLEN_PASSWD]; + int aflg = 0, uflg = 0; + + bzero(ctx, sizeof (*ctx)); + if (sharetype == SMB_ST_DISK) + ctx->ct_flags |= SMBCF_BROWSEOK; + error = nb_ctx_create(&ctx->ct_nb); + if (error) + return (error); + + ctx->ct_fd = -1; + ctx->ct_parsedlevel = SMBL_NONE; + ctx->ct_minlevel = minlevel; + ctx->ct_maxlevel = maxlevel; + + ctx->ct_ssn.ioc_opt = SMBVOPT_CREATE | SMBVOPT_MINAUTH_NTLM; + ctx->ct_ssn.ioc_timeout = 15; + ctx->ct_ssn.ioc_retrycount = 4; + ctx->ct_ssn.ioc_owner = SMBM_ANY_OWNER; + ctx->ct_ssn.ioc_group = SMBM_ANY_GROUP; + ctx->ct_ssn.ioc_mode = SMBM_EXEC; + ctx->ct_ssn.ioc_rights = SMBM_DEFAULT; + + ctx->ct_sh.ioc_opt = SMBVOPT_CREATE; + ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER; + ctx->ct_sh.ioc_group = SMBM_ANY_GROUP; + ctx->ct_sh.ioc_mode = SMBM_EXEC; + ctx->ct_sh.ioc_rights = SMBM_DEFAULT; + ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER; + ctx->ct_sh.ioc_group = SMBM_ANY_GROUP; + + nb_ctx_setscope(ctx->ct_nb, ""); + + /* + * if the user name is not specified some other way, + * use the current user name (built-in default) + */ + if (getpwuid_r(geteuid(), &pw, pwbuf, sizeof (pwbuf)) != NULL) + smb_ctx_setuser(ctx, pw.pw_name, 0); + + /* + * Set a built-in default domain (workgroup). + * XXX: What's the best default? Use "?" instead? + * Using the Windows/NT default for now. + */ + smb_ctx_setworkgroup(ctx, "WORKGROUP", 0); + + /* + * Parse the UNC path. Values from here are + * marked as "from CMD". + */ + if (argv == NULL) + goto done; + for (opt = 1; opt < argc; opt++) { + cp = argv[opt]; + if (strncmp(cp, "//", 2) != 0) + continue; + error = smb_ctx_parseunc(ctx, cp, sharetype, &cp); + if (error) + return (error); + break; + } + + /* + * Parse options, if any. Values from here too + * are marked as "from CMD". + */ + while (error == 0 && (opt = cf_getopt(argc, argv, ":AU:E:L:")) != -1) { + arg = cf_optarg; + switch (opt) { + case 'A': + aflg = 1; + error = smb_ctx_setuser(ctx, "", TRUE); + error = smb_ctx_setpassword(ctx, "", TRUE); + ctx->ct_flags |= SMBCF_NOPWD; + break; + case 'E': +#if 0 /* We don't support any "charset" stuff. (ignore -E) */ + error = smb_ctx_setcharset(ctx, arg); + if (error) + return (error); +#endif + break; + case 'L': +#if 0 /* Use the standard environment variables (ignore -L) */ + error = nls_setlocale(optarg); + if (error) + break; +#endif + break; + case 'U': + uflg = 1; + error = smb_ctx_setuser(ctx, arg, TRUE); + break; + } + } + if (aflg && uflg) { + printf(gettext("-A and -U flags are exclusive.\n")); + return (1); + } + cf_optind = cf_optreset = 1; + +done: + if (smb_debug) + dump_ctx("after smb_ctx_init", ctx); + + return (error); +} + +void +smb_ctx_done(struct smb_ctx *ctx) +{ + + rpc_cleanup_smbctx(ctx); + + /* Kerberos stuff. See smb_ctx_krb5init() */ + if (ctx->ct_krb5ctx) { + if (ctx->ct_krb5cp) + krb5_free_principal(ctx->ct_krb5ctx, ctx->ct_krb5cp); + krb5_free_context(ctx->ct_krb5ctx); + } + + if (ctx->ct_fd != -1) + close(ctx->ct_fd); +#if 0 /* XXX: not pointers anymore */ + if (&ctx->ct_ssn.ioc_server) + nb_snbfree(&ctx->ct_ssn.ioc_server); + if (&ctx->ct_ssn.ioc_local) + nb_snbfree(&ctx->ct_ssn.ioc_local); +#endif + if (ctx->ct_srvaddr) + free(ctx->ct_srvaddr); + if (ctx->ct_nb) + nb_ctx_done(ctx->ct_nb); + if (ctx->ct_secblob) + free(ctx->ct_secblob); + if (ctx->ct_origshare) + free(ctx->ct_origshare); + if (ctx->ct_fullserver) + free(ctx->ct_fullserver); +} + +static int +getsubstring(const char *p, uchar_t sep, char *dest, int maxlen, + const char **next) +{ + int len; + + maxlen--; + for (len = 0; len < maxlen && *p != sep; p++, len++, dest++) { + if (*p == 0) + return (EINVAL); + *dest = *p; + } + *dest = 0; + *next = *p ? p + 1 : p; + return (0); +} + +/* + * Parse the UNC path. Here we expect something like + * "//[workgroup;][user[:password]@]host[/share[/path]]" + * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt + * Values found here are marked as "from CMD". + */ +int +smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype, + const char **next) +{ + const char *p = unc; + char *p1, *colon, *servername; + char tmp[1024]; + char tmp2[1024]; + int error; + + ctx->ct_parsedlevel = SMBL_NONE; + if (*p++ != '/' || *p++ != '/') { + smb_error(dgettext(TEXT_DOMAIN, + "UNC should start with '//'"), 0); + return (EINVAL); + } + p1 = tmp; + error = getsubstring(p, ';', p1, sizeof (tmp), &p); + if (!error) { + if (*p1 == 0) { + smb_error(dgettext(TEXT_DOMAIN, + "empty workgroup name"), 0); + return (EINVAL); + } + nls_str_upper(tmp, tmp); + error = smb_ctx_setworkgroup(ctx, unpercent(tmp), TRUE); + if (error) + return (error); + } + colon = (char *)p; + error = getsubstring(p, '@', p1, sizeof (tmp), &p); + if (!error) { + if (ctx->ct_maxlevel < SMBL_VC) { + smb_error(dgettext(TEXT_DOMAIN, + "no user name required"), 0); + return (EINVAL); + } + p1 = strchr(tmp, ':'); + if (p1) { + colon += p1 - tmp; + *p1++ = (char)0; + error = smb_ctx_setpassword(ctx, unpercent(p1), TRUE); + if (error) + return (error); + if (p - colon > 2) + memset(colon+1, '*', p - colon - 2); + } + p1 = tmp; + if (*p1 == 0) { + smb_error(dgettext(TEXT_DOMAIN, + "empty user name"), 0); + return (EINVAL); + } + error = smb_ctx_setuser(ctx, unpercent(tmp), TRUE); + if (error) + return (error); + ctx->ct_parsedlevel = SMBL_VC; + } + error = getsubstring(p, '/', p1, sizeof (tmp), &p); + if (error) { + error = getsubstring(p, '\0', p1, sizeof (tmp), &p); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "no server name found"), 0); + return (error); + } + } + if (*p1 == 0) { + smb_error(dgettext(TEXT_DOMAIN, "empty server name"), 0); + return (EINVAL); + } + + + /* + * It's safe to uppercase this string, which + * consists of ascii characters that should + * be uppercased, %s, and ascii characters representing + * hex digits 0-9 and A-F (already uppercased, and + * if not uppercased they need to be). However, + * it is NOT safe to uppercase after it has been + * converted, below! + */ + + nls_str_upper(tmp2, tmp); + + /* + * scan for % in the string. + * If we find one, convert + * to the assumed codepage. + */ + + if (strchr(tmp2, '%')) { + /* use the 1st buffer, we don't need the old string */ + servername = tmp; + if (!(servername = convert_utf8_to_wincs(unpercent(tmp2)))) { + smb_error(dgettext(TEXT_DOMAIN, "bad server name"), 0); + return (EINVAL); + } + /* + * Converts utf8 to win equivalent of + * what is configured on this machine. + * Note that we are assuming this is the + * encoding used on the server, and that + * assumption might be incorrect. This is + * the best we can do now, and we should + * move to use port 445 to avoid having + * to worry about server codepages. + */ + } else /* no conversion needed */ + servername = tmp2; + + smb_ctx_setserver(ctx, servername); + error = smb_ctx_setfullserver(ctx, servername); + + if (error) + return (error); + if (sharetype == SMB_ST_NONE) { + *next = p; + return (0); + } + if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) { + smb_error(dgettext(TEXT_DOMAIN, "no share name required"), 0); + return (EINVAL); + } + error = getsubstring(p, '/', p1, sizeof (tmp), &p); + if (error) { + error = getsubstring(p, '\0', p1, sizeof (tmp), &p); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "unexpected end of line"), 0); + return (error); + } + } + if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE && + !(ctx->ct_flags & SMBCF_BROWSEOK)) { + smb_error(dgettext(TEXT_DOMAIN, "empty share name"), 0); + return (EINVAL); + } + *next = p; + if (*p1 == 0) + return (0); + error = smb_ctx_setshare(ctx, unpercent(p1), sharetype); + return (error); +} + +int +smb_ctx_setcharset(struct smb_ctx *ctx, const char *arg) +{ + char *cp, *servercs, *localcs; + int cslen = sizeof (ctx->ct_ssn.ioc_localcs); + int scslen, lcslen, error; + + cp = strchr(arg, ':'); + lcslen = cp ? (cp - arg) : 0; + if (lcslen == 0 || lcslen >= cslen) { + smb_error(dgettext(TEXT_DOMAIN, + "invalid local charset specification (%s)"), 0, arg); + return (EINVAL); + } + scslen = (size_t)strlen(++cp); + if (scslen == 0 || scslen >= cslen) { + smb_error(dgettext(TEXT_DOMAIN, + "invalid server charset specification (%s)"), 0, arg); + return (EINVAL); + } + localcs = memcpy(ctx->ct_ssn.ioc_localcs, arg, lcslen); + localcs[lcslen] = 0; + servercs = strcpy(ctx->ct_ssn.ioc_servercs, cp); + error = nls_setrecode(localcs, servercs); + if (error == 0) + return (0); + smb_error(dgettext(TEXT_DOMAIN, + "can't initialize iconv support (%s:%s)"), + error, localcs, servercs); + localcs[0] = 0; + servercs[0] = 0; + return (error); +} + +int +smb_ctx_setfullserver(struct smb_ctx *ctx, const char *name) +{ + ctx->ct_fullserver = strdup(name); + if (ctx->ct_fullserver == NULL) + return (ENOMEM); + return (0); +} + +/* + * XXX TODO FIXME etc etc + * If the call to nbns_getnodestatus(...) fails we can try one of two other + * methods; use a name of "*SMBSERVER", which is supported by Samba (at least) + * or, as a last resort, try the "truncate-at-dot" heuristic. + * And the heuristic really should attempt truncation at + * each dot in turn, left to right. + * + * These fallback heuristics should be triggered when the attempt to open the + * session fails instead of in the code below. + * + * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt + */ +int +smb_ctx_getnbname(struct smb_ctx *ctx, struct sockaddr *sap) +{ + char server[SMB_MAXSRVNAMELEN + 1]; + char workgroup[SMB_MAXUSERNAMELEN + 1]; + int error; +#if 0 + char *dot; +#endif + + server[0] = workgroup[0] = '\0'; + error = nbns_getnodestatus(sap, ctx->ct_nb, server, workgroup); + if (error == 0) { + /* + * Used to set our domain name to be the same as + * the server's domain name. Unnecessary at best, + * and wrong for accounts in a trusted domain. + */ +#ifdef APPLE + if (workgroup[0] && !ctx->ct_ssn.ioc_workgroup[0]) + smb_ctx_setworkgroup(ctx, workgroup, 0); +#endif + if (server[0]) + smb_ctx_setserver(ctx, server); + } else { + if (smb_verbose) + smb_error(dgettext(TEXT_DOMAIN, + "Failed to get NetBIOS node status."), 0); + if (ctx->ct_ssn.ioc_srvname[0] == (char)0) + smb_ctx_setserver(ctx, "*SMBSERVER"); + } +#if 0 + if (server[0] == (char)0) { + dot = strchr(ctx->ct_fullserver, '.'); + if (dot) + *dot = '\0'; + if (strlen(ctx->ct_fullserver) <= SMB_MAXSRVNAMELEN) { + /* + * don't uppercase the server name. it comes from + * NBNS and uppercasing can clobber the characters + */ + strcpy(ctx->ct_ssn.ioc_srvname, ctx->ct_fullserver); + error = 0; + } else { + error = -1; + } + if (dot) + *dot = '.'; + } +#endif + return (error); +} + +/* this routine does not uppercase the server name */ +void +smb_ctx_setserver(struct smb_ctx *ctx, const char *name) +{ + /* don't uppercase the server name */ + if (strlen(name) > SMB_MAXSRVNAMELEN) { /* NB limit is 15 */ + ctx->ct_ssn.ioc_srvname[0] = '\0'; + } else + strcpy(ctx->ct_ssn.ioc_srvname, name); +} + +int +smb_ctx_setuser(struct smb_ctx *ctx, const char *name, int from_cmd) +{ + + if (strlen(name) >= SMB_MAXUSERNAMELEN) { + smb_error(dgettext(TEXT_DOMAIN, + "user name '%s' too long"), 0, name); + return (ENAMETOOLONG); + } + + /* + * Don't overwrite a value from the command line + * with one from anywhere else. + */ + if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_USR)) + return (0); + + /* don't uppercase the username, just copy it. */ + strcpy(ctx->ct_ssn.ioc_user, name); + + /* Mark this as "from the command line". */ + if (from_cmd) + ctx->ct_flags |= SMBCF_CMD_USR; + + return (0); +} + +/* + * Never uppercase the workgroup + * name here, because it might come + * from a Windows codepage encoding. + * + * Don't overwrite a domain name from the + * command line with one from anywhere else. + * See smb_ctx_init() for notes about this. + */ +int +smb_ctx_setworkgroup(struct smb_ctx *ctx, const char *name, int from_cmd) +{ + + if (strlen(name) >= SMB_MAXUSERNAMELEN) { + smb_error(dgettext(TEXT_DOMAIN, + "workgroup name '%s' too long"), 0, name); + return (ENAMETOOLONG); + } + + /* + * Don't overwrite a value from the command line + * with one from anywhere else. + */ + if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_DOM)) + return (0); + + strcpy(ctx->ct_ssn.ioc_workgroup, name); + + /* Mark this as "from the command line". */ + if (from_cmd) + ctx->ct_flags |= SMBCF_CMD_DOM; + + return (0); +} + +int +smb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd, int from_cmd) +{ + + if (passwd == NULL) /* XXX Huh? */ + return (EINVAL); + if (strlen(passwd) >= SMB_MAXPASSWORDLEN) { + smb_error(dgettext(TEXT_DOMAIN, "password too long"), 0); + return (ENAMETOOLONG); + } + + /* + * Don't overwrite a value from the command line + * with one from anywhere else. + */ + if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_PW)) + return (0); + + if (strncmp(passwd, "$$1", 3) == 0) + smb_simpledecrypt(ctx->ct_ssn.ioc_password, passwd); + else + strcpy(ctx->ct_ssn.ioc_password, passwd); + strcpy(ctx->ct_sh.ioc_password, ctx->ct_ssn.ioc_password); + + /* Mark this as "from the command line". */ + if (from_cmd) + ctx->ct_flags |= SMBCF_CMD_PW; + + return (0); +} + +int +smb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype) +{ + if (strlen(share) >= SMB_MAXSHARENAMELEN) { + smb_error(dgettext(TEXT_DOMAIN, + "share name '%s' too long"), 0, share); + return (ENAMETOOLONG); + } + if (ctx->ct_origshare) + free(ctx->ct_origshare); + if ((ctx->ct_origshare = strdup(share)) == NULL) + return (ENOMEM); + nls_str_upper(ctx->ct_sh.ioc_share, share); + if (share[0] != 0) + ctx->ct_parsedlevel = SMBL_SHARE; + ctx->ct_sh.ioc_stype = stype; + return (0); +} + +int +smb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr) +{ + if (addr == NULL || addr[0] == 0) + return (EINVAL); + if (ctx->ct_srvaddr) + free(ctx->ct_srvaddr); + if ((ctx->ct_srvaddr = strdup(addr)) == NULL) + return (ENOMEM); + return (0); +} + +static int +smb_parse_owner(char *pair, uid_t *uid, gid_t *gid) +{ + struct group gr; + struct passwd pw; + char buf[NSS_BUFLEN_PASSWD]; + char *cp; + + cp = strchr(pair, ':'); + if (cp) { + *cp++ = '\0'; + if (*cp) { + if (getgrnam_r(cp, &gr, buf, sizeof (buf)) != NULL) { + *gid = gr.gr_gid; + } else + smb_error(dgettext(TEXT_DOMAIN, + "Invalid group name %s, ignored"), 0, cp); + } + } + if (*pair) { + if (getpwnam_r(pair, &pw, buf, sizeof (buf)) != NULL) { + *uid = pw.pw_uid; + } else + smb_error(dgettext(TEXT_DOMAIN, + "Invalid user name %s, ignored"), 0, pair); + } + + return (0); +} + +/* + * Commands use this with getopt. See: + * STDPARAM_OPT, STDPARAM_ARGS + * Called after smb_ctx_readrc(). + */ +int +smb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg) +{ + int error = 0; + char *p, *cp; + char tmp[1024]; + + switch (opt) { + case 'A': + case 'U': + /* Handled in smb_ctx_init() */ + break; + case 'I': + error = smb_ctx_setsrvaddr(ctx, arg); + break; + case 'M': + ctx->ct_ssn.ioc_rights = strtol(arg, &cp, 8); + if (*cp == '/') { + ctx->ct_sh.ioc_rights = strtol(cp + 1, &cp, 8); + ctx->ct_flags |= SMBCF_SRIGHTS; + } + break; + case 'N': + ctx->ct_flags |= SMBCF_NOPWD; + break; + case 'O': + p = strdup(arg); + cp = strchr(p, '/'); + if (cp) { + *cp++ = '\0'; + error = smb_parse_owner(cp, &ctx->ct_sh.ioc_owner, + &ctx->ct_sh.ioc_group); + } + if (*p && error == 0) { + error = smb_parse_owner(cp, &ctx->ct_ssn.ioc_owner, + &ctx->ct_ssn.ioc_group); + } + free(p); + break; + case 'P': +/* ctx->ct_ssn.ioc_opt |= SMBCOPT_PERMANENT; */ + break; + case 'R': + ctx->ct_ssn.ioc_retrycount = atoi(arg); + break; + case 'T': + ctx->ct_ssn.ioc_timeout = atoi(arg); + break; + case 'W': + nls_str_upper(tmp, arg); + error = smb_ctx_setworkgroup(ctx, tmp, TRUE); + break; + } + return (error); +} + +#if 0 +static void +smb_hexdump(const uchar_t *buf, int len) { + int ofs = 0; + + while (len--) { + if (ofs % 16 == 0) + printf("\n%02X: ", ofs); + printf("%02x ", *buf++); + ofs++; + } + printf("\n"); +} +#endif + + +static int +smb_addiconvtbl(const char *to, const char *from, const uchar_t *tbl) +{ + int error; + + /* + * Not able to find out what is the work of this routine till + * now. Still investigating. + * REVISIT + */ +#ifdef KICONV_SUPPORT + error = kiconv_add_xlat_table(to, from, tbl); + if (error && error != EEXIST) { + smb_error(dgettext(TEXT_DOMAIN, + "can not setup kernel iconv table (%s:%s)"), + error, from, to); + return (error); + } +#endif + return (0); +} + +/* + * Verify context before connect operation(s), + * lookup specified server and try to fill all forgotten fields. + */ +int +smb_ctx_resolve(struct smb_ctx *ctx) +{ + struct smbioc_ossn *ssn = &ctx->ct_ssn; + struct smbioc_oshare *sh = &ctx->ct_sh; + struct nb_name nn; + struct sockaddr *sap; + struct sockaddr_nb *salocal, *saserver; + char *cp; + uchar_t cstbl[256]; + uint_t i; + int error = 0; + int browseok = ctx->ct_flags & SMBCF_BROWSEOK; + int renego = 0; + + ctx->ct_flags &= ~SMBCF_RESOLVED; + if (isatty(STDIN_FILENO)) + browseok = 0; + if (ctx->ct_fullserver == NULL || ctx->ct_fullserver[0] == 0) { + smb_error(dgettext(TEXT_DOMAIN, + "no server name specified"), 0); + return (EINVAL); + } + if (ctx->ct_minlevel >= SMBL_SHARE && sh->ioc_share[0] == 0 && + !browseok) { + smb_error(dgettext(TEXT_DOMAIN, + "no share name specified for %s@%s"), + 0, ssn->ioc_user, ssn->ioc_srvname); + return (EINVAL); + } + error = nb_ctx_resolve(ctx->ct_nb); + if (error) + return (error); + if (ssn->ioc_localcs[0] == 0) + strcpy(ssn->ioc_localcs, "default"); /* XXX: locale name ? */ + error = smb_addiconvtbl("tolower", ssn->ioc_localcs, nls_lower); + if (error) + return (error); + error = smb_addiconvtbl("toupper", ssn->ioc_localcs, nls_upper); + if (error) + return (error); + if (ssn->ioc_servercs[0] != 0) { + for (i = 0; i < sizeof (cstbl); i++) + cstbl[i] = i; + nls_mem_toext(cstbl, cstbl, sizeof (cstbl)); + error = smb_addiconvtbl(ssn->ioc_servercs, ssn->ioc_localcs, + cstbl); + if (error) + return (error); + for (i = 0; i < sizeof (cstbl); i++) + cstbl[i] = i; + nls_mem_toloc(cstbl, cstbl, sizeof (cstbl)); + error = smb_addiconvtbl(ssn->ioc_localcs, ssn->ioc_servercs, + cstbl); + if (error) + return (error); + } + /* + * If we have an explicit address set for the server in + * an "addr=X" setting in .nsmbrc or SMF, just try using a + * gethostbyname() lookup for it. + */ + if (ctx->ct_srvaddr) { + error = nb_resolvehost_in(ctx->ct_srvaddr, &sap); + if (error == 0) + (void) smb_ctx_getnbname(ctx, sap); + } else + error = -1; + + /* + * Next try a gethostbyname() lookup on the original user- + * specified server name. This is similar to Windows + * NBT option "Use DNS for name resolution." + */ + if (error && ctx->ct_fullserver) { + error = nb_resolvehost_in(ctx->ct_fullserver, &sap); + if (error == 0) + (void) smb_ctx_getnbname(ctx, sap); + } + + /* + * Finally, try the shorter, upper-cased ssn->ioc_srvname + * with a NBNS/WINS lookup if the "nbns_enable" property is + * true (the default). nbns_resolvename() may unicast to the + * "nbns" server or broadcast on the subnet. + */ + if (error && ssn->ioc_srvname[0] && + ctx->ct_nb->nb_flags & NBCF_NS_ENABLE) { + error = nbns_resolvename(ssn->ioc_srvname, + ctx->ct_nb, &sap); + /* + * Used to get the NetBIOS node status here. + * Not necessary (we have the NetBIOS name). + */ + } + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "can't get server address"), error); + return (error); + } + + /* XXX: no nls_str_upper(ssn->ioc_srvname) here? */ + + assert(sizeof (nn.nn_name) == sizeof (ssn->ioc_srvname)); + memcpy(nn.nn_name, ssn->ioc_srvname, NB_NAMELEN); + nn.nn_type = NBT_SERVER; + nn.nn_scope = ctx->ct_nb->nb_scope; + + error = nb_sockaddr(sap, &nn, &saserver); + memcpy(&ctx->ct_srvinaddr, sap, sizeof (struct sockaddr_in)); + nb_snbfree(sap); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "can't allocate server address"), error); + return (error); + } + /* We know it's a NetBIOS address here. */ + bcopy(saserver, &ssn->ioc_server.nb, + sizeof (struct sockaddr_nb)); + if (ctx->ct_locname[0] == 0) { + error = nb_getlocalname(ctx->ct_locname, + SMB_MAXUSERNAMELEN + 1); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "can't get local name"), error); + return (error); + } + nls_str_upper(ctx->ct_locname, ctx->ct_locname); + } + + /* XXX: no nls_str_upper(ctx->ct_locname); here? */ + + memcpy(nn.nn_name, ctx->ct_locname, NB_NAMELEN); + nn.nn_type = NBT_WKSTA; + nn.nn_scope = ctx->ct_nb->nb_scope; + + error = nb_sockaddr(NULL, &nn, &salocal); + if (error) { + nb_snbfree((struct sockaddr *)saserver); + smb_error(dgettext(TEXT_DOMAIN, + "can't allocate local address"), error); + return (error); + } + + /* We know it's a NetBIOS address here. */ + bcopy(salocal, &ssn->ioc_local.nb, + sizeof (struct sockaddr_nb)); + + error = smb_ctx_negotiate(ctx, SMBL_SHARE, SMBLK_CREATE, + ssn->ioc_workgroup); + if (error) + return (error); + ctx->ct_flags &= ~SMBCF_AUTHREQ; + if (!ctx->ct_secblob && browseok && !sh->ioc_share[0] && + !(ctx->ct_flags & SMBCF_XXX)) { + /* assert: anon share list is subset of overall server shares */ + error = smb_browse(ctx, 1); + if (error) /* user cancel or other error? */ + return (error); + /* + * A share was selected, authenticate button was pressed, + * or anon-authentication failed getting browse list. + */ + } + if ((ctx->ct_secblob == NULL) && (ctx->ct_flags & SMBCF_AUTHREQ || + (ssn->ioc_password[0] == '\0' && + !(ctx->ct_flags & SMBCF_NOPWD)))) { +reauth: + /* + * This function is implemented in both + * ui-apple.c and ui-sun.c so let's try to + * keep the same interface. Not sure why + * they didn't just pass ssn here. + */ + error = smb_get_authentication( + ssn->ioc_workgroup, sizeof (ssn->ioc_workgroup) - 1, + ssn->ioc_user, sizeof (ssn->ioc_user) - 1, + ssn->ioc_password, sizeof (ssn->ioc_password) - 1, + ssn->ioc_srvname, ctx); + if (error) + return (error); + } + /* + * if we have a session it is either anonymous + * or from a stale authentication. re-negotiating + * gets us ready for a fresh session + */ + if (ctx->ct_flags & SMBCF_SSNACTIVE || renego) { + renego = 0; + /* don't clobber workgroup name, pass null arg */ + error = smb_ctx_negotiate(ctx, SMBL_SHARE, SMBLK_CREATE, NULL); + if (error) + return (error); + } + if (browseok && !sh->ioc_share[0]) { + ctx->ct_flags &= ~SMBCF_AUTHREQ; + error = smb_browse(ctx, 0); + if (ctx->ct_flags & SMBCF_KCFOUND && smb_autherr(error)) { + smb_error(dgettext(TEXT_DOMAIN, + "smb_ctx_resolve: bad keychain entry"), 0); + ctx->ct_flags |= SMBCF_KCBAD; + renego = 1; + goto reauth; + } + if (error) /* auth, user cancel, or other error */ + return (error); + /* + * Re-authenticate button was pressed? + */ + if (ctx->ct_flags & SMBCF_AUTHREQ) + goto reauth; + if (!sh->ioc_share[0] && !(ctx->ct_flags & SMBCF_XXX)) { + smb_error(dgettext(TEXT_DOMAIN, + "no share specified for %s@%s"), + 0, ssn->ioc_user, ssn->ioc_srvname); + return (EINVAL); + } + } + ctx->ct_flags |= SMBCF_RESOLVED; + + if (smb_debug) + dump_ctx("after smb_ctx_resolve", ctx); + + return (0); +} + +int +smb_open_driver() +{ + char buf[20]; + int err, fd, i; + uint32_t version; + + /* + * First try to open as clone + */ + fd = open("/dev/"NSMB_NAME, O_RDWR); + if (fd >= 0) + goto opened; + + err = errno; /* from open */ +#ifdef APPLE + /* + * well, no clone capabilities available - we have to scan + * all devices in order to get free one + */ + for (i = 0; i < 1024; i++) { + snprintf(buf, sizeof (buf), "/dev/%s%d", NSMB_NAME, i); + fd = open(buf, O_RDWR); + if (fd >= 0) + goto opened; + if (i && POWEROF2(i+1)) + smb_error(dgettext(TEXT_DOMAIN, + "%d failures to open smb device"), errno, i+1); + } + err = ENOENT; +#endif + smb_error(dgettext(TEXT_DOMAIN, + "failed to open %s"), err, "/dev/" NSMB_NAME); + return (-1); + +opened: + /* + * Check the driver version (paranoia) + * Do this BEFORE any other ioctl calls. + */ + if (ioctl(fd, SMBIOC_GETVERS, &version) < 0) { + err = errno; + smb_error(dgettext(TEXT_DOMAIN, + "failed to get driver version"), err); + close(fd); + return (-1); + } + if (version != NSMB_VERSION) { + smb_error(dgettext(TEXT_DOMAIN, + "incorrect driver version"), 0); + close(fd); + return (-1); + } + + return (fd); +} + +static int +smb_ctx_gethandle(struct smb_ctx *ctx) +{ + int err, fd; + + if (ctx->ct_fd != -1) { + rpc_cleanup_smbctx(ctx); + close(ctx->ct_fd); + ctx->ct_fd = -1; + ctx->ct_flags &= ~SMBCF_SSNACTIVE; + } + + fd = smb_open_driver(); + if (fd < 0) + return (ENODEV); + + ctx->ct_fd = fd; + return (0); +} + +int +smb_ctx_ioctl(struct smb_ctx *ctx, int inum, struct smbioc_lookup *rqp) +{ + size_t siz = DEF_SEC_TOKEN_LEN; + int rc = 0; + struct sockaddr sap1, sap2; + int i; + + if (rqp->ioc_ssn.ioc_outtok) + free(rqp->ioc_ssn.ioc_outtok); + rqp->ioc_ssn.ioc_outtoklen = siz; + rqp->ioc_ssn.ioc_outtok = malloc(siz+1); + if (rqp->ioc_ssn.ioc_outtok == NULL) + return (ENOMEM); + bzero(rqp->ioc_ssn.ioc_outtok, siz+1); + /* Note: No longer put length in outtok[0] */ + /* *((int *)rqp->ioc_ssn.ioc_outtok) = (int)siz; */ + + seteuid(eff_uid); /* restore setuid root briefly */ + if (ioctl(ctx->ct_fd, inum, rqp) == -1) { + rc = errno; + goto out; + } + if (rqp->ioc_ssn.ioc_outtoklen <= siz) + goto out; + + /* + * Operation completed, but our output token wasn't large enough. + * The re-call below only pulls the token from the kernel. + */ + siz = rqp->ioc_ssn.ioc_outtoklen; + free(rqp->ioc_ssn.ioc_outtok); + rqp->ioc_ssn.ioc_outtok = malloc(siz + 1); + if (rqp->ioc_ssn.ioc_outtok == NULL) { + rc = ENOMEM; + goto out; + } + bzero(rqp->ioc_ssn.ioc_outtok, siz+1); + /* Note: No longer put length in outtok[0] */ + /* *((int *)rqp->ioc_ssn.ioc_outtok) = siz; */ + if (ioctl(ctx->ct_fd, inum, rqp) == -1) + rc = errno; +out: + seteuid(real_uid); /* and back to real user */ + return (rc); +} + + +/* + * adds a GSSAPI wrapper + */ +char * +smb_ctx_tkt2gtok(uchar_t *tkt, ulong_t tktlen, + uchar_t **gtokp, ulong_t *gtoklenp) +{ + ulong_t bloblen = tktlen; + ulong_t len; + uchar_t krbapreq[2] = "\x01\x00"; /* see RFC 1964 */ + char *failure; + uchar_t *blob = NULL; /* result */ + uchar_t *b; + + bloblen += sizeof (krbapreq); + bloblen += g_stcMechOIDList[spnego_mech_oid_Kerberos_V5].iLen; + len = bloblen; + bloblen = ASNDerCalcTokenLength(bloblen, bloblen); + failure = dgettext(TEXT_DOMAIN, "smb_ctx_tkt2gtok malloc"); + if (!(blob = malloc(bloblen))) + goto out; + b = blob; + b += ASNDerWriteToken(b, SPNEGO_NEGINIT_APP_CONSTRUCT, NULL, len); + b += ASNDerWriteOID(b, spnego_mech_oid_Kerberos_V5); + memcpy(b, krbapreq, sizeof (krbapreq)); + b += sizeof (krbapreq); + failure = dgettext(TEXT_DOMAIN, "smb_ctx_tkt2gtok insanity check"); + if (b + tktlen != blob + bloblen) + goto out; + memcpy(b, tkt, tktlen); + *gtoklenp = bloblen; + *gtokp = blob; + failure = NULL; +out:; + if (blob && failure) + free(blob); + return (failure); +} + + +/* + * Initialization for Kerberos, pulled out of smb_ctx_principal2tkt. + * This just gets our cached credentials, if we have any. + * Based on the "klist" command. + */ +char * +smb_ctx_krb5init(struct smb_ctx *ctx) +{ + char *failure; + krb5_error_code kerr; + krb5_context kctx = NULL; + krb5_ccache kcc = NULL; + krb5_principal kprin = NULL; + + kerr = krb5_init_context(&kctx); + if (kerr) { + failure = "krb5_init_context"; + goto out; + } + ctx->ct_krb5ctx = kctx; + + /* non-default would instead use krb5_cc_resolve */ + kerr = krb5_cc_default(kctx, &kcc); + if (kerr) { + failure = "krb5_cc_default"; + goto out; + } + ctx->ct_krb5cc = kcc; + + /* + * Get the client principal (ticket), + * or find out if we don't have one. + */ + kerr = krb5_cc_get_principal(kctx, kcc, &kprin); + if (kerr) { + failure = "krb5_cc_get_principal"; + goto out; + } + ctx->ct_krb5cp = kprin; + + if (smb_verbose) { + fprintf(stderr, gettext("Ticket cache: %s:%s\n"), + krb5_cc_get_type(kctx, kcc), + krb5_cc_get_name(kctx, kcc)); + } + failure = NULL; + +out: + return (failure); +} + + +/* + * See "Windows 2000 Kerberos Interoperability" paper by + * Christopher Nebergall. RC4 HMAC is the W2K default but + * Samba support lagged (not due to Samba itself, but due to OS' + * Kerberos implementations.) + * + * Only session enc type should matter, not ticket enc type, + * per Sam Hartman on krbdev. + * + * Preauthentication failure topics in krb-protocol may help here... + * try "John Brezak" and/or "Clifford Neuman" too. + */ +static krb5_enctype kenctypes[] = { + ENCTYPE_ARCFOUR_HMAC, /* defined in Tiger krb5.h */ + ENCTYPE_DES_CBC_MD5, + ENCTYPE_DES_CBC_CRC, + ENCTYPE_NULL +}; + +/* + * Obtain a kerberos ticket... + * (if TLD != "gov" then pray first) + */ +char * +smb_ctx_principal2tkt( + struct smb_ctx *ctx, char *prin, + uchar_t **tktp, ulong_t *tktlenp) +{ + char *failure; + krb5_context kctx = NULL; + krb5_error_code kerr; + krb5_ccache kcc = NULL; + krb5_principal kprin = NULL, cprn = NULL; + krb5_creds kcreds, *kcredsp = NULL; + krb5_auth_context kauth = NULL; + krb5_data kdata, kdata0; + uchar_t *tkt; + + memset((char *)&kcreds, 0, sizeof (kcreds)); + kdata0.length = 0; + + /* These shoud have been done in smb_ctx_krb5init() */ + if (ctx->ct_krb5ctx == NULL || + ctx->ct_krb5cc == NULL || + ctx->ct_krb5cp == NULL) { + failure = "smb_ctx_krb5init"; + goto out; + } + kctx = ctx->ct_krb5ctx; + kcc = ctx->ct_krb5cc; + cprn = ctx->ct_krb5cp; + + failure = "krb5_set_default_tgs_enctypes"; + if ((kerr = krb5_set_default_tgs_enctypes(kctx, kenctypes))) + goto out; + /* + * The following is an unrolling of krb5_mk_req. Something like: + * krb5_mk_req(kctx, &kauth, 0, service(prin), hostname(prin), + * &kdata0, kcc, &kdata);) + * ...except we needed krb5_parse_name not krb5_sname_to_principal. + */ + failure = "krb5_parse_name"; + if ((kerr = krb5_parse_name(kctx, prin, &kprin))) + goto out; + failure = "krb5_copy_principal(server)"; + if ((kerr = krb5_copy_principal(kctx, kprin, &kcreds.server))) + goto out; + failure = "krb5_copy_principal(client)"; + if ((kerr = krb5_copy_principal(kctx, cprn, &kcreds.client))) + goto out; + failure = "krb5_get_credentials"; + if ((kerr = krb5_get_credentials(kctx, 0, kcc, &kcreds, &kcredsp))) + goto out; + failure = "krb5_mk_req_extended"; + if ((kerr = krb5_mk_req_extended(kctx, &kauth, 0, &kdata0, kcredsp, + &kdata))) + goto out; + failure = "malloc"; + if (!(tkt = malloc(kdata.length))) { + krb5_free_data_contents(kctx, &kdata); + goto out; + } + *tktlenp = kdata.length; + memcpy(tkt, kdata.data, kdata.length); + krb5_free_data_contents(kctx, &kdata); + *tktp = tkt; + failure = NULL; +out:; + if (kerr) { + if (!failure) + failure = "smb_ctx_principal2tkt"; + /* + * Avoid logging the typical "No credentials cache found" + */ + if (kerr != KRB5_FCC_NOFILE || + strcmp(failure, "krb5_cc_get_principal")) + com_err(__progname, kerr, failure); + } + if (kauth) + krb5_auth_con_free(kctx, kauth); + if (kcredsp) + krb5_free_creds(kctx, kcredsp); + if (kcreds.server || kcreds.client) + krb5_free_cred_contents(kctx, &kcreds); + if (kprin) + krb5_free_principal(kctx, kprin); + + /* Free kctx in smb_ctx_done */ + + return (failure); +} + +char * +smb_ctx_principal2blob( + struct smb_ctx *ctx, + smbioc_ossn_t *ssn, + char *prin) +{ + int rc = 0; + char *failure; + uchar_t *tkt = NULL; + ulong_t tktlen; + uchar_t *gtok = NULL; /* gssapi token */ + ulong_t gtoklen; /* gssapi token length */ + SPNEGO_TOKEN_HANDLE stok = NULL; /* spnego token */ + void *blob = NULL; /* result */ + ulong_t bloblen; /* result length */ + + if ((failure = smb_ctx_principal2tkt(ctx, prin, &tkt, &tktlen))) + goto out; + if ((failure = smb_ctx_tkt2gtok(tkt, tktlen, >ok, >oklen))) + goto out; + /* + * RFC says to send NegTokenTarg now. So does MS docs. But + * win2k gives ERRbaduid if we do... we must send + * another NegTokenInit now! + */ + failure = "spnegoCreateNegTokenInit"; + if ((rc = spnegoCreateNegTokenInit(spnego_mech_oid_Kerberos_V5_Legacy, + 0, gtok, gtoklen, NULL, 0, &stok))) + goto out; + failure = "spnegoTokenGetBinary(NULL)"; + rc = spnegoTokenGetBinary(stok, NULL, &bloblen); + if (rc != SPNEGO_E_BUFFER_TOO_SMALL) + goto out; + failure = "malloc"; + if (!(blob = malloc((size_t)bloblen))) + goto out; + /* No longer store length at start of blob. */ + /* *blob = bloblen; */ + failure = "spnegoTokenGetBinary"; + if ((rc = spnegoTokenGetBinary(stok, blob, &bloblen))) + goto out; + ssn->ioc_intoklen = bloblen; + ssn->ioc_intok = blob; + failure = NULL; +out:; + if (rc) { + /* XXX better is to embed rc in failure */ + smb_error(dgettext(TEXT_DOMAIN, + "spnego principal2blob error %d"), 0, -rc); + if (!failure) + failure = "spnego"; + } + if (blob && failure) + free(blob); + if (stok) + spnegoFreeData(stok); + if (gtok) + free(gtok); + if (tkt) + free(tkt); + return (failure); +} + + +#if 0 +void +prblob(uchar_t *b, size_t len) +{ + while (len--) + fprintf(stderr, "%02x", *b++); + fprintf(stderr, "\n"); +} +#endif + + +/* + * We navigate the SPNEGO & ASN1 encoding to find a kerberos principal + * Note: driver no longer puts length at start of blob. + */ +char * +smb_ctx_blob2principal( + struct smb_ctx *ctx, + smbioc_ossn_t *ssn, + char **prinp) +{ + uchar_t *blob = ssn->ioc_outtok; + size_t len = ssn->ioc_outtoklen; + int rc = 0; + SPNEGO_TOKEN_HANDLE stok = NULL; + int indx = 0; + char *failure; + uchar_t flags = 0; + unsigned long plen = 0; + uchar_t *prin; + +#if 0 + fprintf(stderr, "blob from negotiate:\n"); + prblob(blob, len); +#endif + + /* Skip the GUID */ + assert(len >= SMB_GUIDLEN); + blob += SMB_GUIDLEN; + len -= SMB_GUIDLEN; + + failure = "spnegoInitFromBinary"; + if ((rc = spnegoInitFromBinary(blob, len, &stok))) + goto out; + /* + * Needn't use new Kerberos OID - the Legacy one is fine. + */ + failure = "spnegoIsMechTypeAvailable"; + if (spnegoIsMechTypeAvailable(stok, spnego_mech_oid_Kerberos_V5_Legacy, + &indx)) + goto out; + /* + * Ignoring optional context flags for now. May want to pass + * them to krb5 layer. XXX + */ + if (!spnegoGetContextFlags(stok, &flags)) + fprintf(stderr, dgettext(TEXT_DOMAIN, + "spnego context flags 0x%x\n"), flags); + failure = "spnegoGetMechListMIC(NULL)"; + rc = spnegoGetMechListMIC(stok, NULL, &plen); + if (rc != SPNEGO_E_BUFFER_TOO_SMALL) + goto out; + failure = "malloc"; + if (!(prin = malloc(plen + 1))) + goto out; + failure = "spnegoGetMechListMIC"; + if ((rc = spnegoGetMechListMIC(stok, prin, &plen))) { + free(prin); + goto out; + } + prin[plen] = '\0'; + *prinp = (char *)prin; + failure = NULL; +out:; + if (stok) + spnegoFreeData(stok); + if (rc) { + /* XXX better is to embed rc in failure */ + smb_error(dgettext(TEXT_DOMAIN, + "spnego blob2principal error %d"), 0, -rc); + if (!failure) + failure = "spnego"; + } + return (failure); +} + + +int +smb_ctx_negotiate(struct smb_ctx *ctx, int level, int flags, char *workgroup) +{ + struct smbioc_lookup rq; + int error = 0; + char *failure = NULL; + char *principal = NULL; + char c; + int i; + ssize_t *outtoklen; + uchar_t *blob; + + /* + * We leave ct_secblob set iff extended security + * negotiation succeeds. + */ + if (ctx->ct_secblob) { + free(ctx->ct_secblob); + ctx->ct_secblob = NULL; + } +#ifdef XXX + if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) { + smb_error(dgettext(TEXT_DOMAIN, + "smb_ctx_lookup() data is not resolved"), 0); + return (EINVAL); + } +#endif + if ((error = smb_ctx_gethandle(ctx))) + return (error); + + bzero(&rq, sizeof (rq)); + bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn)); + bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare)); + + /* + * Find out if we have a Kerberos ticket, + * and only offer SPNEGO if we have one. + */ + failure = smb_ctx_krb5init(ctx); + if (failure) { + if (smb_verbose) + smb_error(failure, 0); + goto out; + } + + rq.ioc_flags = flags; + rq.ioc_level = level; + rq.ioc_ssn.ioc_opt |= SMBVOPT_EXT_SEC; + error = smb_ctx_ioctl(ctx, SMBIOC_NEGOTIATE, &rq); + if (error) { + failure = dgettext(TEXT_DOMAIN, "negotiate failed"); + smb_error(failure, error); + if (error == ETIMEDOUT) + return (error); + goto out; + } + /* + * If the server capabilities did not include + * SMB_CAP_EXT_SECURITY then the driver clears + * the flag SMBVOPT_EXT_SEC for us. + * XXX: should add the capabilities to ioc_ssn + * XXX: see comment in driver - smb_usr.c + */ + failure = dgettext(TEXT_DOMAIN, "SPNEGO unsupported"); + if ((rq.ioc_ssn.ioc_opt & SMBVOPT_EXT_SEC) == 0) { + if (smb_verbose) + smb_error(failure, 0); + /* + * Do regular (old style) NTLM or NTLMv2 + * Nothing more to do here in negotiate. + */ + return (0); + } + + /* + * Capabilities DO include SMB_CAP_EXT_SECURITY, + * so this should be an SPNEGO security blob. + * Parse the ASN.1/DER, prepare response(s). + * XXX: Handle STATUS_MORE_PROCESSING_REQUIRED? + * XXX: Requires additional session setup calls. + */ + if (rq.ioc_ssn.ioc_outtoklen <= SMB_GUIDLEN) + goto out; + /* some servers send padding junk */ + blob = rq.ioc_ssn.ioc_outtok; + if (blob[0] == 0) + goto out; + + failure = smb_ctx_blob2principal( + ctx, &rq.ioc_ssn, &principal); + if (failure) + goto out; + failure = smb_ctx_principal2blob( + ctx, &rq.ioc_ssn, principal); + if (failure) + goto out; + + /* Success! Save the blob to send next. */ + ctx->ct_secblob = rq.ioc_ssn.ioc_intok; + ctx->ct_secbloblen = rq.ioc_ssn.ioc_intoklen; + rq.ioc_ssn.ioc_intok = NULL; + +out: + if (principal) + free(principal); + if (rq.ioc_ssn.ioc_intok) + free(rq.ioc_ssn.ioc_intok); + if (rq.ioc_ssn.ioc_outtok) + free(rq.ioc_ssn.ioc_outtok); + if (!failure) + return (0); /* Success! */ + + /* + * Negotiate failed with "extended security". + * + * XXX: If we are doing SPNEGO correctly, + * we should never get here unless the user + * supplied invalid authentication data, + * or we saw some kind of protocol error. + * + * XXX: The error message below should be + * XXX: unconditional (remove "if verbose") + * XXX: but not until we have "NTLMSSP" + * Avoid spew for anticipated failure modes + * but enable this with the verbose flag + */ + if (smb_verbose) { + smb_error(dgettext(TEXT_DOMAIN, + "%s (extended security negotiate)"), error, failure); + } + + /* + * XXX: Try again using NTLM (or NTLMv2) + * XXX: Normal clients don't do this. + * XXX: Should just return an error, but + * keep the fall-back to NTLM for now. + * + * Start over with a new connection. + */ + if ((error = smb_ctx_gethandle(ctx))) + return (error); + bzero(&rq, sizeof (rq)); + bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn)); + bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare)); + rq.ioc_flags = flags; + rq.ioc_level = level; + /* Note: NO SMBVOPT_EXT_SEC */ + error = smb_ctx_ioctl(ctx, SMBIOC_NEGOTIATE, &rq); + if (error) { + failure = dgettext(TEXT_DOMAIN, "negotiate failed"); + smb_error(failure, error); + rpc_cleanup_smbctx(ctx); + close(ctx->ct_fd); + ctx->ct_fd = -1; + return (error); + } + + /* + * Used to copy the workgroup out of the SMB_NEGOTIATE response + * here, to default our domain name to be the same as the server. + * Not a good idea: Unnecessary at best, and sometimes wrong, i.e. + * when our account is in a trusted domain. + */ + + return (error); +} + + +int +smb_ctx_tdis(struct smb_ctx *ctx) +{ + struct smbioc_lookup rq; /* XXX may be used, someday */ + int error = 0; + + if (ctx->ct_fd < 0) { + smb_error(dgettext(TEXT_DOMAIN, + "tree disconnect without handle?!"), 0); + return (EINVAL); + } + if (!(ctx->ct_flags & SMBCF_SSNACTIVE)) { + smb_error(dgettext(TEXT_DOMAIN, + "tree disconnect without session?!"), 0); + return (EINVAL); + } + bzero(&rq, sizeof (rq)); + bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn)); + bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare)); + if (ioctl(ctx->ct_fd, SMBIOC_TDIS, &rq) == -1) { + error = errno; + smb_error(dgettext(TEXT_DOMAIN, + "tree disconnect failed"), error); + } + return (error); +} + + +int +smb_ctx_lookup(struct smb_ctx *ctx, int level, int flags) +{ + struct smbioc_lookup rq; + int error = 0; + char *failure = NULL; + + if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) { + smb_error(dgettext(TEXT_DOMAIN, + "smb_ctx_lookup() data is not resolved"), 0); + return (EINVAL); + } + if (ctx->ct_fd < 0) { + smb_error(dgettext(TEXT_DOMAIN, + "handle from smb_ctx_nego() gone?!"), 0); + return (EINVAL); + } + if (!(flags & SMBLK_CREATE)) + return (0); + bzero(&rq, sizeof (rq)); + bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn)); + bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare)); + rq.ioc_flags = flags; + rq.ioc_level = level; + + /* + * Iff we have a security blob, we're using + * extended security... + */ + if (ctx->ct_secblob) { + rq.ioc_ssn.ioc_opt |= SMBVOPT_EXT_SEC; + if (!(ctx->ct_flags & SMBCF_SSNACTIVE)) { + rq.ioc_ssn.ioc_intok = ctx->ct_secblob; + rq.ioc_ssn.ioc_intoklen = ctx->ct_secbloblen; + error = smb_ctx_ioctl(ctx, SMBIOC_SSNSETUP, &rq); + } + rq.ioc_ssn.ioc_intok = NULL; + if (error) { + failure = dgettext(TEXT_DOMAIN, + "session setup failed"); + } else { + ctx->ct_flags |= SMBCF_SSNACTIVE; + if ((error = smb_ctx_ioctl(ctx, SMBIOC_TCON, &rq))) + failure = dgettext(TEXT_DOMAIN, + "tree connect failed"); + } + if (rq.ioc_ssn.ioc_intok) + free(rq.ioc_ssn.ioc_intok); + if (rq.ioc_ssn.ioc_outtok) + free(rq.ioc_ssn.ioc_outtok); + if (!failure) + return (0); + smb_error(dgettext(TEXT_DOMAIN, + "%s (extended security lookup2)"), error, failure); + /* unwise to failback to NTLM now */ + return (error); + } + + /* + * Otherwise we're doing plain old NTLM + */ + seteuid(eff_uid); /* restore setuid root briefly */ + if ((ctx->ct_flags & SMBCF_SSNACTIVE) == 0) { + /* + * This is the magic that tells the driver to + * copy the password from the keychain, and + * whether to use the system name or the + * account domain to lookup the keychain. + */ + if (ctx->ct_flags & SMBCF_KCFOUND) + rq.ioc_ssn.ioc_opt |= SMBVOPT_USE_KEYCHAIN; + if (ctx->ct_flags & SMBCF_KCDOMAIN) + rq.ioc_ssn.ioc_opt |= SMBVOPT_KC_DOMAIN; + if (ioctl(ctx->ct_fd, SMBIOC_SSNSETUP, &rq) < 0) { + error = errno; + failure = dgettext(TEXT_DOMAIN, "session setup"); + goto out; + } + ctx->ct_flags |= SMBCF_SSNACTIVE; + } + if (ioctl(ctx->ct_fd, SMBIOC_TCON, &rq) == -1) { + error = errno; + failure = dgettext(TEXT_DOMAIN, "tree connect"); + } + +out: + seteuid(real_uid); /* and back to real user */ + if (failure) { + error = errno; + smb_error(dgettext(TEXT_DOMAIN, + "%s phase failed"), error, failure); + } + return (error); +} + +/* + * Return the hflags2 word for an smb_ctx. + */ +int +smb_ctx_flags2(struct smb_ctx *ctx) +{ + uint16_t flags2; + + if (ioctl(ctx->ct_fd, SMBIOC_FLAGS2, &flags2) == -1) { + smb_error(dgettext(TEXT_DOMAIN, + "can't get flags2 for a session"), errno); + return (-1); + } + printf(dgettext(TEXT_DOMAIN, "Flags2 value is %d\n"), flags2); + return (flags2); +} + +/* + * level values: + * 0 - default + * 1 - server + * 2 - server:user + * 3 - server:user:share + */ +static int +smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level) +{ + char *p; + int error; + +#ifdef NOT_DEFINED + if (level > 0) { + rc_getstringptr(smb_rc, sname, "charsets", &p); + if (p) { + error = smb_ctx_setcharset(ctx, p); + if (error) + smb_error(dgettext(TEXT_DOMAIN, + "charset specification in the section '%s' ignored"), + error, sname); + } + } +#endif + + if (level <= 1) { + /* Section is: [default] or [server] */ + + rc_getint(smb_rc, sname, "timeout", + &ctx->ct_ssn.ioc_timeout); + +#ifdef NOT_DEFINED + rc_getint(smb_rc, sname, "retry_count", + &ctx->ct_ssn.ioc_retrycount); + rc_getstringptr(smb_rc, sname, "use_negprot_domain", &p); + if (p && strcmp(p, "NO") == 0) + ctx->ct_flags |= SMBCF_NONEGDOM; +#endif + + rc_getstringptr(smb_rc, sname, "minauth", &p); + if (p) { + /* + * "minauth" was set in this section; override + * the current minimum authentication setting. + */ + ctx->ct_ssn.ioc_opt &= ~SMBVOPT_MINAUTH; + if (strcmp(p, "kerberos") == 0) { + /* + * Don't fall back to NTLMv2, NTLMv1, or + * a clear text password. + */ + ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_KERBEROS; + } else if (strcmp(p, "ntlmv2") == 0) { + /* + * Don't fall back to NTLMv1 or a clear + * text password. + */ + ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NTLMV2; + } else if (strcmp(p, "ntlm") == 0) { + /* + * Don't send the LM response over the wire. + */ + ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NTLM; + } else if (strcmp(p, "lm") == 0) { + /* + * Fail if the server doesn't do encrypted + * passwords. + */ + ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_LM; + } else if (strcmp(p, "none") == 0) { + /* + * Anything goes. + * (The following statement should be + * optimized away.) + */ + /* LINTED */ + ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NONE; + } else { + /* + * Unknown minimum authentication level. + */ + smb_error(dgettext(TEXT_DOMAIN, +"invalid minimum authentication level \"%s\" specified in the section %s"), + 0, p, sname); + return (EINVAL); + } + } + + /* + * Domain name. Allow both keywords: + * "workgroup", "domain" + * + * Note: these are NOT marked "from CMD". + * See long comment at smb_ctx_init() + */ + rc_getstringptr(smb_rc, sname, "workgroup", &p); + if (p) { + nls_str_upper(p, p); + error = smb_ctx_setworkgroup(ctx, p, 0); + if (error) + smb_error(dgettext(TEXT_DOMAIN, + "workgroup specification in the " + "section '%s' ignored"), error, sname); + } + rc_getstringptr(smb_rc, sname, "domain", &p); + if (p) { + nls_str_upper(p, p); + error = smb_ctx_setworkgroup(ctx, p, 0); + if (error) + smb_error(dgettext(TEXT_DOMAIN, + "domain specification in the " + "section '%s' ignored"), error, sname); + } + + rc_getstringptr(smb_rc, sname, "user", &p); + if (p) { + error = smb_ctx_setuser(ctx, p, 0); + if (error) + smb_error(dgettext(TEXT_DOMAIN, + "user specification in the " + "section '%s' ignored"), error, sname); + } + } + + if (level == 1) { + /* Section is: [server] */ + rc_getstringptr(smb_rc, sname, "addr", &p); + if (p) { + error = smb_ctx_setsrvaddr(ctx, p); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "invalid address specified in section %s"), + 0, sname); + return (error); + } + } + } + + rc_getstringptr(smb_rc, sname, "password", &p); + if (p) { + error = smb_ctx_setpassword(ctx, p, 0); + if (error) + smb_error(dgettext(TEXT_DOMAIN, + "password specification in the section '%s' ignored"), + error, sname); + } + + return (0); +} + +/* + * read rc file as follows: + * 0: read [default] section + * 1: override with [server] section + * 2: override with [server:user] section + * 3: override with [server:user:share] section + * Since absence of rcfile is not fatal, silently ignore this fact. + * smb_rc file should be closed by caller. + */ +int +smb_ctx_readrc(struct smb_ctx *ctx) +{ + char sname[SMB_MAXSRVNAMELEN + SMB_MAXUSERNAMELEN + + SMB_MAXSHARENAMELEN + 4]; + + if (smb_open_rcfile(ctx) != 0) + goto done; + + /* + * default parameters (level=0) + */ + smb_ctx_readrcsection(ctx, "default", 0); + nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0); + + /* + * If we don't have a server name, we can't read any of the + * [server...] sections. + */ + if (ctx->ct_ssn.ioc_srvname[0] == 0) + goto done; + + /* + * SERVER parameters. + */ + smb_ctx_readrcsection(ctx, ctx->ct_ssn.ioc_srvname, 1); + + /* + * If we don't have a user name, we can't read any of the + * [server:user...] sections. + */ + if (ctx->ct_ssn.ioc_user[0] == 0) + goto done; + + /* + * SERVER:USER parameters + */ + snprintf(sname, sizeof (sname), "%s:%s", + ctx->ct_ssn.ioc_srvname, + ctx->ct_ssn.ioc_user); + smb_ctx_readrcsection(ctx, sname, 2); + + /* + * If we don't have a share name, we can't read any of the + * [server:user:share] sections. + */ + if (ctx->ct_sh.ioc_share[0] != 0) { + /* + * SERVER:USER:SHARE parameters + */ + snprintf(sname, sizeof (sname), "%s:%s:%s", + ctx->ct_ssn.ioc_srvname, + ctx->ct_ssn.ioc_user, + ctx->ct_sh.ioc_share); + smb_ctx_readrcsection(ctx, sname, 3); + } + +done: + if (smb_debug) + dump_ctx("after smb_ctx_readrc", ctx); + + return (0); +} diff --git a/usr/src/lib/libsmbfs/smb/derparse.c b/usr/src/lib/libsmbfs/smb/derparse.c new file mode 100644 index 0000000000..cc7d61c6bd --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/derparse.c @@ -0,0 +1,750 @@ +/* +// Copyright (C) 2002 Microsoft Corporation +// All rights reserved. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" +// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +// OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY +// AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Date - 10/08/2002 +// Author - Sanj Surati + + +///////////////////////////////////////////////////////////// +// +// DERPARSE.C +// +// SPNEGO Token Handler Source File +// +// Contains implementation of ASN.1 DER read/write functions +// as defined in DERPARSE.H. +// +///////////////////////////////////////////////////////////// + +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stdio.h> +#include <memory.h> +#include <sys/byteorder.h> +#include "spnego.h" +#include "derparse.h" + +/* +// +// The GSS Mechanism OID enumeration values (SPNEGO_MECH_OID) control which offset in +// the array below, that a mechanism can be found. +// +*/ +#pragma error_messages (off,E_INITIALIZATION_TYPE_MISMATCH) +MECH_OID g_stcMechOIDList [] = +{ + {"\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02", 11, 9, + spnego_mech_oid_Kerberos_V5_Legacy }, // 1.2.840.48018.1.2.2 + {"\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02", 11, 9, + spnego_mech_oid_Kerberos_V5 }, // 1.2.840.113554.1.2.2 + {"\x06\x06\x2b\x06\x01\x05\x05\x02", 8, 6, + spnego_mech_oid_Spnego }, // 1.3.6.1.5.5.2 + {"\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a", 12, 10, + spnego_mech_oid_NTLMSSP }, // 1.3.6.1.4.1.311.2.2.10 + {"", 0, 0, spnego_mech_oid_NotUsed } // Placeholder +}; +#pragma error_messages (default,E_INITIALIZATION_TYPE_MISMATCH) + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerGetLength +// +// Parameters: +// [in] pbLengthData - DER Length Data +// [in] nBoundaryLength - Length that value must not exceed. +// [out] pnLength - Filled out with length value +// [out] pnNumLengthBytes - Filled out with number of bytes +// consumed by DER length. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Interprets the data at pbLengthData as a DER length. The length must +// fit within the bounds of nBoundary length. We do not currently +// process lengths that take more than 4 bytes. +// +//////////////////////////////////////////////////////////////////////////// +*/ + +int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pnLength, + long* pnNumLengthBytes ) +{ + int nReturn = SPNEGO_E_INVALID_LENGTH; + int nNumLengthBytes = 0; + + // First check if the extended length bit is set + + if ( *pbLengthData & LEN_XTND ) + { + // Lower 7 bits contain the number of trailing bytes that describe the length + nNumLengthBytes = *pbLengthData & LEN_MASK; + + // Check that the number of bytes we are about to read is within our boundary + // constraints + + if ( nNumLengthBytes <= nBoundaryLength - 1 ) + { + + // For now, our handler won't deal with lengths greater than 4 bytes + if ( nNumLengthBytes >= 1 && nNumLengthBytes <= 4 ) + { + // 0 out the initial length + *pnLength = 0L; + + // Bump by 1 byte + pbLengthData++; + + #ifdef _LITTLE_ENDIAN + + // There may be a cleaner way to do this, but for now, this seems to be + // an easy way to do the transformation + switch ( nNumLengthBytes ) + { + case 1: + { + *( ( (unsigned char*) pnLength ) ) = *pbLengthData; + break; + } + + case 2: + { + *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 1); + *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData); + + break; + } + + case 3: + { + *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 2); + *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1); + *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData); + break; + } + + case 4: + { + *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 3); + *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData + 2); + *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1); + *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData); + break; + } + + } // SWITCH ( nNumLengthBytes ) + + #else + // We are Big-Endian, so the length can be copied in from the source + // as is. Ensure that we adjust for the number of bytes we actually + // copy. + + memcpy( ( (unsigned char *) pnLength ) + ( 4 - nNumLengthBytes ), + pbLengthData, nNumLengthBytes ); + #endif + + // Account for the initial length byte + *pnNumLengthBytes = nNumLengthBytes + 1; + nReturn = SPNEGO_E_SUCCESS; + + } // IF Valid Length + + } // IF num bytes to read is within the boundary length + + } // IF xtended length + else + { + + // Extended bit is not set, so the length is in the value and the one + // byte describes the length + *pnLength = *pbLengthData & LEN_MASK; + *pnNumLengthBytes = 1; + nReturn = SPNEGO_E_SUCCESS; + + } + + return nReturn; +} + + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerCheckToken +// +// Parameters: +// [in] pbTokenData - Token Data +// [in] nToken - Token identifier to check for +// [in] nLengthWithToken - Expected token length (with data) +// [in] nBoundaryLength - Length that value must not exceed. +// [out] pnLength - Filled out with data length +// [out] pnTokenLength - Filled out with number of bytes +// consumed by token identifier and length. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Checks the data pointed to by pbTokenData for the specified token +// identifier and the length that immediately follows. If +// nLengthWithToken is > 0, the calculated length must match. The +// length must also not exceed the specified boundary length . +// +//////////////////////////////////////////////////////////////////////////// +*/ + +int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken, + long nLengthWithToken, long nBoundaryLength, + long* pnLength, long* pnTokenLength ) +{ + + int nReturn = SPNEGO_E_INVALID_LENGTH; + long nNumLengthBytes = 0L; + + // Make sure that we've at least got 2 bytes of room to work with + + if ( nBoundaryLength >= 2 ) + { + // The first byte of the token data MUST match the specified token + if ( *pbTokenData == nToken ) + { + // Next byte indicates the length + pbTokenData++; + + // Get the length described by the token + if ( ( nReturn = ASNDerGetLength( pbTokenData, nBoundaryLength, pnLength, + &nNumLengthBytes ) ) == SPNEGO_E_SUCCESS ) + { + // Verify that the length is LESS THAN the boundary length + // (this should prevent us walking out of our buffer) + if ( ( nBoundaryLength - ( nNumLengthBytes + 1 ) < *pnLength ) ) + { + + nReturn = SPNEGO_E_INVALID_LENGTH; + + } + + // If we were passed a length to check, do so now + if ( nLengthWithToken > 0L ) + { + + // Check that the expected length matches + if ( ( nLengthWithToken - ( nNumLengthBytes + 1 ) ) != *pnLength ) + { + + nReturn = SPNEGO_E_INVALID_LENGTH; + + } + + } // IF need to validate length + + if ( SPNEGO_E_SUCCESS == nReturn ) + { + *pnTokenLength = nNumLengthBytes + 1; + } + + } // IF ASNDerGetLength + + } // IF token matches + else + { + nReturn = SPNEGO_E_TOKEN_NOT_FOUND; + } + + } // IF Boundary Length is at least 2 bytes + + return nReturn; +} + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerCheckOID +// +// Parameters: +// [in] pbTokenData - Token Data +// [in] nMechOID - OID we are looking for +// [in] nBoundaryLength - Length that value must not exceed. +// [out] pnTokenLength - Filled out with number of bytes +// consumed by token and data. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Checks the data pointed to by pbTokenData for the specified OID. +// +//////////////////////////////////////////////////////////////////////////// +*/ + +int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long nBoundaryLength, + long* pnTokenLength ) +{ + int nReturn = 0L; + long nLength = 0L; + + // Verify that we have an OID token + if ( ( nReturn = ASNDerCheckToken( pbTokenData, OID, 0L, nBoundaryLength, + &nLength, pnTokenLength ) ) == SPNEGO_E_SUCCESS ) + { + // Add the data length to the Token Length + *pnTokenLength += nLength; + + // Token Lengths plus the actual length must match the length in our OID list element. + // If it doesn't, we're done + if ( *pnTokenLength == g_stcMechOIDList[nMechOID].iLen ) + { + // Memcompare the token and the expected field + if ( memcmp( pbTokenData, g_stcMechOIDList[nMechOID].ucOid, *pnTokenLength ) != 0 ) + { + nReturn = SPNEGO_E_UNEXPECTED_OID; + } + } + else + { + nReturn = SPNEGO_E_UNEXPECTED_OID; + } + + } // IF OID Token CHecks + + return nReturn; +} + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerCalcNumLengthBytes +// +// Parameters: +// [in] nLength - Length to calculate length bytes for. +// +// Returns: +// int Number of bytes necessary to represent length +// +// Comments : +// Helper function to calculate the number of length bytes necessary to +// represent a length value. For our purposes, a 32-bit value should be +// enough to describea length. +// +//////////////////////////////////////////////////////////////////////////// +*/ + +int ASNDerCalcNumLengthBytes( long nLength ) +{ + if ( nLength <= 0x7F ) + { + // A single byte will be sufficient for describing this length. + // The byte will simply contain the length + return 1; + } + else if ( nLength <= 0xFF ) + { + // Two bytes are necessary, one to say how many following bytes + // describe the length, and one to give the length + return 2; + } + else if ( nLength <= 0xFFFF ) + { + // Three bytes are necessary, one to say how many following bytes + // describe the length, and two to give the length + return 3; + } + else if ( nLength <= 0xFFFFFF ) + { + // Four bytes are necessary, one to say how many following bytes + // describe the length, and three to give the length + return 4; + } + else + { + // Five bytes are necessary, one to say how many following bytes + // describe the length, and four to give the length + return 5; + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerCalcTokenLength +// +// Parameters: +// [in] nLength - Length to calculate length bytes for. +// [in] nDataLength - Actual Data length value. +// +// Returns: +// long Number of bytes necessary to represent a token, length and data +// +// Comments : +// Helper function to calculate a token and value size, based on a +// supplied length value, and any binary data that will need to be +// written out. +// +//////////////////////////////////////////////////////////////////////////// + +long ASNDerCalcTokenLength( long nLength, long nDataLength ) +{ + // Add a byte to the length size to account for a single byte to + // hold the token type. + long nTotalLength = ASNDerCalcNumLengthBytes( nLength ) + 1; + + return nTotalLength + nDataLength; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerCalcElementLength +// +// Parameters: +// [in] nDataLength - Length of data. +// [out] pnInternalLength - Filled out with length of element +// without sequence info. +// +// Returns: +// long Number of bytes necessary to represent an element +// +// Comments : +// Helper function to calculate an element length. An element consists +// of a sequence token, a type token and then the data. +// +//////////////////////////////////////////////////////////////////////////// + +long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength ) +{ + // First the type token and the actual data + long nTotalLength = ASNDerCalcTokenLength( nDataLength, nDataLength ); + + // Internal length is the length without the element sequence token + if ( NULL != pnInternalLength ) + { + *pnInternalLength = nTotalLength; + } + + // Next add in the element's sequence token (remember that its + // length is the total length of the type token and data) + nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); + + return nTotalLength; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerCalcMechListLength +// +// Parameters: +// [in] mechoid - Mech OID to put in list. +// [out] pnInternalLength - Filled out with length of element +// without the primary sequence token. +// +// Returns: +// long Number of bytes necessary to represent a mechList +// +// Comments : +// Helper function to calculate a MechList length. A mechlist consists +// of a NegTokenInit sequence token, a sequence token for the MechList +// and finally a list of OIDs. In our case, we only really have one +// OID. +// +//////////////////////////////////////////////////////////////////////////// + +long ASNDerCalcMechListLength( SPNEGO_MECH_OID mechoid, long* pnInternalLength ) +{ + // First the OID + long nTotalLength = g_stcMechOIDList[mechoid].iLen; + + // Next add in a sequence token + nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); + + // Internal length is the length without the element sequence token + if ( NULL != pnInternalLength ) + { + *pnInternalLength = nTotalLength; + } + + // Finally add in the element's sequence token + nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); + + return nTotalLength; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerWriteLength +// +// Parameters: +// [out] pbData - Buffer to write into. +// [in] nLength - Length to write out. +// +// Returns: +// int Number of bytes written out +// +// Comments : +// Helper function to write out a length value following DER rules . +// +//////////////////////////////////////////////////////////////////////////// + +int ASNDerWriteLength( unsigned char* pbData, long nLength ) +{ + int nNumBytesRequired = ASNDerCalcNumLengthBytes( nLength ); + int nNumLengthBytes = nNumBytesRequired - 1; + + + if ( nNumBytesRequired > 1 ) + { + + // Write out the number of bytes following which will be used + *pbData = (unsigned char ) ( LEN_XTND | nNumLengthBytes ); + + // Point to where we'll actually write the length + pbData++; + +#ifdef _LITTLE_ENDIAN + + // There may be a cleaner way to do this, but for now, this seems to be + // an easy way to do the transformation + switch ( nNumLengthBytes ) + { + case 1: + { + // Cast the length to a single byte, since we know that it + // is 0x7F or less (or we wouldn't only need a single byte). + + *pbData = (unsigned char) nLength; + break; + } + + case 2: + { + *pbData = *( ( (unsigned char*) &nLength ) + 1 ); + *( pbData + 1) = *( ( (unsigned char*) &nLength ) ); + break; + } + + case 3: + { + *pbData = *( ( (unsigned char*) &nLength ) + 3 ); + *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 ); + *( pbData + 2) = *( ( (unsigned char*) &nLength ) ); + break; + } + + case 4: + { + *pbData = *( ( (unsigned char*) &nLength ) + 3 ); + *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 ); + *( pbData + 2) = *( ( (unsigned char*) &nLength ) + 1 ); + *( pbData + 3) = *( ( (unsigned char*) &nLength ) ); + break; + } + + } // SWITCH ( nNumLengthBytes ) + +#else + // We are Big-Endian, so the length can be copied in from the source + // as is. Ensure that we adjust for the number of bytes we actually + // copy. + + memcpy( pbData, + ( (unsigned char *) &nLength ) + ( 4 - nNumLengthBytes ), nNumLengthBytes ); +#endif + + } // IF > 1 byte for length + else + { + // Cast the length to a single byte, since we know that it + // is 0x7F or less (or we wouldn't only need a single byte). + + *pbData = (unsigned char) nLength; + } + + return nNumBytesRequired; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerWriteToken +// +// Parameters: +// [out] pbData - Buffer to write into. +// [in] ucType - Token Type +// [in] pbTokenValue - Actual Value +// [in] nLength - Length of Data. +// +// Returns: +// int Number of bytes written out +// +// Comments : +// Helper function to write out a token and any associated data. If +// pbTokenValue is non-NULL, then it is written out in addition to the +// token identifier and the length bytes. +// +//////////////////////////////////////////////////////////////////////////// + +int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType, + unsigned char* pbTokenValue, long nLength ) +{ + int nTotalBytesWrittenOut = 0L; + int nNumLengthBytesWritten = 0L; + + // Write out the type + *pbData = ucType; + + // Wrote 1 byte, and move data pointer + nTotalBytesWrittenOut++; + pbData++; + + // Now write out the length and adjust the number of bytes written out + nNumLengthBytesWritten = ASNDerWriteLength( pbData, nLength ); + + nTotalBytesWrittenOut += nNumLengthBytesWritten; + pbData += nNumLengthBytesWritten; + + // Write out the token value if we got one. The assumption is that the + // nLength value indicates how many bytes are in pbTokenValue. + + if ( NULL != pbTokenValue ) + { + memcpy( pbData, pbTokenValue, nLength ); + nTotalBytesWrittenOut += nLength; + } + + return nTotalBytesWrittenOut; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerWriteOID +// +// Parameters: +// [out] pbData - Buffer to write into. +// [in] eMechOID - OID to write out. +// +// Returns: +// int Number of bytes written out +// +// Comments : +// Helper function to write out an OID. For these we have the raw bytes +// listed in a global structure. The caller simply indicates which OID +// should be written and we will splat out the data. +// +//////////////////////////////////////////////////////////////////////////// + +int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID ) +{ + + memcpy( pbData, g_stcMechOIDList[eMechOID].ucOid, g_stcMechOIDList[eMechOID].iLen ); + + return g_stcMechOIDList[eMechOID].iLen; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerWriteMechList +// +// Parameters: +// [out] pbData - Buffer to write into. +// [in] eMechOID - OID to put in MechList. +// +// Returns: +// int Number of bytes written out +// +// Comments : +// Helper function to write out a MechList. A MechList consists of the +// Init Token Sequence, a sequence token and then the list of OIDs. In +// our case the OID is from a global array of known OIDs. +// +//////////////////////////////////////////////////////////////////////////// + +long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID mechoid ) +{ + // First get the length + long nInternalLength = 0L; + long nMechListLength = ASNDerCalcMechListLength( mechoid, &nInternalLength ); + long nTempLength = 0L; + + nTempLength = ASNDerWriteToken( pbData, SPNEGO_NEGINIT_ELEMENT_MECHTYPES, + NULL, nInternalLength ); + + // Adjust the data pointer + pbData += nTempLength; + + // Now write the Sequence token and the OID (the OID is a BLOB in the global + // structure. + + nTempLength = ASNDerWriteToken( pbData, SPNEGO_CONSTRUCTED_SEQUENCE, + g_stcMechOIDList[mechoid].ucOid, + g_stcMechOIDList[mechoid].iLen ); + + return nMechListLength; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ASNDerWriteElement +// +// Parameters: +// [out] pbData - Buffer to write into. +// [in] ucElementSequence - Sequence Token +// [in] ucType - Token Type +// [in] pbTokenValue - Actual Value +// [in] nLength - Length of Data. +// +// Returns: +// int Number of bytes written out +// +// Comments : +// Helper function to write out a SPNEGO Token element. An element +// consists of a sequence token, a type token and the associated data. +// +//////////////////////////////////////////////////////////////////////////// + +int ASNDerWriteElement( unsigned char* pbData, unsigned char ucElementSequence, + unsigned char ucType, unsigned char* pbTokenValue, long nLength ) +{ + // First get the length + long nInternalLength = 0L; + long nElementLength = ASNDerCalcElementLength( nLength, &nInternalLength ); + long nTempLength = 0L; + + // Write out the sequence byte and the length of the type and data + nTempLength = ASNDerWriteToken( pbData, ucElementSequence, NULL, nInternalLength ); + + // Adjust the data pointer + pbData += nTempLength; + + // Now write the type and the data. + nTempLength = ASNDerWriteToken( pbData, ucType, pbTokenValue, nLength ); + + return nElementLength; +} diff --git a/usr/src/lib/libsmbfs/smb/derparse.h b/usr/src/lib/libsmbfs/smb/derparse.h new file mode 100644 index 0000000000..dcdf5828dc --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/derparse.h @@ -0,0 +1,196 @@ +// Copyright (C) 2002 Microsoft Corporation +// All rights reserved. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" +// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +// OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY +// AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Date - 10/08/2002 +// Author - Sanj Surati + +///////////////////////////////////////////////////////////// +// +// DERPARSE.H +// +// SPNEGO Token Handler Header File +// +// Contains the definitions required to properly parse the +// SPNEGO DER encoding. +// +///////////////////////////////////////////////////////////// + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef __DERPARSE_H__ +#define __DERPARSE_H__ + +// C++ Specific +#if defined(__cplusplus) +extern "C" +{ +#endif + +/* Identifier Types */ +#define IDENTIFIER_MASK 0xC0 // Bits 7 and 8 +#define IDENTIFIER_UNIVERSAL 0x00 // 00 = universal +#define IDENTIFIER_APPLICATION 0x40 // 01 = application +#define IDENTIFIER_CONTEXT_SPECIFIC 0x80 // 10 = context specific +#define IDENTIFIER_PRIVATE 0xC0 // 11 = Private + +/* Encoding type */ + +#define FORM_MASK 0x20 /* Bit 6 */ +#define PRIMITIVE 0x00 /* 0 = primitive */ +#define CONSTRUCTED 0x20 /* 1 = constructed */ + +/* Universal tags */ + +#define TAG_MASK 0x1F /* Bits 5 - 1 */ +#define BOOLEAN 0x01 /* 1: TRUE or FALSE */ +#define INTEGER 0x02 /* 2: Arbitrary precision integer */ +#define BITSTRING 0x03 /* 2: Sequence of bits */ +#define OCTETSTRING 0x04 /* 4: Sequence of bytes */ +#define NULLTAG 0x05 /* 5: NULL */ +#define OID 0x06 /* 6: Object Identifier (numeric sequence) */ +#define OBJDESCRIPTOR 0x07 /* 7: Object Descriptor (human readable) */ +#define EXTERNAL 0x08 /* 8: External / Instance Of */ +#define REAL 0x09 /* 9: Real (Mantissa * Base^Exponent) */ +#define ENUMERATED 0x0A /* 10: Enumerated */ +#define EMBEDDED_PDV 0x0B /* 11: Embedded Presentation Data Value */ +#define SEQUENCE 0x10 /* 16: Constructed Sequence / Sequence Of */ +#define SET 0x11 /* 17: Constructed Set / Set Of */ +#define NUMERICSTR 0x12 /* 18: Numeric String (digits only) */ +#define PRINTABLESTR 0x13 /* 19: Printable String */ +#define T61STR 0x14 /* 20: T61 String (Teletex) */ +#define VIDEOTEXSTR 0x15 /* 21: Videotex String */ +#define IA5STR 0x16 /* 22: IA5 String */ +#define UTCTIME 0x17 /* 23: UTC Time */ +#define GENERALIZEDTIME 0x18 /* 24: Generalized Time */ +#define GRAPHICSTR 0x19 /* 25: Graphic String */ +#define VISIBLESTR 0x1A /* 26: Visible String (ISO 646) */ +#define GENERALSTR 0x1B /* 27: General String */ +#define UNIVERSALSTR 0x1C /* 28: Universal String */ +#define BMPSTR 0x1E /* 30: Basic Multilingual Plane String */ + +/* Length encoding */ + +#define LEN_XTND 0x80 /* Indefinite or long form */ +#define LEN_MASK 0x7f /* Bits 7 - 1 */ + +#define SEQ_ELM(n) (IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | ((n)&TAG_MASK)) + +// +// SPNEGO Token Parsing Constants +// + + +// Fixed Length of NegTokenInit ReqFlags field +#define SPNEGO_NEGINIT_MAXLEN_REQFLAGS 2 + +// Difference in bits for ReqFlags token +#define SPNEGO_NEGINIT_REQFLAGS_BITDIFF 1 + +// Fixed Length of NegTokenTarg NegResult field +#define SPNEGO_NEGTARG_MAXLEN_NEGRESULT 1 + +// Application Specific Construct - Always at the start of a NegTokenInit +#define SPNEGO_NEGINIT_APP_CONSTRUCT ( IDENTIFIER_APPLICATION | CONSTRUCTED ) // 0x60 + +// Constructed Sequence token - after the actual token identifier token +#define SPNEGO_CONSTRUCTED_SEQUENCE ( SEQUENCE | CONSTRUCTED ) + +// MechList Type Identifier +#define SPNEGO_MECHLIST_TYPE ( SEQUENCE | CONSTRUCTED | OID ) + +// +// NegTokenInit - Token Identifier and Elements +// + +// NegTokenInit - 0xa0 +#define SPNEGO_NEGINIT_TOKEN_IDENTIFIER ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \ + SPNEGO_TOKEN_INIT ) + +// Structure elements for NegTokenInit +#define SPNEGO_NEGINIT_MECHTYPES 0x0 // MechTypes is element 0 +#define SPNEGO_NEGINIT_REQFLAGS 0x1 // ReqFlags is element 1 +#define SPNEGO_NEGINIT_MECHTOKEN 0x2 // MechToken is element 2 +#define SPNEGO_NEGINIT_MECHLISTMIC 0x3 // MechListMIC is element 3 + +// MechTypes element is 0xa0 +#define SPNEGO_NEGINIT_ELEMENT_MECHTYPES SEQ_ELM(SPNEGO_NEGINIT_MECHTYPES) +// ReqFlags element is 0xa1 +#define SPNEGO_NEGINIT_ELEMENT_REQFLAGS SEQ_ELM(SPNEGO_NEGINIT_REQFLAGS) +// MechToken element is 0xa2 +#define SPNEGO_NEGINIT_ELEMENT_MECHTOKEN SEQ_ELM(SPNEGO_NEGINIT_MECHTOKEN) +// MechListMIC element is 0xa3 +#define SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC SEQ_ELM(SPNEGO_NEGINIT_MECHLISTMIC) + +// +// NegTokenTarg - Token Identifier and Elements +// + +// NegTokenTarg - 0xa1 +#define SPNEGO_NEGTARG_TOKEN_IDENTIFIER ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \ + SPNEGO_TOKEN_TARG ) + +// Structure elements for NegTokenTarg +#define SPNEGO_NEGTARG_NEGRESULT 0x0 // NegResult is element 0 +#define SPNEGO_NEGTARG_SUPPORTEDMECH 0x1 // SupportedMech is element 1 +#define SPNEGO_NEGTARG_RESPONSETOKEN 0x2 // ResponseToken is element 2 +#define SPNEGO_NEGTARG_MECHLISTMIC 0x3 // MechListMIC is element 3 + +// NegResult element is 0xa0 +#define SPNEGO_NEGTARG_ELEMENT_NEGRESULT SEQ_ELM(SPNEGO_NEGTARG_NEGRESULT) +// SupportedMech element is 0xa1 +#define SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH SEQ_ELM(SPNEGO_NEGTARG_SUPPORTEDMECH) +// ResponseToken element is 0xa2 +#define SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN SEQ_ELM(SPNEGO_NEGTARG_RESPONSETOKEN) +// MechListMIC element is 0xa3 +#define SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC SEQ_ELM(SPNEGO_NEGTARG_MECHLISTMIC) + +// +// Defines a GSS Mechanism OID. We keep a single static array +// of these which we'll use for validation/searches/parsing. +// + +typedef struct _mechOID +{ + unsigned char* ucOid; // Byte representation of OID + int iLen; // Length of the OID, length and identifier + int iActualDataLen; // Length of the actual OID + SPNEGO_MECH_OID eMechanismOID; // Which OID is this? +} MECH_OID; + + +// +// ASN Der functions +// + +int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pnLength, + long* pnNumLengthBytes ); +int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken, + long nCheckLength, long nBoundaryLength, long* pnLength, + long* pnTokenLength ); +int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long nBoundaryLength, + long* pnTokenLength ); +int ASNDerCalcNumLengthBytes( long nLength ); +long ASNDerCalcTokenLength( long nLength, long nDataLength ); +long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength ); +long ASNDerCalcMechListLength( SPNEGO_MECH_OID mechoid, long* pnInternalLength ); +int ASNDerWriteLength( unsigned char* pbData, long nLength ); +int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType, + unsigned char* pbTokenValue, long nLength ); +int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID ); +long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID mechoid ); +int ASNDerWriteElement( unsigned char* pbData, unsigned char ucElementSequence, + unsigned char ucType, unsigned char* pbTokenValue, long nLength ); + + + // C++ Specific +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/usr/src/lib/libsmbfs/smb/file.c b/usr/src/lib/libsmbfs/smb/file.c new file mode 100644 index 0000000000..dd738e8d97 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/file.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: file.c,v 1.4 2004/12/13 00:25:21 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/mount.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#include <pwd.h> +#include <grp.h> +#include <unistd.h> + +#include <sys/types.h> +extern uid_t real_uid, eff_uid; + +#include <netsmb/smb_lib.h> +#include <cflib.h> + +int +smb_read(struct smb_ctx *ctx, smbfh fh, off_t offset, size_t count, char *dst) +{ + struct smbioc_rw rwrq; + + bzero(&rwrq, sizeof (rwrq)); + rwrq.ioc_fh = fh; + rwrq.ioc_base = dst; + rwrq.ioc_cnt = count; + rwrq.ioc_offset = offset; + seteuid(eff_uid); + if (ioctl(ctx->ct_fd, SMBIOC_READ, &rwrq) == -1) { + seteuid(real_uid); /* and back to real user */ + return (-1); + } + seteuid(real_uid); /* and back to real user */ + return (rwrq.ioc_cnt); +} + +int +smb_write(struct smb_ctx *ctx, smbfh fh, off_t offset, size_t count, + const char *src) +{ + struct smbioc_rw rwrq; + + bzero(&rwrq, sizeof (rwrq)); + rwrq.ioc_fh = fh; + rwrq.ioc_base = (char *)src; + rwrq.ioc_cnt = count; + rwrq.ioc_offset = offset; + seteuid(eff_uid); + if (ioctl(ctx->ct_fd, SMBIOC_WRITE, &rwrq) == -1) { + seteuid(real_uid); /* and back to real user */ + return (-1); + } + seteuid(real_uid); /* and back to real user */ + return (rwrq.ioc_cnt); +} diff --git a/usr/src/lib/libsmbfs/smb/keychain.c b/usr/src/lib/libsmbfs/smb/keychain.c new file mode 100644 index 0000000000..72a979022f --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/keychain.c @@ -0,0 +1,199 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * External interface to the libsmbfs/netsmb keychain + * storage mechanism. This interface is consumed by + * the "smbutil" commands: login, logout, ... + * and by the SMBFS PAM module. + */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <libintl.h> + +#include <netsmb/smb_dev.h> +#include <netsmb/smb_lib.h> +#include <netsmb/smb_keychain.h> + +#include <cflib.h> + +/* common func. for add/del/chk */ +static int +smbfs_keychain_cmn( + int cmd, + uid_t uid, + const char *dom, + const char *usr, + const char *pass) +{ + smbioc_pk_t pk; + int err, fd; + + memset(&pk, 0, sizeof (pk)); + + pk.pk_uid = uid; + + switch (cmd) { + + case SMBIOC_PK_ADD: + if (pass == NULL) + return (SMB_KEYCHAIN_BADPASSWD); + if (strlcpy(pk.pk_pass, pass, sizeof (pk.pk_pass)) >= + sizeof (pk.pk_pass)) + return (SMB_KEYCHAIN_BADPASSWD); + /* FALLTHROUGH */ + + case SMBIOC_PK_CHK: + case SMBIOC_PK_DEL: + if (dom == NULL) + return (SMB_KEYCHAIN_BADDOMAIN); + if (strlcpy(pk.pk_dom, dom, sizeof (pk.pk_dom)) >= + sizeof (pk.pk_dom)) + return (SMB_KEYCHAIN_BADDOMAIN); + if (usr == NULL) + return (SMB_KEYCHAIN_BADUSER); + if (strlcpy(pk.pk_usr, usr, sizeof (pk.pk_usr)) >= + sizeof (pk.pk_usr)) + return (SMB_KEYCHAIN_BADUSER); + break; + + case SMBIOC_PK_DEL_OWNER: /* all owned by the caller */ + case SMBIOC_PK_DEL_EVERYONE: /* all owned by everyone */ + /* + * These two do not copyin any args, but we'll + * pass &pk here anyway just so we can use the + * common code path below. + */ + break; + + default: + return (SMB_KEYCHAIN_UNKNOWN); + } + + fd = smb_open_driver(); + if (fd < 0) { + err = SMB_KEYCHAIN_NODRIVER; + goto out; + } + + err = 0; + if (ioctl(fd, cmd, &pk) < 0) + err = errno; + + close(fd); +out: + memset(&pk, 0, sizeof (pk)); + return (err); +} + +/* Add a password to the keychain. */ +int +smbfs_keychain_add(uid_t uid, const char *dom, const char *usr, + const char *pass) +{ + return (smbfs_keychain_cmn(SMBIOC_PK_ADD, uid, dom, usr, pass)); +} + +/* Delete a password from the keychain. */ +int +smbfs_keychain_del(uid_t uid, const char *dom, const char *usr) +{ + return (smbfs_keychain_cmn(SMBIOC_PK_DEL, uid, dom, usr, NULL)); +} + +/* + * Check for existence of a keychain entry. + * Returns 0 if it exists, else ENOENT. + */ +int +smbfs_keychain_chk(const char *dom, const char *usr) +{ + return (smbfs_keychain_cmn(SMBIOC_PK_CHK, (uid_t)-1, dom, usr, NULL)); +} + +/* + * Delete all keychain entries owned by the caller. + */ +int +smbfs_keychain_del_owner() +{ + return (smbfs_keychain_cmn(SMBIOC_PK_DEL_OWNER, getuid(), 0, 0, 0)); +} + +/* + * Delete all keychain entries (regardless of onwer). + * Requires super-user privliege. + */ +int +smbfs_keychain_del_everyone() +{ + return (smbfs_keychain_cmn(SMBIOC_PK_DEL_EVERYONE, getuid(), 0, 0, 0)); +} + + +/* + * This is not really part of the keychain library, + * but is typically needed in code that wants to + * provide (editable) defaults for domain/user + * + * Get default domain and user names + * Server name is optional. + */ +int +smbfs_default_dom_usr(const char *home, const char *server, + char *dom, int maxdom, char *usr, int maxusr) +{ + struct smb_ctx sctx, *ctx = &sctx; + int err; + + err = smb_ctx_init(ctx, 0, NULL, SMBL_VC, SMBL_VC, SMB_ST_ANY); + if (err) + return (err); + if (server) + smb_ctx_setserver(ctx, server); + if (home && *home) + ctx->ct_home = (char *)home; + err = smb_ctx_readrc(ctx); + if (err) + return (err); + if (smb_rc) + rc_close(smb_rc); + + if (dom) + strlcpy(dom, ctx->ct_ssn.ioc_workgroup, maxdom); + + if (usr) + strlcpy(usr, ctx->ct_ssn.ioc_user, maxusr); + + return (0); +} diff --git a/usr/src/lib/libsmbfs/smb/llib-lsmbfs b/usr/src/lib/libsmbfs/smb/llib-lsmbfs new file mode 100644 index 0000000000..05e1967055 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/llib-lsmbfs @@ -0,0 +1,32 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/*LINTLIBRARY*/ +/*PROTOLIB1*/ + +#include <netsmb/smb_lib.h> diff --git a/usr/src/lib/libsmbfs/smb/mapfile-vers b/usr/src/lib/libsmbfs/smb/mapfile-vers new file mode 100644 index 0000000000..1dadbbe825 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/mapfile-vers @@ -0,0 +1,72 @@ +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +SUNWprivate_1.0 { + global: + dropsuid; + nb_ctx_create; + nb_ctx_done; + nb_ctx_readrcsection; + nb_ctx_resolve; + nb_ctx_setns; + nb_resolvehost_in; + nbns_getnodestatus; + nbns_resolvename; + nls_str_upper; + rc_close; + rc_open; + smb_ctx_done; + smb_ctx_flags2; + smb_ctx_init; + smb_ctx_lookup; + smb_ctx_opt; + smb_ctx_readrc; + smb_ctx_resolve; + smb_ctx_setshare; + smb_ctx_tdis; + smb_debug; + smb_error; + smb_getprogname; + smb_lib_init; + smb_netshareenum; + smb_open_rcfile; + smb_simplecrypt; + smb_simpledecrypt; + smb_strerror; + smb_rc; # data + smb_read; + smb_write; + smb_verbose; + smbfs_default_dom_usr; + smbfs_keychain_add; + smbfs_keychain_chk; + smbfs_keychain_del; + smbfs_keychain_del_everyone; + smbfs_keychain_del_owner; + unpercent; + local: + *; +}; diff --git a/usr/src/lib/libsmbfs/smb/mbuf.c b/usr/src/lib/libsmbfs/smb/mbuf.c new file mode 100644 index 0000000000..f03c4fedc3 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/mbuf.c @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: mbuf.c,v 1.3 2004/12/13 00:25:22 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <libintl.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_lib.h> +#include <netsmb/mchain.h> + +#ifdef APPLE +#define __func__ "" +#define MBERROR(format, args...) \ + printf("%s(%d): "format, __func__, __LINE__, ## args) +#endif + +static int +m_get(size_t len, struct mbuf **mpp) +{ + struct mbuf *m; + + len = M_ALIGN(len); + if (len < M_MINSIZE) + len = M_MINSIZE; + m = malloc(M_BASESIZE + len); + if (m == NULL) + return (ENOMEM); + bzero(m, M_BASESIZE + len); + m->m_maxlen = len; + m->m_data = M_TOP(m); + *mpp = m; + return (0); +} + +static void +m_free(struct mbuf *m) +{ + free(m); +} + +static void +m_freem(struct mbuf *m0) +{ + struct mbuf *m; + + while (m0) { + m = m0->m_next; + m_free(m0); + m0 = m; + } +} + +static size_t +m_totlen(struct mbuf *m0) +{ + struct mbuf *m = m0; + int len = 0; + + while (m) { + len += m->m_len; + m = m->m_next; + } + return (len); +} + +int +m_lineup(struct mbuf *m0, struct mbuf **mpp) +{ + struct mbuf *nm, *m; + char *dp; + size_t len; + int error; + + if (m0->m_next == NULL) { + *mpp = m0; + return (0); + } + if ((error = m_get(m_totlen(m0), &nm)) != 0) + return (error); + dp = mtod(nm, char *); + while (m0) { + len = m0->m_len; + bcopy(m0->m_data, dp, len); + dp += len; + m = m0->m_next; + m_free(m0); + m0 = m; + } + *mpp = nm; + return (0); +} + +int +mb_init(struct mbdata *mbp, size_t size) +{ + struct mbuf *m; + int error; + + if ((error = m_get(size, &m)) != 0) + return (error); + return (mb_initm(mbp, m)); +} + +int +mb_initm(struct mbdata *mbp, struct mbuf *m) +{ + bzero(mbp, sizeof (*mbp)); + mbp->mb_top = mbp->mb_cur = m; + mbp->mb_pos = mtod(m, char *); + return (0); +} + +int +mb_done(struct mbdata *mbp) +{ + if (mbp->mb_top) { + m_freem(mbp->mb_top); + mbp->mb_top = NULL; + } + return (0); +} + +int +m_getm(struct mbuf *top, size_t len, struct mbuf **mpp) +{ + struct mbuf *m, *mp; + int error; + + for (mp = top; ; mp = mp->m_next) { + len -= M_TRAILINGSPACE(mp); + if (mp->m_next == NULL) + break; + + } + if (len > 0) { + if ((error = m_get(len, &m)) != 0) + return (error); + mp->m_next = m; + } + *mpp = top; + return (0); +} + +/* + * Routines to put data in a buffer + */ +#define MB_PUT(t) int error; t *p; \ + if ((error = mb_fit(mbp, sizeof (t), (char **)&p)) != 0) \ + return (error) + +/* + * Check if object of size 'size' fit to the current position and + * allocate new mbuf if not. Advance pointers and increase length of mbuf(s). + * Return pointer to the object placeholder or NULL if any error occured. + */ +int +mb_fit(struct mbdata *mbp, size_t size, char **pp) +{ + struct mbuf *m, *mn; + int error; + + m = mbp->mb_cur; + if (M_TRAILINGSPACE(m) < (int)size) { + if ((error = m_get(size, &mn)) != 0) + return (error); + mbp->mb_pos = mtod(mn, char *); + mbp->mb_cur = m->m_next = mn; + m = mn; + } + m->m_len += size; + *pp = mbp->mb_pos; + mbp->mb_pos += size; + mbp->mb_count += size; + return (0); +} + +int +mb_put_uint8(struct mbdata *mbp, uint8_t x) +{ + MB_PUT(uint8_t); + *p = x; + return (0); +} + +int +mb_put_uint16be(struct mbdata *mbp, uint16_t x) +{ + MB_PUT(uint16_t); + /* LINTED */ + setwbe(p, 0, x); + return (0); +} + +int +mb_put_uint16le(struct mbdata *mbp, uint16_t x) +{ + MB_PUT(uint16_t); + /* LINTED */ + setwle(p, 0, x); + return (0); +} + +int +mb_put_uint32be(struct mbdata *mbp, uint32_t x) +{ + MB_PUT(uint32_t); + /* LINTED */ + setdbe(p, 0, x); + return (0); +} + +int +mb_put_uint32le(struct mbdata *mbp, uint32_t x) +{ + MB_PUT(uint32_t); + /* LINTED */ + setdle(p, 0, x); + return (0); +} + +int +mb_put_uint64be(struct mbdata *mbp, uint64_t x) +{ + MB_PUT(uint64_t); + *p = htobeq(x); + return (0); +} + +int +mb_put_uint64le(struct mbdata *mbp, uint64_t x) +{ + MB_PUT(uint64_t); + *p = htoleq(x); + return (0); +} + +int +mb_put_mem(struct mbdata *mbp, const char *source, size_t size) +{ + struct mbuf *m; + char *dst; + size_t cplen; + int error; + + if (size == 0) + return (0); + m = mbp->mb_cur; + if ((error = m_getm(m, size, &m)) != 0) + return (error); + while (size > 0) { + cplen = M_TRAILINGSPACE(m); + if (cplen == 0) { + m = m->m_next; + continue; + } + if (cplen > size) + cplen = size; + dst = mtod(m, char *) + m->m_len; + if (source) { + bcopy(source, dst, cplen); + source += cplen; + } else + bzero(dst, cplen); + size -= cplen; + m->m_len += cplen; + mbp->mb_count += cplen; + } + mbp->mb_pos = mtod(m, char *) + m->m_len; + mbp->mb_cur = m; + return (0); +} + +int +mb_put_mbuf(struct mbdata *mbp, struct mbuf *m) +{ + mbp->mb_cur->m_next = m; + while (m) { + mbp->mb_count += m->m_len; + if (m->m_next == NULL) + break; + m = m->m_next; + } + mbp->mb_pos = mtod(m, char *) + m->m_len; + mbp->mb_cur = m; + return (0); +} + +int +mb_put_pstring(struct mbdata *mbp, const char *s) +{ + int error, len = strlen(s); + + if (len > 255) { + len = 255; + } + if ((error = mb_put_uint8(mbp, len)) != 0) + return (error); + return (mb_put_mem(mbp, s, len)); +} + +/* + * Routines for fetching data from an mbuf chain + */ +#define mb_left(m, p) (mtod(m, char *) + (m)->m_len - (p)) + +int +mb_get_uint8(struct mbdata *mbp, uint8_t *x) +{ + return (mb_get_mem(mbp, (char *)x, 1)); +} + +int +mb_get_uint16(struct mbdata *mbp, uint16_t *x) +{ + return (mb_get_mem(mbp, (char *)x, 2)); +} + +int +mb_get_uint16le(struct mbdata *mbp, uint16_t *x) +{ + uint16_t v; + int error = mb_get_uint16(mbp, &v); + + if (x != NULL) + *x = letohs(v); + return (error); +} + +int +mb_get_uint16be(struct mbdata *mbp, uint16_t *x) { + uint16_t v; + int error = mb_get_uint16(mbp, &v); + + if (x != NULL) + *x = betohs(v); + return (error); +} + +int +mb_get_uint32(struct mbdata *mbp, uint32_t *x) +{ + return (mb_get_mem(mbp, (char *)x, 4)); +} + +int +mb_get_uint32be(struct mbdata *mbp, uint32_t *x) +{ + uint32_t v; + int error; + + error = mb_get_uint32(mbp, &v); + if (x != NULL) + *x = betohl(v); + return (error); +} + +int +mb_get_uint32le(struct mbdata *mbp, uint32_t *x) +{ + uint32_t v; + int error; + + error = mb_get_uint32(mbp, &v); + if (x != NULL) + *x = letohl(v); + return (error); +} + +int +mb_get_uint64(struct mbdata *mbp, uint64_t *x) +{ + return (mb_get_mem(mbp, (char *)x, 8)); +} + +int +mb_get_uint64be(struct mbdata *mbp, uint64_t *x) +{ + uint64_t v; + int error; + + error = mb_get_uint64(mbp, &v); + if (x != NULL) + *x = betohq(v); + return (error); +} + +int +mb_get_uint64le(struct mbdata *mbp, uint64_t *x) +{ + uint64_t v; + int error; + + error = mb_get_uint64(mbp, &v); + if (x != NULL) + *x = letohq(v); + return (error); +} + +int +mb_get_mem(struct mbdata *mbp, char *target, size_t size) +{ + struct mbuf *m = mbp->mb_cur; + uint_t count; + + while (size > 0) { + if (m == NULL) { +#ifdef DEBUG + printf( + dgettext(TEXT_DOMAIN, "incomplete copy\n")); +#endif +#ifdef APPLE + MBERROR("incomplete copy\n"); +#endif + return (EBADRPC); + } + count = mb_left(m, mbp->mb_pos); + if (count == 0) { + mbp->mb_cur = m = m->m_next; + if (m) + mbp->mb_pos = mtod(m, char *); + continue; + } + if (count > size) + count = size; + size -= count; + if (target) { + if (count == 1) { + *target++ = *mbp->mb_pos; + } else { + bcopy(mbp->mb_pos, target, count); + target += count; + } + } + mbp->mb_pos += count; + } + return (0); +} diff --git a/usr/src/lib/libsmbfs/smb/nb.c b/usr/src/lib/libsmbfs/smb/nb.c new file mode 100644 index 0000000000..f60ae0b314 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/nb.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2000, 2001 Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: nb.c,v 1.1.1.2 2001/07/06 22:38:42 conrad Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/socket.h> + +#include <ctype.h> +#include <netdb.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <unistd.h> +#include <libintl.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <netsmb/netbios.h> +#include <netsmb/smb_lib.h> +#include <netsmb/nb_lib.h> + +#include <cflib.h> + +int +nb_ctx_create(struct nb_ctx **ctxpp) +{ + struct nb_ctx *ctx; + + ctx = malloc(sizeof (struct nb_ctx)); + if (ctx == NULL) + return (ENOMEM); + bzero(ctx, sizeof (struct nb_ctx)); + ctx->nb_flags = NBCF_NS_ENABLE | NBCF_BC_ENABLE; + *ctxpp = ctx; + return (0); +} + +void +nb_ctx_done(struct nb_ctx *ctx) +{ + if (ctx == NULL) + return; + if (ctx->nb_scope) + free(ctx->nb_scope); + if (ctx) + free(ctx); +} + +static int +nb_ctx_setwins(in_addr_t *ina_p, const char *str) +{ + struct in_addr ina; + struct sockaddr *sap; + int error; + + if (str == NULL || str[0] == 0) + return (EINVAL); + + if (inet_aton(str, &ina)) { + *ina_p = ina.s_addr; + } else { + error = nb_resolvehost_in(str, &sap); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, "can't resolve %s"), + error, str); + return (error); + } + if (sap->sa_family != AF_INET) { + smb_error(dgettext(TEXT_DOMAIN, + "unsupported address family %d"), 0, + sap->sa_family); + return (EINVAL); + } + /*LINTED*/ + *ina_p = ((struct sockaddr_in *)sap)->sin_addr.s_addr; + free(sap); + } + + return (0); +} + +/* + * This is called by "smbutil lookup" to handle the + * "-w wins_server" option. Let the semantics of + * this option be: Use specified WINS server only. + * If specified server is the broadcast address, + * set broadcast mode (and no WINS servers). + */ +int +nb_ctx_setns(struct nb_ctx *ctx, const char *addr) +{ + int error; + + error = nb_ctx_setwins(&ctx->nb_wins1, addr); + if (error) + return (error); + ctx->nb_wins2 = 0; + + /* Deal with explicit request for broadcast. */ + if (ctx->nb_wins1 == INADDR_BROADCAST) { + ctx->nb_wins1 = 0; + ctx->nb_flags |= NBCF_BC_ENABLE; + } + return (0); +} + +int +nb_ctx_setscope(struct nb_ctx *ctx, const char *scope) +{ + size_t slen = strlen(scope); + + if (slen >= 128) { + smb_error(dgettext(TEXT_DOMAIN, + "scope '%s' is too long"), 0, scope); + return (ENAMETOOLONG); + } + if (ctx->nb_scope) + free(ctx->nb_scope); + ctx->nb_scope = malloc(slen + 1); + if (ctx->nb_scope == NULL) + return (ENOMEM); + nls_str_upper(ctx->nb_scope, scope); + return (0); +} + +/* + * Now get the WINS server IP addresses directly + * when reading the RC files, so no longer need to + * lookup any names here. + */ +int +nb_ctx_resolve(struct nb_ctx *ctx) +{ + ctx->nb_flags |= NBCF_RESOLVED; + return (0); +} + +/* + * used level values: + * 0 - default + * 1 - server + * + * All of these are normally system-wide settings; + * the checks are in rc_parse() in rcfile.c. + */ +int +nb_ctx_readrcsection(struct rcfile *rcfile, struct nb_ctx *ctx, + const char *sname, int level) +{ + char *p; + int error; + int nbns_enable; + int nbns_broadcast; + + if (level > 1) + return (EINVAL); +#ifdef NOT_DEFINED + rc_getint(rcfile, sname, "nbtimeout", &ctx->nb_timo); + rc_getstringptr(rcfile, sname, "nbscope", &p); + if (p) + nb_ctx_setscope(ctx, p); +#endif + /* "nbns" will be "wins1" some day, and we'll have a "wins2" also */ + rc_getstringptr(rcfile, sname, "nbns", &p); + if (p) { + error = nb_ctx_setwins(&ctx->nb_wins1, p); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "invalid address specified in the section %s"), + 0, sname); + return (error); + } + } + error = rc_getbool(rcfile, sname, "nbns_enable", &nbns_enable); + if (error == 0 && nbns_enable == 0) + ctx->nb_flags &= ~NBCF_NS_ENABLE; + error = rc_getbool(rcfile, sname, "nbns_broadcast", &nbns_broadcast); + if (error == 0 && nbns_broadcast == 0) + ctx->nb_flags &= ~NBCF_BC_ENABLE; + return (0); +} + +#ifdef I18N /* never defined, permits xgettext(1) to pick out strings */ +static const char *nb_err_rcode[] = { + gettext("bad request/response format"), + gettext("NBNS server failure"), + gettext("no such name"), + gettext("unsupported request"), + gettext("request rejected"), + gettext("name already registered)" +}; + +static const char *nb_err[] = { + gettext("host not found"), + gettext("too many redirects"), + gettext("invalid response"), + gettext("NETBIOS name too long"), + gettext("no interface to broadcast on and no NBNS server specified") +}; +#else +static const char *nb_err_rcode[] = { + "bad request/response format", + "NBNS server failure", + "no such name", + "unsupported request", + "request rejected", + "name already registered" +}; + +static const char *nb_err[] = { + "host not found", + "too many redirects", + "invalid response", + "NETBIOS name too long", + "no interface to broadcast on and no NBNS server specified" +}; +#endif + +const char * +nb_strerror(int error) +{ + if (error == 0) + return (NULL); + if (error <= NBERR_ACTIVE) + return (nb_err_rcode[error - 1]); + else if (error >= NBERR_HOSTNOTFOUND && error < NBERR_MAX) + return (nb_err[error - NBERR_HOSTNOTFOUND]); + else + return (NULL); +} diff --git a/usr/src/lib/libsmbfs/smb/nb_name.c b/usr/src/lib/libsmbfs/smb/nb_name.c new file mode 100644 index 0000000000..1c631d73d1 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/nb_name.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: nb_name.c,v 1.11 2004/12/11 05:23:59 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/socket.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <libintl.h> +#include <assert.h> + +#include <netsmb/netbios.h> +#include <netsmb/smb_lib.h> +#include <netsmb/nb_lib.h> +#include <netsmb/mchain.h> + +int +nb_snballoc(int namelen, struct sockaddr_nb **dst) +{ + struct sockaddr_nb *snb; + int slen; + + slen = sizeof (struct sockaddr_nb); + snb = malloc(slen); + if (snb == NULL) + return (ENOMEM); + bzero(snb, slen); + snb->snb_family = AF_NETBIOS; + *dst = snb; + return (0); +} + +void +nb_snbfree(struct sockaddr *snb) +{ + free(snb); +} + +/* + * Create a full NETBIOS address + */ +int +nb_sockaddr(struct sockaddr *peer, struct nb_name *np, + struct sockaddr_nb **dst) + +{ + struct sockaddr_nb *snb; + struct sockaddr_in *sin; + struct hostent *hst; + int nmlen, error; + + if (peer && (peer->sa_family != AF_INET)) + return (EPROTONOSUPPORT); +#if NOT_DEFINED /* moved encoding into kernel */ + nmlen = nb_name_len(np); + if (nmlen < NB_ENCNAMELEN) + return (EINVAL); +#else + nmlen = NB_NAMELEN; +#endif + error = nb_snballoc(nmlen, &snb); + if (error) + return (error); + + /* + * Moved toupper() work to callers. + * + * Moved NetBIOS name encoding into the driver + * so we have readable names right up until the + * point where we marshall them in to a message. + * Just makes debugging easier. + */ +#if NOT_DEFINED + if (nmlen != nb_name_encode(np, snb->snb_name)) + printf(dgettext(TEXT_DOMAIN, + "a bug somewhere in the nb_name* code\n")); + /* XXX */ +#else + /* + * OK, nb_snballoc() did bzero, set snb_family. + * Hacks for "*" moved here from nb_name_encode(), + * but belongs where nn_name is filled in... + * XXX fix later + */ + if (strcmp(np->nn_name, "*") == 0) { + /* Star is special: No blanks, type, etc. */ + snb->snb_name[0] = '*'; + } else { + /* Normal name: pad with blanks, add type. */ + assert(NB_NAMELEN == 16); + snprintf(snb->snb_name, NB_NAMELEN, + "%-15.15s", np->nn_name); + snb->snb_name[15] = (char)np->nn_type; + } +#endif + + if (peer) { + /*LINTED*/ + sin = (struct sockaddr_in *)peer; + snb->snb_ipaddr = sin->sin_addr.s_addr; + } + *dst = snb; + return (0); +} + +int +nb_name_len(struct nb_name *np) +{ + char *name; + int len, sclen; + + len = 1 + NB_ENCNAMELEN; + if (np->nn_scope == NULL) + return (len + 1); + sclen = 0; + for (name = np->nn_scope; *name; name++) { + if (*name == '.') { + sclen = 0; + } else { + if (sclen < NB_MAXLABLEN) { + sclen++; + len++; + } + } + } + return (len + 1); +} + +int +nb_encname_len(const uchar_t *str) +{ + const uchar_t *cp = str; + int len, blen; + + if ((cp[0] & 0xc0) == 0xc0) + return (-1); /* first two bytes are offset to name */ + + len = 1; + for (;;) { + blen = *cp; + if (blen++ == 0) + break; + len += blen; + cp += blen; + } + return (len); +} + +int +nb_name_encode(struct nb_name *np, uchar_t *dst) +{ + char *name; + uchar_t *plen; + uchar_t ch, *cp = dst; + char *p, buf1[NB_NAMELEN+1]; + int i, lblen; + + /* + * XXX: I'd rather see this part moved into + * callers of this function, leaving just + * the pure NB encoding here. -GWR + */ + name = np->nn_name; + if (name[0] == '*') { + /* Star is special: No blanks, type, etc. */ + bzero(buf1, NB_NAMELEN); + buf1[0] = '*'; + } else { + /* Normal name: pad with blanks, add type. */ + assert(NB_NAMELEN == 16); + snprintf(buf1, NB_NAMELEN, + "%-15.15s", name); + buf1[15] = (char)np->nn_type; + } + name = buf1; + + /* + * Do the NetBIOS "first-level encoding" here. + * (RFC1002 explains this wierdness...) + * See similar code in kernel nsmb module: + * uts/common/fs/smbclnt/netsmb/smb_trantcp.c + * + * Here is what we marshall: + * uint8_t NAME_LENGTH (always 32) + * uint8_t ENCODED_NAME[32] + * uint8_t SCOPE_LENGTH + * Scope follows here, then another null. + */ + + /* NAME_LENGTH */ + *cp++ = (2 * NB_NAMELEN); + + /* ENCODED_NAME */ + for (i = 0; i < NB_NAMELEN; i++) { + ch = name[i]; + *cp++ = 'A' + ((ch >> 4) & 0xF); + *cp++ = 'A' + ((ch) & 0xF); + } + + /* + * NetBIOS "scope" sting encoding, + * a.k.a second-level encoding. + * See RFC1002 for the details. + * + * Note: plen points to the length byte at the + * start of each string. This keeps a pointer + * to the location and fills it in after the + * length of the string is determined. + */ +#if NOT_DEFINED /* XXX: not yet */ + if (np->nn_scope) { + plen = cp++; + *plen = 0; /* fill in later */ + lblen = 0; + for (p = np->nn_scope; ; p++) { + if (*p == '.' || *p == 0) { + *plen = lblen; + if (*p == 0) + break; + plen = cp++; + *plen = 0; + lblen = 0; + } else { + if (lblen < NB_MAXLABLEN) { + *cp++ = *p; + lblen++; + } + } + } + } else +#endif /* XXX: not yet */ + { + *cp++ = 0; + } + + return (cp - dst); +} diff --git a/usr/src/lib/libsmbfs/smb/nb_net.c b/usr/src/lib/libsmbfs/smb/nb_net.c new file mode 100644 index 0000000000..7398cff0e0 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/nb_net.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: nb_net.c,v 1.8 2004/03/19 01:49:47 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/sockio.h> +#include <net/if.h> +#include <ctype.h> +#include <netdb.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <unistd.h> + +#include <err.h> + +#include <netsmb/netbios.h> +#include <netsmb/smb_lib.h> +#include <netsmb/nb_lib.h> + +int +nb_getlocalname(char *name, size_t maxlen) +{ + char buf[1024], *cp; + + if (gethostname(buf, sizeof (buf)) != 0) + return (errno); + cp = strchr(buf, '.'); + if (cp) + *cp = 0; + strlcpy(name, buf, maxlen); + return (0); +} + +int +nb_resolvehost_in(const char *name, struct sockaddr **dest) +{ + struct hostent *h; + struct sockaddr_in *sinp; + in_addr_t addr; + struct in_addr in; + int len; + char **p; + + + h = gethostbyname(name); + if (!h) { +#ifdef DEBUG + warnx("can't get server address `%s': ", name); +#endif + return (ENETDOWN); + } + if (h->h_addrtype != AF_INET) { +#ifdef DEBUG + warnx("address for `%s' is not in the AF_INET family", name); +#endif + return (EAFNOSUPPORT); + } + if (h->h_length != 4) { +#ifdef DEBUG + warnx("address for `%s' has invalid length", name); +#endif + return (EAFNOSUPPORT); + } + len = sizeof (struct sockaddr_in); + sinp = malloc(len); + if (sinp == NULL) + return (ENOMEM); + bzero(sinp, len); + /* + * There is no sin_len in sockaddr_in structure on Solaris. + * sinp->sin_len = len; + */ + sinp->sin_family = h->h_addrtype; + memcpy(&sinp->sin_addr.s_addr, *h->h_addr_list,\ + sizeof (sinp->sin_addr.s_addr)); + sinp->sin_port = htons(SMB_TCP_PORT); + *dest = (struct sockaddr *)sinp; + return (0); +} + +#ifdef NOT_DEFINED +int +nb_enum_if(struct nb_ifdesc **iflist) { + struct lifconf ifc; + struct lifreq *ifrqp; + struct nb_ifdesc *ifd; + struct in_addr iaddr, imask; + struct lifnum ifn; + char *ifrdata, *iname; + int s, rdlen, ifcnt, error, iflags, i; + + *iflist = NULL; + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s == -1) + return (errno); + + /* Get number of interfaces. */ + ifn.lifn_family = AF_INET; + ifn.lifn_flags = 0; + ifn.lifn_count = 0; + if (ioctl(s, SIOCGLIFNUM, &ifn) != 0) { + error = errno; + goto bad; + } + + rdlen = ifn.lifn_count * sizeof (struct lifreq); + ifrdata = malloc(rdlen); + if (ifrdata == NULL) { + error = ENOMEM; + goto bad; + } + ifc.lifc_flags = 0; + ifc.lifc_family = AF_INET; + ifc.lifc_len = rdlen; + ifc.lifc_buf = ifrdata; + if (ioctl(s, SIOCGLIFCONF, &ifc) != 0) { + error = errno; + goto bad; + } + ifrqp = ifc.lifc_req; + ifcnt = ifc.lifc_len / sizeof (struct lifreq); + error = 0; + for (i = 0; i < ifcnt; i++, ifrqp++) { + /* XXX for now, avoid IP6 broadcast performance costs */ + if (ifrqp->lifr_addr.ss_family != AF_INET) + continue; + if (ioctl(s, SIOCGLIFFLAGS, ifrqp) != 0) + continue; + iflags = ifrqp->lifr_flags; + if ((iflags & IFF_UP) == 0 || (iflags & IFF_BROADCAST) == 0) + continue; + + if (ioctl(s, SIOCGLIFADDR, ifrqp) != 0 || + ifrqp->lifr_addr.ss_family != AF_INET) { + continue; + } + iname = ifrqp->lifr_name; + if (strlen(iname) >= sizeof (ifd->id_name)) + continue; + iaddr = (*(struct sockaddr_in *)&ifrqp->lifr_addr).sin_addr; + + if (ioctl(s, SIOCGLIFNETMASK, ifrqp) != 0) + continue; + imask = ((struct sockaddr_in *)&ifrqp->lifr_addr)->sin_addr; + + ifd = malloc(sizeof (struct nb_ifdesc)); + if (ifd == NULL) + return (ENOMEM); + bzero(ifd, sizeof (struct nb_ifdesc)); + strcpy(ifd->id_name, iname); + ifd->id_flags = iflags; + ifd->id_addr = iaddr; + ifd->id_mask = imask; + ifd->id_next = *iflist; + *iflist = ifd; + } +bad: + free(ifrdata); + close(s); + return (error); +} + +/*ARGSUSED*/ +int +nbns_resolvename(const char *name, struct sockaddr **dest) +{ + printf("NetBIOS name resolver is not included in this distribution.\n"); + printf("Please use '-I' option to specify an IP address of server.\n"); + return (EHOSTUNREACH); +} + +int +nb_hostlookup(struct nb_name *np, const char *server, const char *hint, + struct sockaddr_nb **dst) +{ + struct sockaddr_nb *snb; + int error; + + error = nb_sockaddr(NULL, np, &snb); + if (error) + return (error); + do { + if (hint) { + error = nb_resolvehost_in(host, snb); + if (error) + break; + } else { + error = nb_resolvename(server); + } + } while (0); + if (!error) { + *dst = snb; + } else + nb_snbfree(snb); + return (error); +} +#endif diff --git a/usr/src/lib/libsmbfs/smb/nbns_rq.c b/usr/src/lib/libsmbfs/smb/nbns_rq.c new file mode 100644 index 0000000000..17de0a103a --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/nbns_rq.c @@ -0,0 +1,581 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: nbns_rq.c,v 1.9 2005/02/24 02:04:38 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <ctype.h> +#include <netdb.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <unistd.h> +#include <libintl.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <tsol/label.h> + +#define NB_NEEDRESOLVER +#include <netsmb/netbios.h> +#include <netsmb/smb_lib.h> +#include <netsmb/nb_lib.h> +#include <netsmb/mchain.h> + +static int nbns_rq_create(int opcode, struct nb_ctx *ctx, + struct nbns_rq **rqpp); +static void nbns_rq_done(struct nbns_rq *rqp); +static int nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp); +static int nbns_rq_prepare(struct nbns_rq *rqp); +static int nbns_rq(struct nbns_rq *rqp); + +static struct nb_ifdesc *nb_iflist = NULL; + +int +nbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp) +{ + struct nbns_rq *rqp; + struct nb_name nn; + struct nbns_rr rr; + struct sockaddr_in *dest; + int error, rdrcount, len; + + if (strlen(name) > NB_NAMELEN) + return (NBERROR(NBERR_NAMETOOLONG)); + error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp); + if (error) + return (error); + /* + * Pad the name with blanks, but + * leave the "type" byte NULL. + * nb_name_encode adds the type. + */ + bzero(&nn, sizeof (nn)); + snprintf(nn.nn_name, NB_NAMELEN, "%-15.15s", name); + nn.nn_type = NBT_SERVER; + nn.nn_scope = ctx->nb_scope; + rqp->nr_nmflags = NBNS_NMFLAG_RD; + rqp->nr_qdname = &nn; + rqp->nr_qdtype = NBNS_QUESTION_TYPE_NB; + rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN; + rqp->nr_qdcount = 1; + rqp->nr_maxretry = 5; + + error = nbns_rq_prepare(rqp); + if (error) { + nbns_rq_done(rqp); + return (error); + } + rdrcount = NBNS_MAXREDIRECTS; + for (;;) { + error = nbns_rq(rqp); + if (error) + break; + if ((rqp->nr_rpnmflags & NBNS_NMFLAG_AA) == 0) { + /* + * Not an authoritative answer. Query again + * using the NS address in the 2nd record. + */ + if (rdrcount-- == 0) { + error = NBERROR(NBERR_TOOMANYREDIRECTS); + break; + } + error = nbns_rq_getrr(rqp, &rr); + if (error) + break; + error = nbns_rq_getrr(rqp, &rr); + if (error) + break; + bcopy(rr.rr_data, &rqp->nr_dest, 4); + continue; + } + if (rqp->nr_rpancount == 0) { + error = NBERROR(NBERR_HOSTNOTFOUND); + break; + } + error = nbns_rq_getrr(rqp, &rr); + if (error) + break; + len = sizeof (struct sockaddr_in); + dest = malloc(len); + if (dest == NULL) + return (ENOMEM); + bzero(dest, len); + /* + * Solaris sockaddr_in doesn't have this field. + * dest->sin_len = len; + */ + dest->sin_family = AF_INET; + bcopy(rr.rr_data + 2, &dest->sin_addr.s_addr, 4); + dest->sin_port = htons(SMB_TCP_PORT); + *adpp = (struct sockaddr *)dest; + ctx->nb_lastns = rqp->nr_sender; + break; + } + nbns_rq_done(rqp); + return (error); +} + +static char * +smb_optstrncpy(char *d, char *s, unsigned maxlen) +{ + if (d && s) { + strncpy(d, s, maxlen); + d[maxlen] = (char)0; + } + return (d); +} + + +int +nbns_getnodestatus(struct sockaddr *targethost, + struct nb_ctx *ctx, char *system, char *workgroup) +{ + struct nbns_rq *rqp; + struct nbns_rr rr; + struct nb_name nn; + struct nbns_nr *nrp; + char nrtype; + char *cp, *retname = NULL; + struct sockaddr_in *dest; + unsigned char nrcount; + int error, rdrcount, i, foundserver = 0, foundgroup = 0; + + if (targethost->sa_family != AF_INET) + return (EINVAL); + error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp); + if (error) + return (error); + bzero(&nn, sizeof (nn)); + strcpy((char *)nn.nn_name, "*"); + nn.nn_scope = ctx->nb_scope; + nn.nn_type = NBT_WKSTA; + rqp->nr_nmflags = 0; + rqp->nr_qdname = &nn; + rqp->nr_qdtype = NBNS_QUESTION_TYPE_NBSTAT; + rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN; + rqp->nr_qdcount = 1; + rqp->nr_maxretry = 2; + + /* LINTED */ + dest = (struct sockaddr_in *)targethost; + rqp->nr_dest = dest->sin_addr; + + error = nbns_rq_prepare(rqp); + if (error) { + nbns_rq_done(rqp); + return (error); + } + + /* + * Darwin had a loop here, allowing redirect, etc. + * but we only handle point-to-point for node status. + */ + error = nbns_rq(rqp); + if (error) + goto out; + if (rqp->nr_rpancount == 0) { + error = NBERROR(NBERR_HOSTNOTFOUND); + goto out; + } + error = nbns_rq_getrr(rqp, &rr); + if (error) + goto out; + + /* Compiler didn't like cast on lvalue++ */ + nrcount = *((unsigned char *)rr.rr_data); + rr.rr_data++; + /* LINTED */ + for (i = 1, nrp = (struct nbns_nr *)rr.rr_data; + i <= nrcount; ++i, ++nrp) { + nrtype = nrp->ns_name[NB_NAMELEN-1]; + /* Terminate the string: */ + nrp->ns_name[NB_NAMELEN-1] = (char)0; + /* Strip off trailing spaces */ + for (cp = &nrp->ns_name[NB_NAMELEN-2]; + cp >= nrp->ns_name; --cp) { + if (*cp != (char)0x20) + break; + *cp = (char)0; + } + nrp->ns_flags = ntohs(nrp->ns_flags); + if (nrp->ns_flags & NBNS_GROUPFLG) { + if (!foundgroup || + (foundgroup != NBT_WKSTA+1 && + nrtype == NBT_WKSTA)) { + smb_optstrncpy(workgroup, nrp->ns_name, + SMB_MAXUSERNAMELEN); + foundgroup = nrtype+1; + } + } else { + /* + * Track at least ONE name, in case + * no server name is found + */ + retname = nrp->ns_name; + } + if (nrtype == NBT_SERVER) { + smb_optstrncpy(system, nrp->ns_name, + SMB_MAXSRVNAMELEN); + foundserver = 1; + } + } + if (!foundserver) + smb_optstrncpy(system, retname, SMB_MAXSRVNAMELEN); + ctx->nb_lastns = rqp->nr_sender; + +out: + nbns_rq_done(rqp); + return (error); +} + +int +nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp) +{ + struct nbns_rq *rqp; + static uint16_t trnid; + int error; + + if (trnid == 0) + trnid = getpid(); + rqp = malloc(sizeof (*rqp)); + if (rqp == NULL) + return (ENOMEM); + bzero(rqp, sizeof (*rqp)); + error = mb_init(&rqp->nr_rq, NBDG_MAXSIZE); + if (error) { + free(rqp); + return (error); + } + rqp->nr_opcode = opcode; + rqp->nr_nbd = ctx; + rqp->nr_trnid = trnid++; + *rqpp = rqp; + return (0); +} + +void +nbns_rq_done(struct nbns_rq *rqp) +{ + if (rqp == NULL) + return; + if (rqp->nr_fd >= 0) + close(rqp->nr_fd); + mb_done(&rqp->nr_rq); + mb_done(&rqp->nr_rp); + if (rqp->nr_if) + free(rqp->nr_if); + free(rqp); +} + +/* + * Extract resource record from the packet. Assume that there is only + * one mbuf. + */ +int +nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp) +{ + struct mbdata *mbp = &rqp->nr_rp; + uchar_t *cp; + int error, len; + + bzero(rrp, sizeof (*rrp)); + cp = (uchar_t *)mbp->mb_pos; + len = nb_encname_len(cp); + if (len < 1) + return (NBERROR(NBERR_INVALIDRESPONSE)); + rrp->rr_name = cp; + error = mb_get_mem(mbp, NULL, len); + if (error) + return (error); + mb_get_uint16be(mbp, &rrp->rr_type); + mb_get_uint16be(mbp, &rrp->rr_class); + mb_get_uint32be(mbp, &rrp->rr_ttl); + mb_get_uint16be(mbp, &rrp->rr_rdlength); + rrp->rr_data = (uchar_t *)mbp->mb_pos; + error = mb_get_mem(mbp, NULL, rrp->rr_rdlength); + return (error); +} + +int +nbns_rq_prepare(struct nbns_rq *rqp) +{ + struct nb_ctx *ctx = rqp->nr_nbd; + struct mbdata *mbp = &rqp->nr_rq; + uint16_t ofr; /* opcode, flags, rcode */ + uchar_t *cp; + int len, error; + + /* + * Replacing with one argument. + * error = mb_init(&rqp->nr_rp, NBDG_MAXSIZE); + */ + error = mb_init(&rqp->nr_rp, NBDG_MAXSIZE); + if (error) + return (error); + + /* + * When looked into the ethereal trace, 'nmblookup' command sets this + * flag. We will also set. + */ + mb_put_uint16be(mbp, rqp->nr_trnid); + ofr = ((rqp->nr_opcode & 0x1F) << 11) | + ((rqp->nr_nmflags & 0x7F) << 4); /* rcode=0 */ + mb_put_uint16be(mbp, ofr); + mb_put_uint16be(mbp, rqp->nr_qdcount); + mb_put_uint16be(mbp, rqp->nr_ancount); + mb_put_uint16be(mbp, rqp->nr_nscount); + mb_put_uint16be(mbp, rqp->nr_arcount); + if (rqp->nr_qdcount) { + if (rqp->nr_qdcount > 1) + return (EINVAL); + len = nb_name_len(rqp->nr_qdname); + error = mb_fit(mbp, len, (char **)&cp); + if (error) + return (error); + nb_name_encode(rqp->nr_qdname, cp); + mb_put_uint16be(mbp, rqp->nr_qdtype); + mb_put_uint16be(mbp, rqp->nr_qdclass); + } + m_lineup(mbp->mb_top, &mbp->mb_top); + if (ctx->nb_timo == 0) + ctx->nb_timo = 1; /* by default 1 second */ + return (0); +} + +static int +nbns_rq_recv(struct nbns_rq *rqp) +{ + struct mbdata *mbp = &rqp->nr_rp; + void *rpdata = mtod(mbp->mb_top, void *); + fd_set rd, wr, ex; + struct timeval tv; + struct sockaddr_in sender; + int s = rqp->nr_fd; + int n, len; + + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + FD_SET(s, &rd); + + tv.tv_sec = rqp->nr_nbd->nb_timo; + tv.tv_usec = 0; + + n = select(s + 1, &rd, &wr, &ex, &tv); + if (n == -1) + return (-1); + if (n == 0) + return (ETIMEDOUT); + if (FD_ISSET(s, &rd) == 0) + return (ETIMEDOUT); + len = sizeof (sender); + n = recvfrom(s, rpdata, mbp->mb_top->m_maxlen, 0, + (struct sockaddr *)&sender, &len); + if (n < 0) + return (errno); + mbp->mb_top->m_len = mbp->mb_count = n; + rqp->nr_sender = sender; + return (0); +} + +static int +nbns_rq_opensocket(struct nbns_rq *rqp) +{ + struct sockaddr_in locaddr; + int opt = 1, s; + struct nb_ctx *ctx = rqp->nr_nbd; + + s = rqp->nr_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + return (errno); + if (ctx->nb_flags & NBCF_BC_ENABLE) { + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt, + sizeof (opt)) < 0) + return (errno); + } + if (is_system_labeled()) + (void) setsockopt(s, SOL_SOCKET, SO_MAC_EXEMPT, &opt, + sizeof (opt)); + bzero(&locaddr, sizeof (locaddr)); + locaddr.sin_family = AF_INET; + /* locaddr.sin_len = sizeof (locaddr); */ + if (bind(s, (struct sockaddr *)&locaddr, sizeof (locaddr)) < 0) + return (errno); + return (0); +} + +static int +nbns_rq_send(struct nbns_rq *rqp, in_addr_t ina) +{ + struct sockaddr_in dest; + struct mbdata *mbp = &rqp->nr_rq; + int s = rqp->nr_fd; + uint16_t ofr, ofr_save; /* opcode, nmflags, rcode */ + uint16_t *datap; + uint8_t nmflags; + int rc; + + bzero(&dest, sizeof (dest)); + dest.sin_family = AF_INET; + dest.sin_port = htons(NBNS_UDP_PORT); + dest.sin_addr.s_addr = ina; + + if (ina == INADDR_BROADCAST) { + /* Turn on the broadcast bit. */ + nmflags = rqp->nr_nmflags | NBNS_NMFLAG_BCAST; + /*LINTED*/ + datap = mtod(mbp->mb_top, uint16_t *); + ofr = ((rqp->nr_opcode & 0x1F) << 11) | + ((nmflags & 0x7F) << 4); /* rcode=0 */ + ofr_save = datap[1]; + datap[1] = htons(ofr); + } + + rc = sendto(s, mtod(mbp->mb_top, char *), mbp->mb_count, 0, + (struct sockaddr *)&dest, sizeof (dest)); + + if (ina == INADDR_BROADCAST) { + /* Turn the broadcast bit back off. */ + datap[1] = ofr_save; + } + + + if (rc < 0) + return (errno); + + return (0); +} + +int +nbns_rq(struct nbns_rq *rqp) +{ + struct nb_ctx *ctx = rqp->nr_nbd; + struct mbdata *mbp = &rqp->nr_rq; + uint16_t ofr, rpid; + uint8_t nmflags; + int error, tries, maxretry; + + error = nbns_rq_opensocket(rqp); + if (error) + return (error); + + maxretry = rqp->nr_maxretry; + for (tries = 0; tries < maxretry; tries++) { + + /* + * Minor hack: If nr_dest is set, send there only. + * Used by _getnodestatus, _resolvname redirects. + */ + if (rqp->nr_dest.s_addr) { + error = nbns_rq_send(rqp, rqp->nr_dest.s_addr); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "nbns error %d sending to %s"), + 0, error, inet_ntoa(rqp->nr_dest)); + } + goto do_recv; + } + + if (ctx->nb_wins1) { + error = nbns_rq_send(rqp, ctx->nb_wins1); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "nbns error %d sending to wins1"), + 0, error); + } + } + + if (ctx->nb_wins2 && (tries > 0)) { + error = nbns_rq_send(rqp, ctx->nb_wins2); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "nbns error %d sending to wins2"), + 0, error); + } + } + + /* + * If broadcast is enabled, start broadcasting + * only after wins servers fail to respond, or + * immediately if no WINS servers configured. + */ + if ((ctx->nb_flags & NBCF_BC_ENABLE) && + ((tries > 1) || (ctx->nb_wins1 == 0))) { + error = nbns_rq_send(rqp, INADDR_BROADCAST); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "nbns error %d sending broadcast"), + 0, error); + } + } + + /* + * Wait for responses from ANY of the above. + */ +do_recv: + error = nbns_rq_recv(rqp); + if (error == ETIMEDOUT) + continue; + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "nbns recv error %d"), + 0, error); + return (error); + } + + mbp = &rqp->nr_rp; + if (mbp->mb_count < 12) + return (NBERROR(NBERR_INVALIDRESPONSE)); + mb_get_uint16be(mbp, &rpid); + if (rpid != rqp->nr_trnid) + return (NBERROR(NBERR_INVALIDRESPONSE)); + break; + } + + mb_get_uint16be(mbp, &ofr); + rqp->nr_rpnmflags = (ofr >> 4) & 0x7F; + rqp->nr_rprcode = ofr & 0xf; + if (rqp->nr_rprcode) + return (NBERROR(rqp->nr_rprcode)); + mb_get_uint16be(mbp, &rpid); /* QDCOUNT */ + mb_get_uint16be(mbp, &rqp->nr_rpancount); + mb_get_uint16be(mbp, &rqp->nr_rpnscount); + mb_get_uint16be(mbp, &rqp->nr_rparcount); + return (0); +} diff --git a/usr/src/lib/libsmbfs/smb/netshareenum.c b/usr/src/lib/libsmbfs/smb/netshareenum.c new file mode 100644 index 0000000000..4da5fd17a2 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/netshareenum.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* BEGIN CSTYLED */ +/* + * @(#)ui.c * + * (c) 2004 Apple Computer, Inc. All Rights Reserved + * + * + * netshareenum.c -- Routines for getting a list of share information + * from a server. + * + * MODIFICATION HISTORY: + * 27-Nov-2004 Guy Harris New today + */ +/* END CSTYLED */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include <netsmb/mchain.h> +#include <netsmb/smb_lib.h> +#include <netsmb/smb_rap.h> +#include <netsmb/smb_netshareenum.h> +#include "charsets.h" + +#if 0 /* XXX see below */ +#include <dce/exc_handling.h> +#include <attrb.h> +#include "srvsvc.h" +#endif + +/* + * Don't want RPC client-side code in here. + * It's good code; just doesn't belong here. + * + * The API provided by this library should be + * just files and pipes (and not much more). + * It MAY be useful to provide some of the + * RAP (remote API) functions functions like + * rap_netshareenum below... + * + * XXX: Not sure this file belongs here at all. + * smb_rap.h looks like a reasonable API + * for this library to export. + */ +#if 0 /* XXX */ + +static int +rpc_netshareenum(struct smb_ctx *ctx, int *entriesp, int *totalp, + struct share_info **entries_listp) +{ + char ctx_string[2+16+1]; /* enough for 64-bit pointer, in hex */ + unsigned_char_p_t binding; + unsigned32 binding_status; + rpc_binding_handle_t binding_h; + int error, i, entries; + char *addrstr, *srvnamestr; + unsigned short *usrvnamestr; + unsigned32 level; + SHARE_ENUM_STRUCT share_info; + SHARE_INFO_1_CONTAINER share_info_1_container; + SHARE_INFO_1 *shares, *share; + unsigned32 total_entries; + unsigned32 status, free_status; + struct share_info *entry_list, *elp; + static EXCEPTION rpc_x_connect_rejected; + static int exceptions_initialized; + + sprintf(ctx_string, "%p", ctx); + rpc_string_binding_compose(NULL, "ncacn_np", ctx_string, + "srvsvc", NULL, &binding, &binding_status); + if (binding_status != rpc_s_ok) { + smb_error(dgettext(TEXT_DOMAIN, + "rpc_string_binding_compose failed with %d"), + 0, binding_status); + return (EINVAL); + } + rpc_binding_from_string_binding(binding, &binding_h, &status); + if (binding_status != rpc_s_ok) { + smb_error(dgettext(TEXT_DOMAIN, + "rpc_binding_from_string_binding failed with %d"), 0, + binding_status); + return (EINVAL); + } + level = 1; + share_info.share_union.level = 1; + share_info.share_union.tagged_union.share1 = &share_info_1_container; + share_info_1_container.share_count = 0; + share_info_1_container.shares = NULL; + /* + * Convert the server IP address to a string, and send that as + * the "server name" - that's what Windows appears to do, and + * that avoids problems with NetBIOS names containing + * non-ASCII characters. + */ + addrstr = inet_ntoa(ctx->ct_srvinaddr.sin_addr); + srvnamestr = malloc(strlen(addrstr) + 3); + if (srvnamestr == NULL) { + status = errno; + smb_error(dgettext(TEXT_DOMAIN, + "can't allocate string for server address"), status); + rpc_binding_free(&binding_h, &free_status); + return (status); + } + strcpy(srvnamestr, "\\\\"); + strcat(srvnamestr, addrstr); +#ifdef NOTYETDEFINED + usrvnamestr = convert_utf8_to_leunicode(srvnamestr); +#endif + usrvnamestr = srvnamestr; + if (usrvnamestr == NULL) { + smb_error(dgettext(TEXT_DOMAIN, + "can't convert string for server address to Unicode"), 0); + rpc_binding_free(&binding_h, &free_status); + return (EINVAL); + } + if (!exceptions_initialized) { + EXCEPTION_INIT(rpc_x_connect_rejected); + exc_set_status(&rpc_x_connect_rejected, rpc_s_connect_rejected); + exceptions_initialized = 1; + } + /* printf("Calling NetrShareEnum.."); XXX */ + TRY + status = NetrShareEnum(binding_h, usrvnamestr, &level, + &share_info, 4294967295U, &total_entries, NULL); + if (status != 0) + smb_error(dgettext(TEXT_DOMAIN, + "error from NetrShareEnum call: status = 0x%08x"), + 0, status); + /*CSTYLED*/ + CATCH (rpc_x_connect_rejected) + /* + * This is what we get if we can't open the pipe. + * That's a normal occurrence when we're talking + * to a system that (presumably) doesn't support + * DCE RPC on the server side, such as Windows 95/98/Me, + * so we don't log an error. + */ + /*CSTYLED*/ + status = ENOTSUP; + CATCH_ALL + /* + * XXX - should we handle some exceptions differently, + * returning different errors, and try RAP only for + * ENOTSUP? + */ + smb_error(dgettext(TEXT_DOMAIN, + "error from NetrShareEnum call: exception = %u"), + 0, THIS_CATCH->match.value); + status = ENOTSUP; + ENDTRY + rpc_binding_free(&binding_h, &free_status); + free(srvnamestr); + free(usrvnamestr); + if (status != 0) + return (ENOTSUP); + + /* + * XXX - if the IDL is correct, it's not clear whether the + * unmarshalling code will properly handle the case where + * a packet where "share_count" and the max count for the + * array of shares don't match; a valid DCE RPC implementation + * won't marshal something like that, but there's no guarantee + * that the server we're talking to has a valid implementation + * (which could be a *malicious* implementation!). + */ + entries = share_info.share_union.tagged_union.share1->share_count; + shares = share_info.share_union.tagged_union.share1->shares; + entry_list = calloc(entries, sizeof (struct share_info)); + if (entry_list == NULL) { + error = errno; + goto cleanup_and_return; + } + for (share = shares, elp = entry_list, i = 0; i < entries; + i++, share++) { + elp->type = share->shi1_type; +#ifdef NOTYETDEFINED + elp->netname = convert_unicode_to_utf8(share->shi1_share); +#endif + elp->netname = share->shi1_share; + if (elp->netname == NULL) + goto fail; +#ifdef NOTYETDEFINED + elp->remark = convert_unicode_to_utf8(share->shi1_remark); +#endif + elp->remark = share->shi1_remark; + if (elp->remark == NULL) + goto fail; + elp++; + } + *entriesp = entries; + *totalp = total_entries; + *entries_listp = entry_list; + error = 0; + goto cleanup_and_return; + +fail: + error = errno; + for (elp = entry_list, i = 0; i < entries; i++, elp++) { + /* + * elp->netname is set before elp->remark, so if + * elp->netname is null, elp->remark is also null. + * If either of them is null, we haven't done anything + * to any entries after this one. + */ + if (elp->netname == NULL) + break; + free(elp->netname); + if (elp->remark == NULL) + break; + free(elp->remark); + } + free(entry_list); + +cleanup_and_return: + for (share = shares, i = 0; i < entries; i++, share++) { + free(share->shi1_share); + free(share->shi1_remark); + } + free(shares); + /* + * XXX - "share1" should be a unique pointer, but we haven't + * changed the marshalling code to support non-full pointers + * in unions, so we leave it as a full pointer. + * + * That means that this might, or might not, be changed from + * pointing to "share_info_1_container" to pointing to a + * mallocated structure, according to the DCE RPC 1.1 IDL spec; + * we free it only if it's changed. + */ + if (share_info.share_union.tagged_union.share1 != + &share_info_1_container) + free(share_info.share_union.tagged_union.share1); + return (error); +} +#endif /* XXX */ + +static int +rap_netshareenum(struct smb_ctx *ctx, int *entriesp, int *totalp, + struct share_info **entries_listp) +{ + int error, bufsize, i, entries, total, nreturned; + struct smb_share_info_1 *rpbuf, *ep; + struct share_info *entry_list, *elp; + char *cp; + int lbound, rbound; + + bufsize = 0xffe0; /* samba notes win2k bug for 65535 */ + rpbuf = malloc(bufsize); + if (rpbuf == NULL) + return (errno); + + error = smb_rap_NetShareEnum(ctx, 1, rpbuf, &bufsize, &entries, &total); + if (error && + error != (SMB_ERROR_MORE_DATA | SMB_RAP_ERROR)) { + free(rpbuf); + return (error); + } + entry_list = malloc(entries * sizeof (struct share_info)); + if (entry_list == NULL) { + error = errno; + free(rpbuf); + return (error); + } + lbound = entries * (sizeof (struct smb_share_info_1)); + rbound = bufsize; + for (ep = rpbuf, elp = entry_list, i = 0, nreturned = 0; i < entries; + i++, ep++) { + elp->type = letohs(ep->shi1_type); + ep->shi1_pad = '\0'; /* ensure null termination */ + elp->netname = strdup(ep->shi1_netname); +#ifdef NOTYETDEFINED + elp->netname = convert_wincs_to_utf8(ep->shi1_netname); +#endif + if (elp->netname == NULL) + continue; /* punt on this entry */ + /* + * Check for validity of offset. + */ + if (ep->shi1_remark >= lbound && ep->shi1_remark < rbound) { + cp = (char *)rpbuf + ep->shi1_remark; + elp->remark = cp; +#ifdef NOTYETDEFINED + elp->remark = nls_str_toloc(cp, cp); +#endif + } else + elp->remark = NULL; + elp++; + nreturned++; + } + *entriesp = nreturned; + *totalp = total; + *entries_listp = entry_list; + free(rpbuf); + return (0); +} + +/* + * First we try the RPC-based NetrShareEnum, and, if that fails, we fall + * back on the RAP-based NetShareEnum. + */ +int +smb_netshareenum(struct smb_ctx *ctx, int *entriesp, int *totalp, + struct share_info **entry_listp) +{ + int error; + +#ifdef NOTYETDEFINED + /* + * Try getting a list of shares with the SRVSVC RPC service. + */ + error = rpc_netshareenum(ctx, entriesp, totalp, entry_listp); + if (error == 0) + return (0); +#endif + + /* + * OK, that didn't work - try RAP. + * XXX - do so only if it failed because we couldn't open + * the pipe? + */ + return (rap_netshareenum(ctx, entriesp, totalp, entry_listp)); +} diff --git a/usr/src/lib/libsmbfs/smb/nls.c b/usr/src/lib/libsmbfs/smb/nls.c new file mode 100644 index 0000000000..03fe1bec13 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/nls.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: nls.c,v 1.10 2004/12/13 00:25:22 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <stdlib.h> +#include <locale.h> +#include <errno.h> + +#include <netsmb/smb_lib.h> + +/* + * prototype iconv* functions + */ +typedef void *iconv_t; + +static size_t(*my_iconv)(iconv_t, const char **, size_t *, char **, size_t *); + +u_char nls_lower[256]; +u_char nls_upper[256]; + +static iconv_t nls_toext, nls_toloc; +static int iconv_loaded; + +int +nls_setlocale(const char *name) +{ + int i; + + if (setlocale(LC_CTYPE, name) == NULL) { + fprintf(stdout, dgettext(TEXT_DOMAIN, + "can't set locale '%s'\n"), name); + } + for (i = 0; i < 256; i++) { + nls_lower[i] = tolower(i); + nls_upper[i] = toupper(i); + } + return 0; +} + +int +nls_setrecode(const char *local, const char *external) +{ + return ENOENT; +} + +char * +nls_str_toloc(char *dst, const char *src) +{ + char *p = dst; + size_t inlen, outlen; + + if (!iconv_loaded) + return strcpy(dst, src); + + if (nls_toloc == (iconv_t)0) + return strcpy(dst, src); + inlen = outlen = strlen(src); + my_iconv(nls_toloc, NULL, NULL, &p, &outlen); + my_iconv(nls_toloc, &src, &inlen, &p, &outlen); + *p = 0; + return dst; +} + +char * +nls_str_toext(char *dst, const char *src) +{ + char *p = dst; + size_t inlen, outlen; + + if (!iconv_loaded) + return strcpy(dst, src); + + if (nls_toext == (iconv_t)0) + return strcpy(dst, src); + inlen = outlen = strlen(src); + my_iconv(nls_toext, NULL, NULL, &p, &outlen); + my_iconv(nls_toext, &src, &inlen, &p, &outlen); + *p = 0; + return dst; +} + +void * +nls_mem_toloc(void *dst, const void *src, int size) +{ + char *p = dst; + const char *s = src; + size_t inlen, outlen; + + if (!iconv_loaded) + return memcpy(dst, src, size); + + if (size == 0) + return NULL; + + if (nls_toloc == (iconv_t)0) + return memcpy(dst, src, size); + inlen = outlen = size; + my_iconv(nls_toloc, NULL, NULL, &p, &outlen); + my_iconv(nls_toloc, &s, &inlen, &p, &outlen); + return dst; +} + +void * +nls_mem_toext(void *dst, const void *src, int size) +{ + char *p = dst; + const char *s = src; + size_t inlen, outlen; + + if (size == 0) + return NULL; + + if (!iconv_loaded || nls_toext == (iconv_t)0) + return memcpy(dst, src, size); + + inlen = outlen = size; + my_iconv(nls_toext, NULL, NULL, &p, &outlen); + my_iconv(nls_toext, &s, &inlen, &p, &outlen); + return dst; +} + +char * +nls_str_upper(char *dst, const char *src) +{ + char *p = dst; + + while (*src) + *dst++ = toupper(*src++); + *dst = 0; + return p; +} + +char * +nls_str_lower(char *dst, const char *src) +{ + char *p = dst; + + while (*src) + *dst++ = tolower(*src++); + *dst = 0; + return p; +} diff --git a/usr/src/lib/libsmbfs/smb/print.c b/usr/src/lib/libsmbfs/smb/print.c new file mode 100644 index 0000000000..7b87c06e41 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/print.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: print.c,v 1.1.1.3 2001/07/06 22:38:43 conrad Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/mount.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <pwd.h> +#include <grp.h> +#include <unistd.h> + +#include <netsmb/smb_lib.h> +#include <cflib.h> + +int +smb_smb_open_print_file(struct smb_ctx *ctx, int setuplen, int mode, + const char *ident, smbfh *fhp) +{ + struct smb_rq *rqp; + struct mbdata *mbp; + int error; + + error = smb_rq_init(ctx, SMB_COM_OPEN_PRINT_FILE, 2, &rqp); + if (error) + return (error); + mbp = smb_rq_getrequest(rqp); + mb_put_uint16le(mbp, setuplen); + mb_put_uint16le(mbp, mode); + smb_rq_wend(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + smb_rq_dstring(mbp, ident); + error = smb_rq_simple(rqp); + if (!error) { + mbp = smb_rq_getreply(rqp); + mb_get_uint16(mbp, fhp); + } + smb_rq_done(rqp); + return (error); +} + +int +smb_smb_close_print_file(struct smb_ctx *ctx, smbfh fh) +{ + struct smb_rq *rqp; + struct mbdata *mbp; + int error; + + error = smb_rq_init(ctx, SMB_COM_CLOSE_PRINT_FILE, 0, &rqp); + if (error) + return (error); + mbp = smb_rq_getrequest(rqp); + mb_put_mem(mbp, (char *)&fh, 2); + smb_rq_wend(rqp); + error = smb_rq_simple(rqp); + smb_rq_done(rqp); + return (error); +} diff --git a/usr/src/lib/libsmbfs/smb/queue.h b/usr/src/lib/libsmbfs/smb/queue.h new file mode 100644 index 0000000000..6c99fb946d --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/queue.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file [used to define] five types of data structures: + * singly-linked lists, ... + * [ all other types of lists removed ] + * + * Using excerpts of FreeBSD 4.5 sys/queue.h here, + * but only temporarily, until rcfile.c is replaced + * by SMF integration code. + * + * Yes we also have queue.h in uts/common/fs/smbclnt + * but don't want to make that part of the exported + * interface to the user-level code. + */ + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_NEXT(curelm, field) = \ + SLIST_NEXT(SLIST_NEXT(curelm, field), field); \ + } \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + diff --git a/usr/src/lib/libsmbfs/smb/rap.c b/usr/src/lib/libsmbfs/smb/rap.c new file mode 100644 index 0000000000..00ccbd54a2 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/rap.c @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: rap.c,v 1.5 2004/12/13 00:25:23 lindak Exp $ + * + * This is very simple implementation of RAP protocol. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/stat.h> +#include <sys/isa_defs.h> + +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> +#include <strings.h> +#include <stdlib.h> +#include <libintl.h> +#include <sysexits.h> + +#include <netsmb/mchain.h> +#include <netsmb/smb_lib.h> +#include <netsmb/smb_rap.h> + +static int +smb_rap_parserqparam(const char *s, char **next, int *rlen) +{ + char *np; + int len; + + switch (*s++) { + case 'L': + case 'T': + case 'W': + len = 2; + break; + case 'D': + case 'O': + len = 4; + break; + case 'b': + case 'F': + len = 1; + break; + case 'r': + case 's': + len = 0; + break; + default: + return (EINVAL); + } + if (isdigit(*s)) { + len *= strtoul(s, &np, 10); + s = np; + } + *rlen = len; + *(const char **)next = s; + return (0); +} + +static int +smb_rap_parserpparam(const char *s, char **next, int *rlen) +{ + char *np; + int len = 0; + + switch (*s++) { + case 'e': + case 'h': + len = 2; + break; + case 'i': + len = 4; + break; + case 'g': + len = 1; + break; + default: + return (EINVAL); + } + if (isdigit(*s)) { + len *= strtoul(s, &np, 10); + s = np; + } + *rlen = len; + *(const char **)next = s; + return (0); +} + +static int +smb_rap_parserpdata(const char *s, char **next, int *rlen) +{ + char *np; + int len; + + switch (*s++) { + case 'B': + len = 1; + break; + case 'W': + len = 2; + break; + case 'D': + case 'O': + case 'z': + len = 4; + break; + default: + return (EINVAL); + } + if (isdigit(*s)) { + len *= strtoul(s, &np, 10); + s = np; + } + *rlen = len; + *(const char **)next = s; + return (0); +} + +static int +smb_rap_rqparam_z(struct smb_rap *rap, const char *value) +{ + int len = strlen(value) + 1; + + bcopy(value, rap->r_npbuf, len); + rap->r_npbuf += len; + rap->r_plen += len; + return (0); +} + +/* + * Marshal RAP request parameters. + * Note: value is in host order. + */ +static int +smb_rap_rqparam(struct smb_rap *rap, char ptype, char plen, int value) +{ + char *p = rap->r_npbuf; + int len = 0; + uint_t uv = (uint_t)value; + + switch (ptype) { + case 'L': + case 'W': + /* LINTED */ + setwle(p, 0, uv); + len = 2; + break; + case 'D': + /* LINTED */ + setdle(p, 0, uv); + len = 4; + break; + case 'b': + memset(p, uv, plen); + len = plen; + default: + return (EINVAL); + } + rap->r_npbuf += len; + rap->r_plen += len; + return (0); +} + +int +smb_rap_create(int fn, const char *param, const char *data, + struct smb_rap **rapp) +{ + struct smb_rap *rap; + char *p; + int plen = 0, len = 0; + int i; + + rap = malloc(sizeof (*rap)); + if (rap == NULL) + return (ENOMEM); + bzero(rap, sizeof (*rap)); + p = rap->r_sparam = rap->r_nparam = strdup(param); + rap->r_sdata = rap->r_ndata = strdup(data); + + /* + * Calculate length of request parameter block + */ + len = 2 + strlen(param) + 1 + strlen(data) + 1; + while (*p) { + if (smb_rap_parserqparam(p, &p, &plen) != 0) + break; + len += plen; + } + rap->r_pbuf = rap->r_npbuf = malloc(len); + smb_rap_rqparam(rap, 'W', 1, fn); + smb_rap_rqparam_z(rap, rap->r_sparam); + smb_rap_rqparam_z(rap, rap->r_sdata); + *rapp = rap; + return (0); +} + +void +smb_rap_done(struct smb_rap *rap) +{ + if (rap->r_sparam) + free(rap->r_sparam); + if (rap->r_sdata) + free(rap->r_sdata); + if (rap->r_pbuf) + free(rap->r_pbuf); +#ifdef NOTYETDEFINED + if (rap->r_npbuf) + free(rap->r_npbuf); + if (rap->r_dbuf) + free(rap->r_dbuf); + if (rap->r_rcvbuf) + free(rap->r_rcvbuf); +#endif + free(rap); +} + +int +smb_rap_setNparam(struct smb_rap *rap, int value) +{ + char *p = rap->r_nparam; + char ptype = *p; + int error, plen; + + error = smb_rap_parserqparam(p, &p, &plen); + if (error) + return (error); + switch (ptype) { + case 'L': + rap->r_rcvbuflen = value; + /* FALLTHROUGH */ + case 'W': + case 'D': + case 'b': + error = smb_rap_rqparam(rap, ptype, plen, value); + break; + default: + return (EINVAL); + } + rap->r_nparam = p; + return (0); +} + +int +smb_rap_setPparam(struct smb_rap *rap, void *value) +{ + char *p = rap->r_nparam; + char ptype = *p; + int error, plen; + + error = smb_rap_parserqparam(p, &p, &plen); + if (error) + return (error); + switch (ptype) { + case 'r': + rap->r_rcvbuf = value; + break; + default: + return (EINVAL); + } + rap->r_nparam = p; + return (0); +} + +static int +smb_rap_getNparam(struct smb_rap *rap, long *value) +{ + char *p = rap->r_nparam; + char ptype = *p; + int error, plen; + uint16_t *te; + + error = smb_rap_parserpparam(p, &p, &plen); + if (error) + return (error); + switch (ptype) { + case 'h': + /* LINTED */ + te = (uint16_t *)rap->r_npbuf; + *value = letohs(*te); + break; + default: + return (EINVAL); + } + rap->r_npbuf += plen; + rap->r_nparam = p; + return (0); +} + +int +smb_rap_request(struct smb_rap *rap, struct smb_ctx *ctx) +{ + uint16_t *rp, conv, *tmp; + uint32_t *p32, ps1; + char *dp, *p = rap->r_nparam; + char ptype; + int error, rdatacnt, rparamcnt, entries, done, dlen, buffer_oflow, i; + + rdatacnt = rap->r_rcvbuflen; + rparamcnt = rap->r_plen; + error = smb_t2_request(ctx, 0, NULL, "\\PIPE\\LANMAN", + rap->r_plen, rap->r_pbuf, /* int tparamcnt,void *tparam */ + 0, NULL, /* int tdatacnt, void *tdata */ + &rparamcnt, rap->r_pbuf, /* rparamcnt, void *rparam */ + &rdatacnt, rap->r_rcvbuf, /* int *rdatacnt, void *rdata */ + &buffer_oflow); + if (error) + return (error); + + /* LINTED */ + rp = (uint16_t *)rap->r_pbuf; + + /* + * Note: First is a "LanMan API" error code. + * See: usr/src/uts/common/smbsrv/lmerr.h + */ + if (rparamcnt < 2) + return (EBADRPC); + rap->r_result = letohs(*rp); + rp++; rparamcnt -= 2; + + if (rap->r_result != 0) { + /* + * Could also return zero and let the caller + * come get r_result via smb_rap_error(), + * but in case they dont... + */ + return (rap->r_result | SMB_RAP_ERROR); + } + + if (rparamcnt < 2) + return (EBADRPC); + conv = letohs(*rp); + rp++; rparamcnt -= 2; + + rap->r_npbuf = (char *)rp; + rap->r_entries = entries = 0; + /* Save the returned data length */ + rap->r_rcvbuflen = rdatacnt; + done = 0; + + while (!done && *p) { + ptype = *p; + switch (ptype) { + case 'e': + if (rparamcnt < 2) + return (EBADRPC); + /* LINTED */ + tmp = (uint16_t *)rap->r_npbuf; + rap->r_entries = entries = letohs(*tmp); + rap->r_npbuf += 2; + rparamcnt -= 2; + p++; + break; + default: + done = 1; + } +#if 0 /* commented out in Darwin. Why? */ + error = smb_rap_parserpparam(p, &p, &plen); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "reply parameter mismatch %s"), 0, p); + return (EBADRPC); + } +#endif + } + rap->r_nparam = p; + /* + * In general, unpacking entries we may need to relocate + * entries for proper aligning. For now use them as is. + */ + dp = rap->r_rcvbuf; + while (entries--) { + p = rap->r_sdata; + while (*p) { + ptype = *p; + error = smb_rap_parserpdata(p, &p, &dlen); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "reply data mismatch %s"), 0, p); + return (EBADRPC); + } + if (rdatacnt < dlen) + return (EBADRPC); + switch (ptype) { + case 'z': + /* LINTED */ + p32 = (uint32_t *)dp; + *p32 = (letohl(*p32) & 0xffff) - conv; + break; + } + dp += dlen; + rdatacnt -= dlen; + } + } + return (error); +} + +int +smb_rap_error(struct smb_rap *rap, int error) +{ + if (error) + return (error); + if (rap->r_result == 0) + return (0); + return (rap->r_result | SMB_RAP_ERROR); +} + +/* todo: move this function to libnetapi */ +int +smb_rap_NetShareEnum(struct smb_ctx *ctx, int sLevel, void *pbBuffer, + int *cbBuffer, int *pcEntriesRead, int *pcTotalAvail) +{ + struct smb_rap *rap; + long lval = -1; + int error; + char *pass; + int i; + + error = smb_rap_create(0, "WrLeh", "B13BWz", &rap); + if (error) + return (error); + smb_rap_setNparam(rap, sLevel); /* W - sLevel */ + smb_rap_setPparam(rap, pbBuffer); /* r - pbBuffer */ + smb_rap_setNparam(rap, *cbBuffer); /* L - cbBuffer */ + error = smb_rap_request(rap, ctx); + if (error == 0) { + *pcEntriesRead = rap->r_entries; + error = smb_rap_getNparam(rap, &lval); + *pcTotalAvail = lval; + /* Copy the data length into the IN/OUT variable. */ + *cbBuffer = rap->r_rcvbuflen; + } + error = smb_rap_error(rap, error); + smb_rap_done(rap); + return (error); +} diff --git a/usr/src/lib/libsmbfs/smb/rcfile.c b/usr/src/lib/libsmbfs/smb/rcfile.c new file mode 100644 index 0000000000..498b91c0d8 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/rcfile.c @@ -0,0 +1,644 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: rcfile.c,v 1.1.1.2 2001/07/06 22:38:43 conrad Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/stat.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#include <libintl.h> +#include <pwd.h> +#include <unistd.h> +#include <sys/debug.h> + +#include <cflib.h> +#include "rcfile_priv.h" + +SLIST_HEAD(rcfile_head, rcfile); +static struct rcfile_head pf_head = {NULL}; + +static struct rcfile *rc_cachelookup(const char *filename); +struct rcsection *rc_findsect(struct rcfile *rcp, const char *sectname); +static struct rcsection *rc_addsect(struct rcfile *rcp, const char *sectname); +static int rc_freesect(struct rcfile *rcp, struct rcsection *rsp); +struct rckey *rc_sect_findkey(struct rcsection *rsp, const char *keyname); +static struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name, + const char *value); +static void rc_key_free(struct rckey *p); +static void rc_parse(struct rcfile *rcp); + +int insecure_nsmbrc; + +/* + * open rcfile and load its content, if already open - return previous handle + */ +int +rc_open(const char *filename, const char *mode, struct rcfile **rcfile) +{ + struct rcfile *rcp; + FILE *f; + struct stat statbuf; + + rcp = rc_cachelookup(filename); + if (rcp) { + *rcfile = rcp; + return (0); + } + f = fopen(filename, mode); + if (f == NULL) + return (errno); + insecure_nsmbrc = 0; + if (fstat(fileno(f), &statbuf) >= 0 && + (statbuf.st_mode & 077) != 0) + insecure_nsmbrc = 1; + rcp = malloc(sizeof (struct rcfile)); + if (rcp == NULL) { + fclose(f); + return (ENOMEM); + } + bzero(rcp, sizeof (struct rcfile)); + rcp->rf_name = strdup(filename); + rcp->rf_f = f; + SLIST_INSERT_HEAD(&pf_head, rcp, rf_next); + rc_parse(rcp); + *rcfile = rcp; + return (0); +} + +int +rc_merge(const char *filename, struct rcfile **rcfile) +{ + struct rcfile *rcp = *rcfile; + FILE *f, *t; + + insecure_nsmbrc = 0; + if (rcp == NULL) { + return (rc_open(filename, "r", rcfile)); + } + f = fopen(filename, "r"); + if (f == NULL) + return (errno); + t = rcp->rf_f; + rcp->rf_f = f; + rc_parse(rcp); + rcp->rf_f = t; + fclose(f); + return (0); +} + +int +rc_merge_pipe(const char *command, struct rcfile **rcfile) +{ + struct rcfile *rcp = *rcfile; + FILE *f, *t; + + insecure_nsmbrc = 0; + f = popen(command, "r"); + if (f == NULL) + return (errno); + if (rcp == NULL) { + rcp = malloc(sizeof (struct rcfile)); + if (rcp == NULL) { + fclose(f); + return (ENOMEM); + } + *rcfile = rcp; + bzero(rcp, sizeof (struct rcfile)); + rcp->rf_name = strdup(command); + rcp->rf_f = f; + SLIST_INSERT_HEAD(&pf_head, rcp, rf_next); + rc_parse(rcp); + } else { + t = rcp->rf_f; + rcp->rf_f = f; + rc_parse(rcp); + rcp->rf_f = t; + } + fclose(f); + return (0); +} + +int +rc_close(struct rcfile *rcp) +{ + struct rcsection *p, *n; + + fclose(rcp->rf_f); + for (p = SLIST_FIRST(&rcp->rf_sect); p; ) { + n = p; + p = SLIST_NEXT(p, rs_next); + rc_freesect(rcp, n); + } + free(rcp->rf_name); + SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next); + free(rcp); + return (0); +} + +static struct rcfile * +rc_cachelookup(const char *filename) +{ + struct rcfile *p; + + SLIST_FOREACH(p, &pf_head, rf_next) + if (strcmp(filename, p->rf_name) == 0) + return (p); + return (0); +} + +/* static */ struct rcsection * +rc_findsect(struct rcfile *rcp, const char *sectname) +{ + struct rcsection *p; + + SLIST_FOREACH(p, &rcp->rf_sect, rs_next) + if (strcasecmp(p->rs_name, sectname) == 0) + return (p); + return (NULL); +} + +static struct rcsection * +rc_addsect(struct rcfile *rcp, const char *sectname) +{ + struct rcsection *p; + + p = rc_findsect(rcp, sectname); + if (p) + return (p); + p = malloc(sizeof (*p)); + if (!p) + return (NULL); + p->rs_name = strdup(sectname); + SLIST_INIT(&p->rs_keys); + SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next); + return (p); +} + +static int +rc_freesect(struct rcfile *rcp, struct rcsection *rsp) +{ + struct rckey *p, *n; + + SLIST_REMOVE(&rcp->rf_sect, rsp, rcsection, rs_next); + for (p = SLIST_FIRST(&rsp->rs_keys); p; ) { + n = p; + p = SLIST_NEXT(p, rk_next); + rc_key_free(n); + } + free(rsp->rs_name); + free(rsp); + return (0); +} + +/* static */ struct rckey * +rc_sect_findkey(struct rcsection *rsp, const char *keyname) +{ + struct rckey *p; + + SLIST_FOREACH(p, &rsp->rs_keys, rk_next) + if (strcmp(p->rk_name, keyname) == 0) + return (p); + return (NULL); +} + +static struct rckey * +rc_sect_addkey(struct rcsection *rsp, const char *name, const char *value) +{ + struct rckey *p; + + p = rc_sect_findkey(rsp, name); + if (!p) { + p = malloc(sizeof (*p)); + if (!p) + return (NULL); + SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next); + p->rk_name = strdup(name); + p->rk_value = value ? strdup(value) : strdup(""); + } + return (p); +} + +#if 0 +void +rc_sect_delkey(struct rcsection *rsp, struct rckey *p) +{ + + SLIST_REMOVE(&rsp->rs_keys, p, rckey, rk_next); + rc_key_free(p); +} +#endif + +static void +rc_key_free(struct rckey *p) +{ + free(p->rk_value); + free(p->rk_name); + free(p); +} + +enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue}; + +int home_nsmbrc = 0; + +static char *minauth[] = { + "kerberos", + "ntlmv2", + "ntlm", + "lm", + "none", + NULL +}; + +static int +eval_minauth(char *auth) +{ + int i; + + for (i = 0; minauth[i]; i++) + if (strcmp(auth, minauth[i]) == 0) + break; + return (i); +} + +/* + * Ensure that "minauth" is set to the highest level (lowest array offset) + */ +static void +set_value(struct rcfile *rcp, struct rcsection *rsp, struct rckey *rkp, + char *ptr) +{ + int now, new; + + if (strcmp(rkp->rk_name, "minauth") == 0) { + now = eval_minauth(rkp->rk_value); + new = eval_minauth(ptr); + if (new >= now) { +#ifdef DEBUG + printf("set_value: rejecting %s=%s from %s\n", + rkp->rk_name, ptr, home_nsmbrc ? "user file" : "SMF"); +#endif + return; + } + } +#ifdef DEBUG + printf("set_value: applying %s=%s from %s\n", + rkp->rk_name, ptr, home_nsmbrc ? "user file" : "SMF"); +#endif + rkp->rk_value = strdup(ptr); +} + +static void +rc_parse(struct rcfile *rcp) +{ + FILE *f = rcp->rf_f; + int state = stNewLine, c; + struct rcsection *rsp = NULL; + struct rckey *rkp = NULL; + char buf[2048]; + char *next = buf, *last = &buf[sizeof (buf)-1]; + + while ((c = getc(f)) != EOF) { + if (c == '\r') + continue; + if (state == stNewLine) { + next = buf; + if (isspace(c)) + continue; /* skip leading junk */ + if (c == '[') { + state = stHeader; + rsp = NULL; + continue; + } + if (c == '#' || c == ';') { + state = stSkipToEOL; + } else { /* something meaningfull */ + state = stGetKey; + } + } + /* ignore long lines */ + if (state == stSkipToEOL || next == last) { + if (c == '\n') { + state = stNewLine; + next = buf; + } + continue; + } + if (state == stHeader) { + if (c == ']') { + *next = 0; + next = buf; + rsp = rc_addsect(rcp, buf); + state = stSkipToEOL; + } else + *next++ = c; + continue; + } + if (state == stGetKey) { + /* side effect: 'key name=' */ + if (c == ' ' || c == '\t') + continue; /* become 'keyname=' */ + if (c == '\n') { /* silently ignore ... */ + state = stNewLine; + continue; + } + if (c != '=') { + *next++ = c; + continue; + } + *next = 0; + if (rsp == NULL) { + fprintf(stderr, dgettext(TEXT_DOMAIN, + "Key '%s' defined before section\n"), buf); + state = stSkipToEOL; + continue; + } + if (home_nsmbrc && + (strcmp(buf, "nbns") == 0 || + strcmp(buf, "nbns_enable") == 0 || + strcmp(buf, "nbns_broadcast") == 0)) { + fprintf(stderr, dgettext(TEXT_DOMAIN, + "option %s may not be set " + "in user .nsmbrc file\n"), buf); + next = buf; + state = stNewLine; + continue; + } + if (insecure_nsmbrc && (strcmp(buf, "password") == 0)) { + fprintf(stderr, dgettext(TEXT_DOMAIN, + "Warning: .nsmbrc file not secure, " + "ignoring passwords\n")); + next = buf; + state = stNewLine; + continue; + } + rkp = rc_sect_addkey(rsp, buf, NULL); + next = buf; + state = stGetValue; + continue; + } + /* only stGetValue left */ + if (state != stGetValue) { + fprintf(stderr, dgettext(TEXT_DOMAIN, + "Well, I can't parse file '%s'\n"), rcp->rf_name); + state = stSkipToEOL; + } + if (c != '\n') { + *next++ = c; + continue; + } + *next = 0; + set_value(rcp, rsp, rkp, buf); + state = stNewLine; + rkp = NULL; + } /* while */ + if (c == EOF && state == stGetValue) { + *next = 0; + set_value(rcp, rsp, rkp, buf); + } +} + +int +rc_getstringptr(struct rcfile *rcp, const char *section, const char *key, + char **dest) +{ + struct rcsection *rsp; + struct rckey *rkp; + + *dest = NULL; + rsp = rc_findsect(rcp, section); + if (!rsp) + return (ENOENT); + rkp = rc_sect_findkey(rsp, key); + if (!rkp) + return (ENOENT); + *dest = rkp->rk_value; + return (0); +} + +int +rc_getstring(struct rcfile *rcp, const char *section, const char *key, + size_t maxlen, char *dest) +{ + char *value; + int error; + + error = rc_getstringptr(rcp, section, key, &value); + if (error) + return (error); + if (strlen(value) >= maxlen) { + fprintf(stdout, dgettext(TEXT_DOMAIN, + "line too long for key '%s' in section '%s', max = %d\n"), + key, section, maxlen); + return (EINVAL); + } + strcpy(dest, value); + return (0); +} + +int +rc_getint(struct rcfile *rcp, const char *section, const char *key, int *value) +{ + struct rcsection *rsp; + struct rckey *rkp; + + rsp = rc_findsect(rcp, section); + if (!rsp) + return (ENOENT); + rkp = rc_sect_findkey(rsp, key); + if (!rkp) + return (ENOENT); + errno = 0; + *value = strtol(rkp->rk_value, NULL, 0); + if (errno) { + fprintf(stdout, dgettext(TEXT_DOMAIN, + "invalid int value '%s' for key '%s' in section '%s'\n"), + rkp->rk_value, key, section); + return (errno); + } + return (0); +} + +/* + * 1,yes,true + * 0,no,false + */ +int +rc_getbool(struct rcfile *rcp, const char *section, const char *key, int *value) +{ + struct rcsection *rsp; + struct rckey *rkp; + char *p; + + rsp = rc_findsect(rcp, section); + if (!rsp) + return (ENOENT); + rkp = rc_sect_findkey(rsp, key); + if (!rkp) + return (ENOENT); + p = rkp->rk_value; + while (*p && isspace(*p)) p++; + if (*p == '0' || + strcasecmp(p, "no") == 0 || + strcasecmp(p, "false") == 0) { + *value = 0; + return (0); + } + if (*p == '1' || + strcasecmp(p, "yes") == 0 || + strcasecmp(p, "true") == 0) { + *value = 1; + return (0); + } + fprintf(stderr, dgettext(TEXT_DOMAIN, + "invalid boolean value '%s' for key '%s' in section '%s' \n"), + p, key, section); + return (EINVAL); +} + +/* + * Unified command line/rc file parser + */ +int +opt_args_parse(struct rcfile *rcp, struct opt_args *ap, const char *sect, + opt_callback_t *callback) +{ + int len, error; + + for (; ap->opt; ap++) { + switch (ap->type) { + case OPTARG_STR: + if (rc_getstringptr(rcp, sect, ap->name, &ap->str) != 0) + break; + len = strlen(ap->str); + if (len > ap->ival) { + fprintf(stdout, dgettext(TEXT_DOMAIN, + "rc: argument for option '%c' (%s) too long\n"), + ap->opt, ap->name); + return (EINVAL); + } + callback(ap); + break; + case OPTARG_BOOL: + error = rc_getbool(rcp, sect, ap->name, &ap->ival); + if (error == ENOENT) + break; + if (error) + return (EINVAL); + callback(ap); + break; + case OPTARG_INT: + if (rc_getint(rcp, sect, ap->name, &ap->ival) != 0) + break; + if (((ap->flag & OPTFL_HAVEMIN) && + ap->ival < ap->min) || + ((ap->flag & OPTFL_HAVEMAX) && + ap->ival > ap->max)) { + fprintf(stdout, dgettext(TEXT_DOMAIN, + "rc: argument for option '%c' (%s) " + "should be in [%d-%d] range\n"), + ap->opt, ap->name, ap->min, ap->max); + return (EINVAL); + } + callback(ap); + break; + default: + break; + } + } + return (0); +} + +int +opt_args_parseopt(struct opt_args *ap, int opt, char *arg, + opt_callback_t *callback) +{ + int len; + + for (; ap->opt; ap++) { + if (ap->opt != opt) + continue; + switch (ap->type) { + case OPTARG_STR: + ap->str = arg; + if (arg) { + len = strlen(ap->str); + if (len > ap->ival) { + fprintf(stdout, dgettext(TEXT_DOMAIN, + "opt: Argument for option '%c' (%s) too long\n"), + ap->opt, ap->name); + return (EINVAL); + } + callback(ap); + } + break; + case OPTARG_BOOL: + ap->ival = 0; + callback(ap); + break; + case OPTARG_INT: + errno = 0; + ap->ival = strtol(arg, NULL, 0); + if (errno) { + fprintf(stdout, dgettext(TEXT_DOMAIN, + "opt: Invalid integer value for " + "option '%c' (%s).\n"), + ap->opt, ap->name); + return (EINVAL); + } + if (((ap->flag & OPTFL_HAVEMIN) && + (ap->ival < ap->min)) || + ((ap->flag & OPTFL_HAVEMAX) && + (ap->ival > ap->max))) { + fprintf(stdout, dgettext(TEXT_DOMAIN, + "opt: Argument for option '%c' (%s) " + "should be in [%d-%d] range\n"), + ap->opt, ap->name, ap->min, ap->max); + return (EINVAL); + } + callback(ap); + break; + default: + break; + } + break; + } + return (0); +} diff --git a/usr/src/lib/libsmbfs/smb/rcfile_priv.h b/usr/src/lib/libsmbfs/smb/rcfile_priv.h new file mode 100644 index 0000000000..85ed97e1fd --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/rcfile_priv.h @@ -0,0 +1,21 @@ +#pragma ident "%Z%%M% %I% %E% SMI" + +struct rckey { + SLIST_ENTRY(rckey) rk_next; + char *rk_name; + char *rk_value; +}; + +struct rcsection { + SLIST_ENTRY(rcsection) rs_next; + SLIST_HEAD(rckey_head,rckey) rs_keys; + char *rs_name; +}; + +struct rcfile { + SLIST_ENTRY(rcfile) rf_next; + SLIST_HEAD(rcsec_head, rcsection) rf_sect; + char *rf_name; + FILE *rf_f; +}; + diff --git a/usr/src/lib/libsmbfs/smb/rq.c b/usr/src/lib/libsmbfs/smb/rq.c new file mode 100644 index 0000000000..c31c271819 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/rq.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: rq.c,v 1.4 2004/12/13 00:25:23 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <strings.h> +#include <stdlib.h> +#include <sysexits.h> +#include <libintl.h> + +#include <netsmb/smb_lib.h> + +extern uid_t real_uid, eff_uid; + + +int +smb_rq_init(struct smb_ctx *ctx, uchar_t cmd, size_t rpbufsz, + struct smb_rq **rqpp) +{ + struct smb_rq *rqp; + + rqp = malloc(sizeof (*rqp)); + if (rqp == NULL) + return (ENOMEM); + bzero(rqp, sizeof (*rqp)); + rqp->rq_cmd = cmd; + rqp->rq_ctx = ctx; + mb_init(&rqp->rq_rq, M_MINSIZE); + mb_init(&rqp->rq_rp, rpbufsz); + *rqpp = rqp; + return (0); +} + +void +smb_rq_done(struct smb_rq *rqp) +{ + mb_done(&rqp->rq_rp); + mb_done(&rqp->rq_rq); + free(rqp); +} + +void +smb_rq_wend(struct smb_rq *rqp) +{ + if (rqp->rq_rq.mb_count & 1) + smb_error(dgettext(TEXT_DOMAIN, + "smbrq_wend: odd word count\n"), 0); + rqp->rq_wcount = rqp->rq_rq.mb_count / 2; + rqp->rq_rq.mb_count = 0; +} + +int +smb_rq_dmem(struct mbdata *mbp, const char *src, size_t size) +{ + struct mbuf *m; + char *dst; + int cplen, error; + + if (size == 0) + return (0); + m = mbp->mb_cur; + if ((error = m_getm(m, size, &m)) != 0) + return (error); + while (size > 0) { + cplen = M_TRAILINGSPACE(m); + if (cplen == 0) { + m = m->m_next; + continue; + } + if (cplen > (int)size) + cplen = size; + dst = mtod(m, char *) + m->m_len; + nls_mem_toext(dst, src, cplen); + size -= cplen; + src += cplen; + m->m_len += cplen; + mbp->mb_count += cplen; + } + mbp->mb_pos = mtod(m, char *) + m->m_len; + mbp->mb_cur = m; + return (0); +} + +int +smb_rq_dstring(struct mbdata *mbp, const char *s) +{ + return (smb_rq_dmem(mbp, s, strlen(s) + 1)); +} + +int +smb_rq_simple(struct smb_rq *rqp) +{ + struct smbioc_rq krq; + struct mbdata *mbp; + char *data; + int i; + + mbp = smb_rq_getrequest(rqp); + m_lineup(mbp->mb_top, &mbp->mb_top); + data = mtod(mbp->mb_top, char *); + bzero(&krq, sizeof (krq)); + krq.ioc_cmd = rqp->rq_cmd; + krq.ioc_twc = rqp->rq_wcount; + krq.ioc_twords = data; + krq.ioc_tbc = mbp->mb_count; + krq.ioc_tbytes = data + rqp->rq_wcount * 2; + + mbp = smb_rq_getreply(rqp); + krq.ioc_rpbufsz = mbp->mb_top->m_maxlen; + krq.ioc_rpbuf = mtod(mbp->mb_top, char *); + seteuid(eff_uid); + if (ioctl(rqp->rq_ctx->ct_fd, SMBIOC_REQUEST, &krq) == -1) { + seteuid(real_uid); /* and back to real user */ + return (errno); + } + mbp->mb_top->m_len = krq.ioc_rwc * 2 + krq.ioc_rbc; + rqp->rq_wcount = krq.ioc_rwc; + rqp->rq_bcount = krq.ioc_rbc; + seteuid(real_uid); /* and back to real user */ + return (0); +} + + +int +smb_t2_request(struct smb_ctx *ctx, int setupcount, uint16_t *setup, + const char *name, + int tparamcnt, void *tparam, + int tdatacnt, void *tdata, + int *rparamcnt, void *rparam, + int *rdatacnt, void *rdata, + int *buffer_oflow) +{ + smbioc_t2rq_t *krq; + int i; + char *pass; + + + krq = (smbioc_t2rq_t *)malloc(sizeof (smbioc_t2rq_t)); + bzero(krq, sizeof (*krq)); + + if (setupcount < 0 || setupcount >= SMB_MAXSETUPWORDS) { + /* Bogus setup count, or too many setup words */ + return (EINVAL); + } + for (i = 0; i < setupcount; i++) + krq->ioc_setup[i] = setup[i]; + krq->ioc_setupcnt = setupcount; + strcpy(krq->ioc_name, name); + krq->ioc_tparamcnt = tparamcnt; + krq->ioc_tparam = tparam; + krq->ioc_tdatacnt = tdatacnt; + krq->ioc_tdata = tdata; + + krq->ioc_rparamcnt = *rparamcnt; + krq->ioc_rdatacnt = *rdatacnt; + krq->ioc_rparam = rparam; + krq->ioc_rdata = rdata; + + seteuid(eff_uid); + if (ioctl(ctx->ct_fd, SMBIOC_T2RQ, krq) == -1) { + seteuid(real_uid); /* and back to real user */ + return (errno); + } + + *rparamcnt = krq->ioc_rparamcnt; + *rdatacnt = krq->ioc_rdatacnt; + *buffer_oflow = (krq->ioc_rpflags2 & SMB_FLAGS2_ERR_STATUS) && + (krq->ioc_error == NT_STATUS_BUFFER_OVERFLOW); + seteuid(real_uid); /* and back to real user */ + free(krq); + return (0); +} diff --git a/usr/src/lib/libsmbfs/smb/spnego.c b/usr/src/lib/libsmbfs/smb/spnego.c new file mode 100644 index 0000000000..3e300cd606 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/spnego.c @@ -0,0 +1,797 @@ +// Copyright (C) 2002 Microsoft Corporation +// All rights reserved. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" +// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +// OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY +// AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Date - 10/08/2002 +// Author - Sanj Surati + +///////////////////////////////////////////////////////////// +// +// SPNEGO.C +// +// SPNEGO Token Handler Source File +// +// Contains implementation of SPNEGO Token Handling API +// as defined in SPNEGO.H. +// +///////////////////////////////////////////////////////////// + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stdio.h> +#include <memory.h> +#include "spnego.h" +#include "derparse.h" +#include "spnegoparse.h" + +// +// Defined in DERPARSE.C +// + +extern MECH_OID g_stcMechOIDList []; + + +/**********************************************************************/ +/** **/ +/** **/ +/** **/ +/** **/ +/** SPNEGO Token Handler API implementation **/ +/** **/ +/** **/ +/** **/ +/** **/ +/**********************************************************************/ + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// spnegoInitFromBinary +// +// Parameters: +// [in] pbTokenData - Binary Token Data +// [in] ulLength - Length of binary Token Data +// [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Initializes a SPNEGO_TOKEN_HANDLE from the supplied +// binary data. Data is copied locally. Returned data structure +// must be freed by calling spnegoFreeData(). +// +//////////////////////////////////////////////////////////////////////////// + +int spnegoInitFromBinary( unsigned char* pbTokenData, unsigned long ulLength, SPNEGO_TOKEN_HANDLE* phSpnegoToken ) +{ + int nReturn = SPNEGO_E_INVALID_PARAMETER; + SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**) phSpnegoToken; + + // Pass off to a handler function that allows tighter control over how the token structure + // is handled. In this case, we want the token data copied and we want the associated buffer + // freed. + nReturn = InitTokenFromBinary( SPNEGO_TOKEN_INTERNAL_COPYDATA, + SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA, pbTokenData, + ulLength, ppSpnegoToken ); + + return nReturn; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// spnegoCreateNegTokenInit +// +// Parameters: +// [in] MechType - MechType to specify in MechTypeList element +// [in] ucContextFlags - Context Flags element value +// [in] pbMechToken - Pointer to binary MechToken Data +// [in] ulMechTokenLen - Length of MechToken Data +// [in] pbMechListMIC - Pointer to binary MechListMIC Data +// [in] ulMechListMICLen - Length of MechListMIC Data +// [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Initializes a SPNEGO_TOKEN_HANDLE for a NegTokenInit type +// from the supplied parameters. ucContextFlags may be 0 or must be +// a valid flag combination. MechToken data can be NULL - if not, it +// must correspond to the MechType. MechListMIC can also be NULL. +// Returned data structure must be freed by calling spnegoFreeData(). +// +//////////////////////////////////////////////////////////////////////////// + +int spnegoCreateNegTokenInit( SPNEGO_MECH_OID MechType, + unsigned char ucContextFlags, unsigned char* pbMechToken, + unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, + unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken ) +{ + int nReturn = SPNEGO_E_INVALID_PARAMETER; + long nTokenLength = 0L; + long nInternalTokenLength = 0L; + unsigned char* pbTokenData = NULL; + SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**) phSpnegoToken; + + if ( NULL != ppSpnegoToken && + IsValidMechOid( MechType ) && + IsValidContextFlags( ucContextFlags ) ) + { + // Get the actual token size + + if ( ( nReturn = CalculateMinSpnegoInitTokenSize( ulMechTokenLen, ulMechListMICLen, + MechType, ( ucContextFlags != 0L ), + &nTokenLength, &nInternalTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + // Allocate a buffer to hold the data. + pbTokenData = calloc( 1, nTokenLength ); + + if ( NULL != pbTokenData ) + { + + // Now write the token + if ( ( nReturn = CreateSpnegoInitToken( MechType, + ucContextFlags, pbMechToken, + ulMechTokenLen, pbMechListMIC, + ulMechListMICLen, pbTokenData, + nTokenLength, nInternalTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + + // This will copy our allocated pointer, and ensure that the sructure cleans + // up the data later + nReturn = InitTokenFromBinary( SPNEGO_TOKEN_INTERNAL_COPYPTR, + SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA, + pbTokenData, nTokenLength, ppSpnegoToken ); + + } + + // Cleanup on failure + if ( SPNEGO_E_SUCCESS != nReturn ) + { + free( pbTokenData ); + } + + } // IF alloc succeeded + else + { + nReturn = SPNEGO_E_OUT_OF_MEMORY; + } + + } // If calculated token size + + } // IF Valid Parameters + + return nReturn; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// spnegoCreateNegTokenTarg +// +// Parameters: +// [in] MechType - MechType to specify in supported MechType element +// [in] spnegoNegResult - NegResult value +// [in] pbMechToken - Pointer to response MechToken Data +// [in] ulMechTokenLen - Length of MechToken Data +// [in] pbMechListMIC - Pointer to binary MechListMIC Data +// [in] ulMechListMICLen - Length of MechListMIC Data +// [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Initializes a SPNEGO_TOKEN_HANDLE for a NegTokenTarg type +// from the supplied parameters. MechToken data can be NULL - if not, +// it must correspond to the MechType. MechListMIC can also be NULL. +// Returned data structure must be freed by calling spnegoFreeData(). +// +//////////////////////////////////////////////////////////////////////////// + +int spnegoCreateNegTokenTarg( SPNEGO_MECH_OID MechType, + SPNEGO_NEGRESULT spnegoNegResult, unsigned char* pbMechToken, + unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, + unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken ) +{ + int nReturn = SPNEGO_E_INVALID_PARAMETER; + long nTokenLength = 0L; + long nInternalTokenLength = 0L; + unsigned char* pbTokenData = NULL; + SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**) phSpnegoToken; + + // + // spnego_mech_oid_NotUsed and spnego_negresult_NotUsed + // are okay here, however a valid MechOid is required + // if spnego_negresult_success or spnego_negresult_incomplete + // is specified. + // + + if ( NULL != ppSpnegoToken && + + ( IsValidMechOid( MechType ) || + spnego_mech_oid_NotUsed == MechType ) && + + ( IsValidNegResult( spnegoNegResult ) || + spnego_negresult_NotUsed == spnegoNegResult ) && + + !( !IsValidMechOid( MechType ) && + ( spnego_negresult_success == spnegoNegResult || + spnego_negresult_incomplete == spnegoNegResult ) ) ) + { + + // Get the actual token size + + if ( ( nReturn = CalculateMinSpnegoTargTokenSize( MechType, spnegoNegResult, ulMechTokenLen, + ulMechListMICLen, &nTokenLength, + &nInternalTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + // Allocate a buffer to hold the data. + pbTokenData = calloc( 1, nTokenLength ); + + if ( NULL != pbTokenData ) + { + + // Now write the token + if ( ( nReturn = CreateSpnegoTargToken( MechType, + spnegoNegResult, pbMechToken, + ulMechTokenLen, pbMechListMIC, + ulMechListMICLen, pbTokenData, + nTokenLength, nInternalTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + + // This will copy our allocated pointer, and ensure that the sructure cleans + // up the data later + nReturn = InitTokenFromBinary( SPNEGO_TOKEN_INTERNAL_COPYPTR, + SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA, + pbTokenData, nTokenLength, ppSpnegoToken ); + + } + + // Cleanup on failure + if ( SPNEGO_E_SUCCESS != nReturn ) + { + free( pbTokenData ); + } + + } // IF alloc succeeded + else + { + nReturn = SPNEGO_E_OUT_OF_MEMORY; + } + + } // If calculated token size + + } // IF Valid Parameters + + return nReturn; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// spnegoTokenGetBinary +// +// Parameters: +// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE +// [out] pbTokenData - Buffer to copy token into +// [in/out] pulDataLen - Length of pbTokenData buffer, filled out +// with actual size used upon function return. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Copies binary SPNEGO token data from hSpnegoToken into the user +// supplied buffer. If pbTokenData is NULL, or the value in pulDataLen +// is too small, the function will return SPNEGO_E_BUFFER_TOO_SMALL and +// fill out pulDataLen with the minimum required buffer size. +// +//////////////////////////////////////////////////////////////////////////// + +int spnegoTokenGetBinary( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, + unsigned long * pulDataLen ) +{ + int nReturn = SPNEGO_E_INVALID_PARAMETER; + SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; + + // Check parameters - pbTokenData is optional + if ( IsValidSpnegoToken( pSpnegoToken ) && + NULL != pulDataLen ) + { + + // Check for Buffer too small conditions + if ( NULL == pbTokenData || + pSpnegoToken->ulBinaryDataLen > *pulDataLen ) + { + *pulDataLen = pSpnegoToken->ulBinaryDataLen; + nReturn = SPNEGO_E_BUFFER_TOO_SMALL; + } + else + { + memcpy( pbTokenData, pSpnegoToken->pbBinaryData, pSpnegoToken->ulBinaryDataLen ); + *pulDataLen = pSpnegoToken->ulBinaryDataLen; + nReturn = SPNEGO_E_SUCCESS; + } + + } // IF parameters OK + + return nReturn;; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// spnegoFreeData +// +// Parameters: +// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE +// +// Returns: +// void +// +// Comments : +// Frees up resources consumed by hSpnegoToken. The supplied data +// pointer is invalidated by this function. +// +//////////////////////////////////////////////////////////////////////////// + +void spnegoFreeData( SPNEGO_TOKEN_HANDLE hSpnegoToken ) +{ + FreeSpnegoToken( (SPNEGO_TOKEN*) hSpnegoToken); + return; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// spnegoGetTokenType +// +// Parameters: +// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE +// [out] piTokenType - Filled out with token type value. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// The function will analyze hSpnegoToken and return the appropriate +// type in piTokenType. +// +//////////////////////////////////////////////////////////////////////////// + +int spnegoGetTokenType( SPNEGO_TOKEN_HANDLE hSpnegoToken, int * piTokenType ) +{ + int nReturn = SPNEGO_E_INVALID_PARAMETER; + SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; + + // Check parameters + if ( IsValidSpnegoToken( pSpnegoToken ) && + NULL != piTokenType && + pSpnegoToken) + { + + // Check that the type in the structure makes sense + if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType || + SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType ) + { + *piTokenType = pSpnegoToken->ucTokenType; + nReturn = SPNEGO_E_SUCCESS; + } + + } // IF parameters OK + + return nReturn; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// spnegoIsMechTypeAvailable +// +// Parameters: +// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE +// [in] MechOID - MechOID to search MechTypeList for +// [out] piMechTypeIndex - Filled out with index in MechTypeList +// element if MechOID is found. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// hSpnegoToken must reference a token of type NegTokenInit. The +// function will search the MechTypeList element for an OID corresponding +// to the specified MechOID. If one is found, the index (0 based) will +// be passed into the piMechTypeIndex parameter. +// +//////////////////////////////////////////////////////////////////////////// + +// Returns the Initial Mech Type in the MechList element in the NegInitToken. +int spnegoIsMechTypeAvailable( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID MechOID, int * piMechTypeIndex ) +{ + int nReturn = SPNEGO_E_INVALID_PARAMETER; + SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; + + // Check parameters + if ( IsValidSpnegoToken( pSpnegoToken ) && + NULL != piMechTypeIndex && + IsValidMechOid( MechOID ) && + SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) + { + + // Check if MechList is available + if ( pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTYPES_ELEMENT].iElementPresent + == SPNEGO_TOKEN_ELEMENT_AVAILABLE ) + { + // Locate the MechOID in the list element + nReturn = FindMechOIDInMechList( + &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTYPES_ELEMENT], + MechOID, piMechTypeIndex ); + } + else + { + nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE; + } + + } // IF parameters OK + + return nReturn;; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// spnegoGetContextFlags +// +// Parameters: +// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE +// [out] pucContextFlags - Filled out with ContextFlags value. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// hSpnegoToken must reference a token of type NegTokenInit. The +// function will copy data from the ContextFlags element into the +// location pucContextFlags points to. Note that the function will +// fail if the actual ContextFlags data appears invalid. +// +//////////////////////////////////////////////////////////////////////////// + +int spnegoGetContextFlags( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pucContextFlags ) +{ + int nReturn = SPNEGO_E_INVALID_PARAMETER; + SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; + + // Check parameters + if ( IsValidSpnegoToken( pSpnegoToken ) && + NULL != pucContextFlags && + SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) + { + + // Check if ContextFlags is available + if ( pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].iElementPresent + == SPNEGO_TOKEN_ELEMENT_AVAILABLE ) + { + // The length should be two, the value should show a 1 bit difference in the difference byte, and + // the value must be valid + if ( pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].nDatalength == SPNEGO_NEGINIT_MAXLEN_REQFLAGS && + pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[0] == SPNEGO_NEGINIT_REQFLAGS_BITDIFF && + IsValidContextFlags( pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[1] ) ) + { + *pucContextFlags = pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[1]; + nReturn = SPNEGO_E_SUCCESS; + } + else + { + nReturn = SPNEGO_E_INVALID_ELEMENT; + } + + } + else + { + nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE; + } + + } // IF parameters OK + + return nReturn;; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// spnegoGetNegotiationResult +// +// Parameters: +// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE +// [out] pnegResult - Filled out with NegResult value. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// hSpnegoToken must reference a token of type NegTokenTarg. The +// function will copy data from the NegResult element into the +// location pointed to by pnegResult. Note that the function will +// fail if the actual NegResult data appears invalid. +// +//////////////////////////////////////////////////////////////////////////// + +int spnegoGetNegotiationResult( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_NEGRESULT* pnegResult ) +{ + int nReturn = SPNEGO_E_INVALID_PARAMETER; + SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; + + // Check parameters + if ( IsValidSpnegoToken( pSpnegoToken ) && + NULL != pnegResult && + SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType ) + { + + // Check if NegResult is available + if ( pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].iElementPresent + == SPNEGO_TOKEN_ELEMENT_AVAILABLE ) + { + // Must be 1 byte long and a valid value + if ( pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].nDatalength == SPNEGO_NEGTARG_MAXLEN_NEGRESULT && + IsValidNegResult( *pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].pbData ) ) + { + *pnegResult = *pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].pbData; + nReturn = SPNEGO_E_SUCCESS; + } + else + { + nReturn = SPNEGO_E_INVALID_ELEMENT; + } + } + else + { + nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE; + } + + } // IF parameters OK + + return nReturn;; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// spnegoGetSupportedMechType +// +// Parameters: +// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE +// [out] pMechOID - Filled out with Supported MechType value. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// hSpnegoToken must reference a token of type NegTokenTarg. The +// function will check the Supported MechType element, and if it +// corresponds to a supported MechType ( spnego_mech_oid_Kerberos_V5_Legacy +// or spnego_mech_oid_Kerberos_V5 ), will set the location pointed +// to by pMechOID equal to the appropriate value. +// +//////////////////////////////////////////////////////////////////////////// + +int spnegoGetSupportedMechType( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID* pMechOID ) +{ + int nReturn = SPNEGO_E_INVALID_PARAMETER; + int nCtr = 0L; + long nLength = 0L; + SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; + + // Check parameters + if ( IsValidSpnegoToken( pSpnegoToken ) && + NULL != pMechOID && + SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType ) + { + + // Check if MechList is available + if ( pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].iElementPresent + == SPNEGO_TOKEN_ELEMENT_AVAILABLE ) + { + + for ( nCtr = 0; + nReturn != SPNEGO_E_SUCCESS && + g_stcMechOIDList[nCtr].eMechanismOID != spnego_mech_oid_NotUsed; + nCtr++ ) + { + + if ( ( nReturn = ASNDerCheckOID( + pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].pbData, + nCtr, + pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].nDatalength, + &nLength ) ) == SPNEGO_E_SUCCESS ) + { + *pMechOID = nCtr; + } + + } // For enum MechOIDs + + + } + else + { + nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE; + } + + } // IF parameters OK + + return nReturn;; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// spnegoTokenGetMechToken +// +// Parameters: +// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE +// [out] pbTokenData - Buffer to copy MechToken into +// [in/out] pulDataLen - Length of pbTokenData buffer, filled out +// with actual size used upon function return. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// hSpnegoToken can point to either NegTokenInit or a NegTokenTarg token. +// The function will copy the MechToken (the initial MechToken if +// NegTokenInit, the response MechToken if NegTokenTarg) from the +// underlying token into the buffer pointed to by pbTokenData. If +// pbTokenData is NULL, or the value in pulDataLen is too small, the +// function will return SPNEGO_E_BUFFER_TOO_SMALL and fill out pulDataLen +// with the minimum required buffer size. The token can then be passed +// to a GSS-API function for processing. +// +//////////////////////////////////////////////////////////////////////////// + +int spnegoGetMechToken( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, unsigned long* pulDataLen ) +{ + int nReturn = SPNEGO_E_INVALID_PARAMETER; + SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; + SPNEGO_ELEMENT* pSpnegoElement = NULL; + + // Check parameters + if ( IsValidSpnegoToken( pSpnegoToken ) && + NULL != pulDataLen ) + { + + // Point at the proper Element + if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) + { + pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTOKEN_ELEMENT]; + } + else + { + pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_TARG_RESPTOKEN_ELEMENT]; + } + + // Check if MechType is available + if ( SPNEGO_TOKEN_ELEMENT_AVAILABLE == pSpnegoElement->iElementPresent ) + { + // Check for Buffer too small conditions + if ( NULL == pbTokenData || + pSpnegoElement->nDatalength > *pulDataLen ) + { + *pulDataLen = pSpnegoElement->nDatalength; + nReturn = SPNEGO_E_BUFFER_TOO_SMALL; + } + else + { + // Copy Memory + memcpy( pbTokenData, pSpnegoElement->pbData, pSpnegoElement->nDatalength ); + *pulDataLen = pSpnegoElement->nDatalength; + nReturn = SPNEGO_E_SUCCESS; + } + } + else + { + nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE; + } + + } // IF parameters OK + + return nReturn;; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// spnegoTokenGetMechListMIC +// +// Parameters: +// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE +// [out] pbTokenData - Buffer to copy MechListMIC data into +// [in/out] pulDataLen - Length of pbTokenData buffer, filled out +// with actual size used upon function return. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// hSpnegoToken can point to either NegTokenInit or a NegTokenTarg token. +// The function will copy the MechListMIC data from the underlying token +// into the buffer pointed to by pbTokenData. If pbTokenData is NULL, +// or the value in pulDataLen is too small, the function will return +// SPNEGO_E_BUFFER_TOO_SMALL and fill out pulDataLen with the minimum +// required buffer size. +// +//////////////////////////////////////////////////////////////////////////// + +int spnegoGetMechListMIC( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbMICData, unsigned long* pulDataLen ) +{ + int nReturn = SPNEGO_E_INVALID_PARAMETER; + SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) hSpnegoToken; + SPNEGO_ELEMENT* pSpnegoElement = NULL; + + // Check parameters + if ( IsValidSpnegoToken( pSpnegoToken ) && + NULL != pulDataLen ) + { + + // Point at the proper Element + if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) + { + pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHLISTMIC_ELEMENT]; + } + else + { + pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_TARG_MECHLISTMIC_ELEMENT]; + } + + // Check if MechType is available + if ( SPNEGO_TOKEN_ELEMENT_AVAILABLE == pSpnegoElement->iElementPresent ) + { + // Check for Buffer too small conditions + if ( NULL == pbMICData || + pSpnegoElement->nDatalength > *pulDataLen ) + { + *pulDataLen = pSpnegoElement->nDatalength; + nReturn = SPNEGO_E_BUFFER_TOO_SMALL; + } + else + { + // Copy Memory + memcpy( pbMICData, pSpnegoElement->pbData, pSpnegoElement->nDatalength ); + *pulDataLen = pSpnegoElement->nDatalength; + nReturn = SPNEGO_E_SUCCESS; + } + } + else + { + nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE; + } + + } // IF parameters OK + + return nReturn;; +} + diff --git a/usr/src/lib/libsmbfs/smb/spnego.h b/usr/src/lib/libsmbfs/smb/spnego.h new file mode 100644 index 0000000000..9865fbd85d --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/spnego.h @@ -0,0 +1,244 @@ +// Copyright (C) 2002 Microsoft Corporation +// All rights reserved. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" +// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +// OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY +// AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Date - 10/08/2002 +// Author - Sanj Surati + +///////////////////////////////////////////////////////////// +// +// SPNEGO.H +// +// SPNEGO Token Handler Header File +// +// Contains the definitions required to interpret and create +// SPNEGO tokens so that Kerberos GSS tokens can be +// Unpackaged/packaged. +// +///////////////////////////////////////////////////////////// + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef __SPNEGO_H__ +#define __SPNEGO_H__ + +// C++ Specific +#if defined(__cplusplus) +extern "C" +{ +#endif + +// Type Definitions + +// +// Users of SPNEGO Token Handler API will request +// these as well as free them, +// +typedef void* SPNEGO_TOKEN_HANDLE; + +// +// Defines the element types that are found +// in each of the tokens. +// + +typedef enum spnego_element_type +{ + spnego_element_min, // Lower bound + + // Init token elements + spnego_init_mechtypes, + spnego_init_reqFlags, + spnego_init_mechToken, + spnego_init_mechListMIC, + + // Targ token elements + spnego_targ_negResult, + spnego_targ_supportedMech, + spnego_targ_responseToken, + spnego_targ_mechListMIC, + + spnego_element_max // Upper bound + +} SPNEGO_ELEMENT_TYPE; + +// +// Token Element Availability. Elements in both +// token types are optional. Since there are only +// 4 elements in each Token, we will allocate space +// to hold the information, but we need a way to +// indicate whether or not an element is available +// + +#define SPNEGO_TOKEN_ELEMENT_UNAVAILABLE 0 +#define SPNEGO_TOKEN_ELEMENT_AVAILABLE 1 + +// +// Token type values. SPNEGO has 2 token types: +// NegTokenInit and NegTokenTarg +// + +#define SPNEGO_TOKEN_INIT 0 +#define SPNEGO_TOKEN_TARG 1 + +// +// GSS Mechanism OID enumeration. We only really handle +// 3 different OIDs. These are stored in an array structure +// defined in the parsing code. +// + +typedef enum spnego_mech_oid +{ + // Init token elements + spnego_mech_oid_Kerberos_V5_Legacy, // Really V5, but OID off by 1 bit + spnego_mech_oid_Kerberos_V5, + spnego_mech_oid_Spnego, + spnego_mech_oid_NTLMSSP, + spnego_mech_oid_NotUsed = -1 + +} SPNEGO_MECH_OID; + +// +// Defines the negResult values. +// + +typedef enum spnego_negResult +{ + spnego_negresult_success, + spnego_negresult_incomplete, + spnego_negresult_rejected, + spnego_negresult_NotUsed = -1 +} SPNEGO_NEGRESULT; + +// +// Context Flags in NegTokenInit +// + +// +// ContextFlags values MUST be zero or a combination +// of the below +// + +#define SPNEGO_NEGINIT_CONTEXT_DELEG_FLAG 0x80 +#define SPNEGO_NEGINIT_CONTEXT_MUTUAL_FLAG 0x40 +#define SPNEGO_NEGINIT_CONTEXT_REPLAY_FLAG 0x20 +#define SPNEGO_NEGINIT_CONTEXT_SEQUENCE_FLAG 0x10 +#define SPNEGO_NEGINIT_CONTEXT_ANON_FLAG 0x8 +#define SPNEGO_NEGINIT_CONTEXT_CONF_FLAG 0x4 +#define SPNEGO_NEGINIT_CONTEXT_INTEG_FLAG 0x2 + +// +// Mask to retrieve valid values. +// + +#define SPNEGO_NEGINIT_CONTEXT_MASK 0xFE // Logical combination of above flags + +// +// SPNEGO API return codes. +// + +// API function was successful +#define SPNEGO_E_SUCCESS 0 + +// The supplied Token was invalid +#define SPNEGO_E_INVALID_TOKEN -1 + +// An invalid length was encountered +#define SPNEGO_E_INVALID_LENGTH -2 + +// The Token Parse failed +#define SPNEGO_E_PARSE_FAILED -3 + +// The requested value was not found +#define SPNEGO_E_NOT_FOUND -4 + +// The requested element is not available +#define SPNEGO_E_ELEMENT_UNAVAILABLE -5 + +// Out of Memory +#define SPNEGO_E_OUT_OF_MEMORY -6 + +// Not Implemented +#define SPNEGO_E_NOT_IMPLEMENTED -7 + +// Invalid Parameter +#define SPNEGO_E_INVALID_PARAMETER -8 + +// Token Handler encountered an unexpected OID +#define SPNEGO_E_UNEXPECTED_OID -9 + +// The requested token was not found +#define SPNEGO_E_TOKEN_NOT_FOUND -10 + +// An unexpected type was encountered in the encoding +#define SPNEGO_E_UNEXPECTED_TYPE -11 + +// The buffer was too small +#define SPNEGO_E_BUFFER_TOO_SMALL -12 + +// A Token Element was invalid (e.g. improper length or value) +#define SPNEGO_E_INVALID_ELEMENT -13 + +/* Miscelaneous API Functions */ + +// Frees opaque data +void spnegoFreeData( SPNEGO_TOKEN_HANDLE hSpnegoToken ); + +// Initializes SPNEGO_TOKEN structure from DER encoded binary data +int spnegoInitFromBinary( unsigned char* pbTokenData, unsigned long ulLength, SPNEGO_TOKEN_HANDLE* phSpnegoToken ); + +// Initializes SPNEGO_TOKEN structure for a NegTokenInit type using the +// supplied parameters +int spnegoCreateNegTokenInit( SPNEGO_MECH_OID MechType, + unsigned char ucContextFlags, unsigned char* pbMechToken, + unsigned long ulMechTokenLen, unsigned char* pbMechTokenMIC, + unsigned long ulMechTokenMIC, SPNEGO_TOKEN_HANDLE* phSpnegoToken ); + +// Initializes SPNEGO_TOKEN structure for a NegTokenTarg type using the +// supplied parameters +int spnegoCreateNegTokenTarg( SPNEGO_MECH_OID MechType, + SPNEGO_NEGRESULT spnegoNegResult, unsigned char* pbMechToken, + unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, + unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken ); + +// Copies binary representation of SPNEGO Data into user supplied buffer +int spnegoTokenGetBinary( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, + unsigned long * pulDataLen ); + +// Returns SPNEGO Token Type +int spnegoGetTokenType( SPNEGO_TOKEN_HANDLE hSpnegoToken, int * piTokenType ); + +/* Reading an Init Token */ + +// Returns the Initial Mech Type in the MechList element in the NegInitToken. +int spnegoIsMechTypeAvailable( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID MechOID, int * piMechTypeIndex ); + +// Returns the value from the context flags element in the NegInitToken as an unsigned long +int spnegoGetContextFlags( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pucContextFlags ); + +/* Reading a Response Token */ + +// Returns the value from the negResult element (Status code of GSS call - 0,1,2) +int spnegoGetNegotiationResult( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_NEGRESULT* pnegResult ); + +// Returns the Supported Mech Type from the NegTokenTarg. +int spnegoGetSupportedMechType( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID* pMechOID ); + +/* Reading either Token Type */ + +// Returns the actual Mechanism data from the token (this is what is passed into GSS-API functions +int spnegoGetMechToken( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, unsigned long* pulDataLen ); + +// Returns the Message Integrity BLOB in the token +int spnegoGetMechListMIC( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbMICData, unsigned long* pulDataLen ); + +// C++ Specific +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/usr/src/lib/libsmbfs/smb/spnegoparse.c b/usr/src/lib/libsmbfs/smb/spnegoparse.c new file mode 100644 index 0000000000..5da1983c27 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/spnegoparse.c @@ -0,0 +1,1883 @@ +// Copyright (C) 2002 Microsoft Corporation +// All rights reserved. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" +// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +// OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY +// AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Date - 10/08/2002 +// Author - Sanj Surati + +///////////////////////////////////////////////////////////// +// +// SPNEGOPARSE.C +// +// SPNEGO Token Handler Source File +// +// Contains implementation of SPNEGO Token parsing functions. +// +///////////////////////////////////////////////////////////// + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stdio.h> +#include <memory.h> +#include "spnego.h" +#include "derparse.h" +#include "spnegoparse.h" + +// +// Defined in DERPARSE.C +// + +extern MECH_OID g_stcMechOIDList []; + +/**********************************************************************/ +/** **/ +/** **/ +/** **/ +/** **/ +/** Local SPNEGO Helper definitions **/ +/** **/ +/** **/ +/** **/ +/** **/ +/**********************************************************************/ + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// CalculateMinSpnegoInitTokenSize +// +// Parameters: +// [in] nMechTokenLength - Length of the MechToken Element +// [in] nMechListMICLength - Length of the MechListMIC Element +// [in] mechOID - OID for MechList +// [in] nReqFlagsAvailable - Is ContextFlags element available +// [out] pnTokenSize - Filled out with total size of token +// [out] pnInternalTokenLength - Filled out with length minus length +// for initial token. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Calculates the required length for a SPNEGO NegTokenInit token based +// on the supplied variable length values and which elements are present. +// Note that because the lengths can be represented by an arbitrary +// number of bytes in DER encodings, we actually calculate the lengths +// backwards, so we always know how many bytes we will potentially be +// writing out. +// +//////////////////////////////////////////////////////////////////////////// + +int CalculateMinSpnegoInitTokenSize( long nMechTokenLength, + long nMechListMICLength, SPNEGO_MECH_OID mechOid, + int nReqFlagsAvailable, long* pnTokenSize, + long* pnInternalTokenLength ) +{ + int nReturn = SPNEGO_E_INVALID_LENGTH; + + // Start at 0. + long nTotalLength = 0; + long nTempLength= 0L; + + // We will calculate this by walking the token backwards + + // Start with MIC Element + if ( nMechListMICLength > 0L ) + { + nTempLength = ASNDerCalcElementLength( nMechListMICLength, NULL ); + + // Check for rollover error + if ( nTempLength < nMechListMICLength ) + { + goto xEndTokenInitLength; + } + + nTotalLength += nTempLength; + } + + // Next is the MechToken + if ( nMechTokenLength > 0L ) + { + nTempLength += ASNDerCalcElementLength( nMechTokenLength, NULL ); + + // Check for rollover error + if ( nTempLength < nTotalLength ) + { + goto xEndTokenInitLength; + } + + nTotalLength = nTempLength; + } + + // Next is the ReqFlags + if ( nReqFlagsAvailable ) + { + nTempLength += ASNDerCalcElementLength( SPNEGO_NEGINIT_MAXLEN_REQFLAGS, NULL ); + + // Check for rollover error + if ( nTempLength < nTotalLength ) + { + goto xEndTokenInitLength; + } + + nTotalLength = nTempLength; + } + + // Next is the MechList - This is REQUIRED + nTempLength += ASNDerCalcMechListLength( mechOid, NULL ); + + // Check for rollover error + if ( nTempLength < nTotalLength ) + { + goto xEndTokenInitLength; + } + + nTotalLength = nTempLength; + + // Following four fields are the basic header tokens + + // Sequence Token + nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L ); + + // Check for rollover error + if ( nTempLength < nTotalLength ) + { + goto xEndTokenInitLength; + } + + nTotalLength = nTempLength; + + // Neg Token Identifier Token + nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L ); + + // Check for rollover error + if ( nTempLength < nTotalLength ) + { + goto xEndTokenInitLength; + } + + nTotalLength = nTempLength; + + // SPNEGO OID Token + nTempLength += g_stcMechOIDList[spnego_mech_oid_Spnego].iLen; + + // Check for rollover error + if ( nTempLength < nTotalLength ) + { + goto xEndTokenInitLength; + } + + nTotalLength = nTempLength; + + // App Constructed Token + nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L ); + + // Check for rollover error + if ( nTempLength < nTotalLength ) + { + goto xEndTokenInitLength; + } + + // The internal length doesn't include the number of bytes + // for the initial token + *pnInternalTokenLength = nTotalLength; + nTotalLength = nTempLength; + + // We're done + *pnTokenSize = nTotalLength; + nReturn = SPNEGO_E_SUCCESS; + +xEndTokenInitLength: + + return nReturn; + +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// CreateSpnegoInitToken +// +// Parameters: +// [in] MechType - OID in MechList +// [in] ucContextFlags - ContextFlags value +// [in] pbMechToken - Mech Token Binary Data +// [in] ulMechTokenLen - Length of Mech Token +// [in] pbMechListMIC - MechListMIC Binary Data +// [in] ulMechListMICn - Length of MechListMIC +// [out] pbTokenData - Buffer to write token into. +// [in] nTokenLength - Length of pbTokenData buffer +// [in] nInternalTokenLength - Length of full token without leading +// token bytes. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Uses DER to fill out pbTokenData with a SPNEGO NegTokenInit Token +// Note that because the lengths can be represented by an arbitrary +// number of bytes in DER encodings, we actually calculate the lengths +// backwards, so we always know how many bytes we will potentially be +// writing out. +// +//////////////////////////////////////////////////////////////////////////// + +int CreateSpnegoInitToken( SPNEGO_MECH_OID MechType, + unsigned char ucContextFlags, unsigned char* pbMechToken, + unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, + unsigned long ulMechListMICLen, unsigned char* pbTokenData, + long nTokenLength, long nInternalTokenLength ) +{ + int nReturn = SPNEGO_E_INVALID_LENGTH; + + // Start at 0. + long nTempLength= 0L; + long nTotalBytesWritten = 0L; + long nInternalLength = 0L; + + unsigned char* pbWriteTokenData = pbTokenData + nTokenLength; + + // Temporary buffer to hold the REQ Flags as BIT String Data + unsigned char abTempReqFlags[SPNEGO_NEGINIT_MAXLEN_REQFLAGS]; + + + // We will write the token out backwards to properly handle the cases + // where the length bytes become adjustable + + // Start with MIC Element + if ( ulMechListMICLen > 0L ) + { + nTempLength = ASNDerCalcElementLength( ulMechListMICLen, &nInternalLength ); + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC, + OCTETSTRING, pbMechListMIC, ulMechListMICLen ); + + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + nInternalTokenLength -= nTempLength; + + if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) + { + goto xEndWriteNegTokenInit; + } + + } // IF MechListMIC is present + + // Next is the MechToken + if ( ulMechTokenLen > 0L ) + { + nTempLength = ASNDerCalcElementLength( ulMechTokenLen, &nInternalLength ); + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGINIT_ELEMENT_MECHTOKEN, + OCTETSTRING, pbMechToken, ulMechTokenLen ); + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + nInternalTokenLength -= nTempLength; + + if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) + { + goto xEndWriteNegTokenInit; + } + + } // IF MechToken Length is present + + // Next is the ReqFlags + if ( ucContextFlags > 0L ) + { + + nTempLength = ASNDerCalcElementLength( SPNEGO_NEGINIT_MAXLEN_REQFLAGS, &nInternalLength ); + + // We need a byte that indicates how many bits difference between the number + // of bits used in final octet (we only have one) and the max (8) + + abTempReqFlags[0] = SPNEGO_NEGINIT_REQFLAGS_BITDIFF; + abTempReqFlags[1] = ucContextFlags; + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGINIT_ELEMENT_REQFLAGS, + BITSTRING, abTempReqFlags, SPNEGO_NEGINIT_MAXLEN_REQFLAGS ); + + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + nInternalTokenLength -= nTempLength; + + if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) + { + goto xEndWriteNegTokenInit; + } + + } // IF ContextFlags + + // Next is the MechList - This is REQUIRED + nTempLength = ASNDerCalcMechListLength( MechType, &nInternalLength ); + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteMechList( pbWriteTokenData, MechType ); + + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + nInternalTokenLength -= nTempLength; + + if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) + { + goto xEndWriteNegTokenInit; + } + + // The next tokens we're writing out reflect the total number of bytes + // we have actually written out. + + // Sequence Token + nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L ); + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_CONSTRUCTED_SEQUENCE, + NULL, nTotalBytesWritten ); + + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + nInternalTokenLength -= nTempLength; + + if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) + { + goto xEndWriteNegTokenInit; + } + + // Neg Init Token Identifier Token + nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L ); + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_NEGINIT_TOKEN_IDENTIFIER, + NULL, nTotalBytesWritten ); + + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + nInternalTokenLength -= nTempLength; + + if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) + { + goto xEndWriteNegTokenInit; + } + + // SPNEGO OID Token + nTempLength = g_stcMechOIDList[spnego_mech_oid_Spnego].iLen; + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteOID( pbWriteTokenData, spnego_mech_oid_Spnego ); + + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + nInternalTokenLength -= nTempLength; + + if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) + { + goto xEndWriteNegTokenInit; + } + + // App Constructed Token + nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L ); + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_NEGINIT_APP_CONSTRUCT, + NULL, nTotalBytesWritten ); + + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + + // Don't adjust the internal token length here, it doesn't account + // the initial bytes written out (we really don't need to keep + // a running count here, but for debugging, it helps to be able + // to see the total number of bytes written out as well as the + // number of bytes left to write). + + if ( nTotalBytesWritten == nTokenLength && nInternalTokenLength == 0 && + pbWriteTokenData == pbTokenData ) + { + nReturn = SPNEGO_E_SUCCESS; + } + +xEndWriteNegTokenInit: + + return nReturn; + +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// CalculateMinSpnegoTargTokenSize +// +// Parameters: +// [in] MechType - Supported MechType +// [in] spnegoNegResult - Neg Result +// [in] nMechTokenLength - Length of the MechToken Element +// [in] nMechListMICLength - Length of the MechListMIC Element +// [out] pnTokenSize - Filled out with total size of token +// [out] pnInternalTokenLength - Filled out with length minus length +// for initial token. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Calculates the required length for a SPNEGO NegTokenTarg token based +// on the supplied variable length values and which elements are present. +// Note that because the lengths can be represented by an arbitrary +// number of bytes in DER encodings, we actually calculate the lengths +// backwards, so we always know how many bytes we will potentially be +// writing out. +// +//////////////////////////////////////////////////////////////////////////// + +int CalculateMinSpnegoTargTokenSize( SPNEGO_MECH_OID MechType, + SPNEGO_NEGRESULT spnegoNegResult, long nMechTokenLen, + long nMechListMICLen, long* pnTokenSize, + long* pnInternalTokenLength ) +{ + int nReturn = SPNEGO_E_INVALID_LENGTH; + + // Start at 0. + long nTotalLength = 0; + long nTempLength= 0L; + + // We will calculate this by walking the token backwards + + // Start with MIC Element + if ( nMechListMICLen > 0L ) + { + nTempLength = ASNDerCalcElementLength( nMechListMICLen, NULL ); + + // Check for rollover error + if ( nTempLength < nMechListMICLen ) + { + goto xEndTokenTargLength; + } + + nTotalLength += nTempLength; + } + + // Next is the MechToken + if ( nMechTokenLen > 0L ) + { + nTempLength += ASNDerCalcElementLength( nMechTokenLen, NULL ); + + // Check for rollover error + if ( nTempLength < nTotalLength ) + { + goto xEndTokenTargLength; + } + + nTotalLength = nTempLength; + } + + // Supported MechType + if ( spnego_mech_oid_NotUsed != MechType ) + { + // Supported MechOID element - we use the token function since + // we already know the size of the OID token and value + nTempLength += ASNDerCalcElementLength( g_stcMechOIDList[MechType].iActualDataLen, + NULL ); + + // Check for rollover error + if ( nTempLength < nTotalLength ) + { + goto xEndTokenTargLength; + } + + nTotalLength = nTempLength; + + } // IF MechType is available + + // NegResult Element + if ( spnego_negresult_NotUsed != spnegoNegResult ) + { + nTempLength += ASNDerCalcElementLength( SPNEGO_NEGTARG_MAXLEN_NEGRESULT, NULL ); + + // Check for rollover error + if ( nTempLength < nTotalLength ) + { + goto xEndTokenTargLength; + } + + nTotalLength = nTempLength; + + } // IF negResult is available + + // Following two fields are the basic header tokens + + // Sequence Token + nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L ); + + // Check for rollover error + if ( nTempLength < nTotalLength ) + { + goto xEndTokenTargLength; + } + + nTotalLength = nTempLength; + + // Neg Token Identifier Token + nTempLength += ASNDerCalcTokenLength( nTotalLength, 0L ); + + // Check for rollover error + if ( nTempLength < nTotalLength ) + { + goto xEndTokenTargLength; + } + + // The internal length doesn't include the number of bytes + // for the initial token + *pnInternalTokenLength = nTotalLength; + nTotalLength = nTempLength; + + // We're done + *pnTokenSize = nTotalLength; + nReturn = SPNEGO_E_SUCCESS; + +xEndTokenTargLength: + + return nReturn; + +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// CreateSpnegoTargToken +// +// Parameters: +// [in] MechType - Supported MechType +// [in] eNegResult - NegResult value +// [in] pbMechToken - Mech Token Binary Data +// [in] ulMechTokenLen - Length of Mech Token +// [in] pbMechListMIC - MechListMIC Binary Data +// [in] ulMechListMICn - Length of MechListMIC +// [out] pbTokenData - Buffer to write token into. +// [in] nTokenLength - Length of pbTokenData buffer +// [in] nInternalTokenLength - Length of full token without leading +// token bytes. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Uses DER to fill out pbTokenData with a SPNEGO NegTokenTarg Token +// Note that because the lengths can be represented by an arbitrary +// number of bytes in DER encodings, we actually calculate the lengths +// backwards, so we always know how many bytes we will potentially be +// writing out. +// +//////////////////////////////////////////////////////////////////////////// + +int CreateSpnegoTargToken( SPNEGO_MECH_OID MechType, + SPNEGO_NEGRESULT eNegResult, unsigned char* pbMechToken, + unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, + unsigned long ulMechListMICLen, unsigned char* pbTokenData, + long nTokenLength, long nInternalTokenLength ) +{ + int nReturn = SPNEGO_E_INVALID_LENGTH; + + // Start at 0. + long nTempLength= 0L; + long nTotalBytesWritten = 0L; + long nInternalLength = 0L; + + unsigned char ucTemp = 0; + + // We will write the token out backwards to properly handle the cases + // where the length bytes become adjustable, so the write location + // is initialized to point *just* past the end of the buffer. + + unsigned char* pbWriteTokenData = pbTokenData + nTokenLength; + + + // Start with MIC Element + if ( ulMechListMICLen > 0L ) + { + nTempLength = ASNDerCalcElementLength( ulMechListMICLen, &nInternalLength ); + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC, + OCTETSTRING, pbMechListMIC, ulMechListMICLen ); + + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + nInternalTokenLength -= nTempLength; + + if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) + { + goto xEndWriteNegTokenTarg; + } + + } // IF MechListMIC is present + + // Next is the MechToken + if ( ulMechTokenLen > 0L ) + { + nTempLength = ASNDerCalcElementLength( ulMechTokenLen, &nInternalLength ); + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN, + OCTETSTRING, pbMechToken, ulMechTokenLen ); + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + nInternalTokenLength -= nTempLength; + + if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) + { + goto xEndWriteNegTokenTarg; + } + + } // IF MechToken Length is present + + // Supported Mech Type + if ( spnego_mech_oid_NotUsed != MechType ) + { + + nTempLength = ASNDerCalcElementLength( g_stcMechOIDList[MechType].iActualDataLen, + &nInternalLength ); + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH, + g_stcMechOIDList[MechType].ucOid, + g_stcMechOIDList[MechType].iLen ); + + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + nInternalTokenLength -= nTempLength; + + if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) + { + goto xEndWriteNegTokenTarg; + } + + } // IF MechType is present + + // Neg Result + // NegResult Element + if ( spnego_negresult_NotUsed != eNegResult ) + { + ucTemp = (unsigned char) eNegResult; + + nTempLength = ASNDerCalcElementLength( SPNEGO_NEGTARG_MAXLEN_NEGRESULT, &nInternalLength ); + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGTARG_ELEMENT_NEGRESULT, + ENUMERATED, &ucTemp, SPNEGO_NEGTARG_MAXLEN_NEGRESULT ); + + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + nInternalTokenLength -= nTempLength; + + if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) + { + goto xEndWriteNegTokenTarg; + } + + } // If eNegResult is available + + // The next tokens we're writing out reflect the total number of bytes + // we have actually written out. + + // Sequence Token + nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L ); + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_CONSTRUCTED_SEQUENCE, + NULL, nTotalBytesWritten ); + + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + nInternalTokenLength -= nTempLength; + + if ( nTotalBytesWritten > nTokenLength || nInternalTokenLength < 0 ) + { + goto xEndWriteNegTokenTarg; + } + + // Neg Targ Token Identifier Token + nTempLength = ASNDerCalcTokenLength( nTotalBytesWritten, 0L ); + + // Decrease the pbWriteTokenData, now we know the length and + // write it out. + pbWriteTokenData -= nTempLength; + nTempLength = ASNDerWriteToken( pbWriteTokenData, SPNEGO_NEGTARG_TOKEN_IDENTIFIER, + NULL, nTotalBytesWritten ); + + // Adjust Values and sanity check + nTotalBytesWritten += nTempLength; + + // Don't adjust the internal token length here, it doesn't account + // the initial bytes written out (we really don't need to keep + // a running count here, but for debugging, it helps to be able + // to see the total number of bytes written out as well as the + // number of bytes left to write). + + if ( nTotalBytesWritten == nTokenLength && nInternalTokenLength == 0 && + pbWriteTokenData == pbTokenData ) + { + nReturn = SPNEGO_E_SUCCESS; + } + + +xEndWriteNegTokenTarg: + + return nReturn; + + +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// AllocEmptySpnegoToken +// +// Parameters: +// [in] ucCopyData - Flag to copy data or pointer. +// [in] ulFlags - Flags for SPNEGO_TOKEN data member. +// [in] pbTokenData - Binary token data. +// [in] ulTokenSize - Size of pbTokenData. +// +// Returns: +// SPNEGO_TOKEN* Success - Pointer to initialized SPNEGO_TOKEN struct +// Failure - NULL +// +// Comments : +// Allocates a SPNEGO_TOKEN data structure and initializes it. Based on +// the value of ucCopyData, if non-zero, we copy the data into a buffer +// we allocate in this function, otherwise, we copy the data pointer +// direcly. +// +//////////////////////////////////////////////////////////////////////////// + +SPNEGO_TOKEN* AllocEmptySpnegoToken( unsigned char ucCopyData, unsigned long ulFlags, + unsigned char * pbTokenData, unsigned long ulTokenSize ) +{ + SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*) calloc( 1, sizeof(SPNEGO_TOKEN) ); + + if ( NULL != pSpnegoToken ) + { + // Set the token size + pSpnegoToken->nStructSize = SPNEGO_TOKEN_SIZE; + + // Initialize the element array + InitSpnegoTokenElementArray( pSpnegoToken ); + + // Assign the flags value + pSpnegoToken->ulFlags = ulFlags; + + // + // IF ucCopyData is TRUE, we will allocate a buffer and copy data into it. + // Otherwise, we will just copy the pointer and the length. This is so we + // can cut out additional allocations for performance reasons + // + + if ( SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA == ucCopyData ) + { + // Alloc the internal buffer. Cleanup on failure. + pSpnegoToken->pbBinaryData = (unsigned char*) calloc( ulTokenSize, sizeof(unsigned char) ); + + if ( NULL != pSpnegoToken->pbBinaryData ) + { + // We must ALWAYS free this buffer + pSpnegoToken->ulFlags |= SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA; + + // Copy the data locally + memcpy( pSpnegoToken->pbBinaryData, pbTokenData, ulTokenSize ); + pSpnegoToken->ulBinaryDataLen = ulTokenSize; + } + else + { + free( pSpnegoToken ); + pSpnegoToken = NULL; + } + + } // IF ucCopyData + else + { + // Copy the pointer and the length directly - ulFlags will control whether or not + // we are allowed to free the value + + pSpnegoToken->pbBinaryData = pbTokenData; + pSpnegoToken->ulBinaryDataLen = ulTokenSize; + } + + } + + return pSpnegoToken; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// FreeSpnegoToken +// +// Parameters: +// [in] pSpnegoToken - Points to SPNEGO_TOKEN to free. +// +// Returns: +// void +// +// Comments : +// If non-NULL, interprets pSpnegoToken, freeing any internal allocations +// and finally the actual structure. +// +//////////////////////////////////////////////////////////////////////////// + +void FreeSpnegoToken( SPNEGO_TOKEN* pSpnegoToken ) +{ + if ( NULL != pSpnegoToken ) + { + + // Cleanup internal allocation per the flags + if ( pSpnegoToken->ulFlags & SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA && + NULL != pSpnegoToken->pbBinaryData ) + { + free( pSpnegoToken->pbBinaryData ); + pSpnegoToken->pbBinaryData = NULL; + } + + free ( pSpnegoToken ); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// InitSpnegoTokenElementArray +// +// Parameters: +// [in] pSpnegoToken - Points to SPNEGO_TOKEN structure. +// +// Returns: +// void +// +// Comments : +// Initializes the element array data member of a SPNEGO_TOKEN data +// structure. +// +//////////////////////////////////////////////////////////////////////////// + +void InitSpnegoTokenElementArray( SPNEGO_TOKEN* pSpnegoToken ) +{ + int nCtr; + + // Set the number of elemnts + pSpnegoToken->nNumElements = MAX_NUM_TOKEN_ELEMENTS; + + // + // Initially, all elements are unavailable + // + + for ( nCtr = 0; nCtr < MAX_NUM_TOKEN_ELEMENTS; nCtr++ ) + { + // Set the element size as well + pSpnegoToken->aElementArray[ nCtr ].nStructSize = SPNEGO_ELEMENT_SIZE; + pSpnegoToken->aElementArray[ nCtr ].iElementPresent = SPNEGO_TOKEN_ELEMENT_UNAVAILABLE; + } + +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// InitSpnegoTokenType +// +// Parameters: +// [in] pSpnegoToken - Points to SPNEGO_TOKEN structure. +// [out] pnTokenLength - Filled out with total token length +// [out] pnRemainingTokenLength - Filled out with remaining length +// after header is parsed +// [out] ppbFirstElement - Filled out with pointer to first +// element after header info. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Walks the underlying binary data for a SPNEGO_TOKEN data structure +// and determines the type of the underlying token based on token header +// information. +// +//////////////////////////////////////////////////////////////////////////// + +int InitSpnegoTokenType( SPNEGO_TOKEN* pSpnegoToken, long* pnTokenLength, + long* pnRemainingTokenLength, unsigned char** ppbFirstElement ) +{ + int nReturn = SPNEGO_E_INVALID_TOKEN; + long nActualTokenLength = 0L; + long nBoundaryLength = pSpnegoToken->ulBinaryDataLen; + unsigned char* pbTokenData = pSpnegoToken->pbBinaryData; + + // + // First byte MUST be either an APP_CONSTRUCT or the NEGTARG_TOKEN_TARG + // + + if ( SPNEGO_NEGINIT_APP_CONSTRUCT == *pbTokenData ) + { + // Validate the above token - this will tell us the actual length of the token + // per the encoding (minus the actual token bytes) + if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_NEGINIT_APP_CONSTRUCT, 0L, nBoundaryLength, + pnTokenLength, &nActualTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + // Initialize the remaining token length value. This will be used + // to tell the caller how much token there is left once we've parsed + // the header (they could calculate it from the other values, but this + // is a bit friendlier) + *pnRemainingTokenLength = *pnTokenLength; + + // Make adjustments to next token + pbTokenData += nActualTokenLength; + nBoundaryLength -= nActualTokenLength; + + // The next token should be an OID + if ( ( nReturn = ASNDerCheckOID( pbTokenData, spnego_mech_oid_Spnego, nBoundaryLength, + &nActualTokenLength ) ) == SPNEGO_E_SUCCESS ) + { + // Make adjustments to next token + pbTokenData += nActualTokenLength; + nBoundaryLength -= nActualTokenLength; + *pnRemainingTokenLength -= nActualTokenLength; + + // The next token should specify the NegTokenInit + if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_NEGINIT_TOKEN_IDENTIFIER, + *pnRemainingTokenLength, nBoundaryLength, pnTokenLength, + &nActualTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + // Make adjustments to next token + pbTokenData += nActualTokenLength; + nBoundaryLength -= nActualTokenLength; + *pnRemainingTokenLength -= nActualTokenLength; + + // The next token should specify the start of a sequence + if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE, + *pnRemainingTokenLength, nBoundaryLength, pnTokenLength, + &nActualTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + // NegTokenInit header is now checked out! + + // Make adjustments to next token + *pnRemainingTokenLength -= nActualTokenLength; + + // Store pointer to first element + *ppbFirstElement = pbTokenData + nActualTokenLength; + pSpnegoToken->ucTokenType = SPNEGO_TOKEN_INIT; + } // IF Check Sequence Token + + } // IF Check NegTokenInit token + + + } // IF Check for SPNEGO OID + + + } // IF check app construct token + + } + else if ( SPNEGO_NEGTARG_TOKEN_IDENTIFIER == *pbTokenData ) + { + + // The next token should specify the NegTokenInit + if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_NEGTARG_TOKEN_IDENTIFIER, + *pnRemainingTokenLength, nBoundaryLength, pnTokenLength, + &nActualTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + // Initialize the remaining token length value. This will be used + // to tell the caller how much token there is left once we've parsed + // the header (they could calculate it from the other values, but this + // is a bit friendlier) + *pnRemainingTokenLength = *pnTokenLength; + + // Make adjustments to next token + pbTokenData += nActualTokenLength; + nBoundaryLength -= nActualTokenLength; + + // The next token should specify the start of a sequence + if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE, + *pnRemainingTokenLength, nBoundaryLength, pnTokenLength, + &nActualTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + // NegTokenInit header is now checked out! + + // Make adjustments to next token + *pnRemainingTokenLength -= nActualTokenLength; + + // Store pointer to first element + *ppbFirstElement = pbTokenData + nActualTokenLength; + pSpnegoToken->ucTokenType = SPNEGO_TOKEN_TARG; + } // IF Check Sequence Token + + } // IF Check NegTokenInit token + + } // ELSE IF it's a NegTokenTarg + + return nReturn; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// GetSpnegoInitTokenMechList +// +// Parameters: +// [in] pbTokenData - Points to binary MechList element +// in NegTokenInit. +// [in] nMechListLength - Length of the MechList +// [out] pSpnegoElement - Filled out with MechList Element +// data. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Checks that pbTokenData is pointing at something that at least +// *looks* like a MechList and then fills out the supplied +// SPNEGO_ELEMENT structure. +// +//////////////////////////////////////////////////////////////////////////// + +int GetSpnegoInitTokenMechList( unsigned char* pbTokenData, int nMechListLength, + SPNEGO_ELEMENT* pSpnegoElement ) +{ + int nReturn = SPNEGO_E_INVALID_TOKEN; + long nLength = 0L; + long nActualTokenLength = 0L; + + // Actual MechList is prepended by a Constructed Sequence Token + if ( ( nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE, + nMechListLength, nMechListLength, + &nLength, &nActualTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + // Adjust for this token + nMechListLength -= nActualTokenLength; + pbTokenData += nActualTokenLength; + + // Perform simple validation of the actual MechList (i.e. ensure that + // the OIDs in the MechList are reasonable). + + if ( ( nReturn = ValidateMechList( pbTokenData, nLength ) ) == SPNEGO_E_SUCCESS ) + { + // Initialize the element now + pSpnegoElement->eElementType = spnego_init_mechtypes; + pSpnegoElement->iElementPresent = SPNEGO_TOKEN_ELEMENT_AVAILABLE; + pSpnegoElement->type = SPNEGO_MECHLIST_TYPE; + pSpnegoElement->nDatalength = nLength; + pSpnegoElement->pbData = pbTokenData; + } + + } // IF Check Token + + return nReturn; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// InitSpnegoTokenElementFromBasicType +// +// Parameters: +// [in] pbTokenData - Points to binary element data in +// a SPNEGO token. +// [in] nElementLength - Length of the element +// [in] ucExpectedType - Expected DER type. +// [in] spnegoElementType - Which element is this? +// [out] pSpnegoElement - Filled out with element data. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Checks that pbTokenData is pointing at the specified DER type. If so, +// then we verify that lengths are proper and then fill out the +// SPNEGO_ELEMENT data structure. +// +//////////////////////////////////////////////////////////////////////////// + +int InitSpnegoTokenElementFromBasicType( unsigned char* pbTokenData, int nElementLength, + unsigned char ucExpectedType, + SPNEGO_ELEMENT_TYPE spnegoElementType, + SPNEGO_ELEMENT* pSpnegoElement ) +{ + int nReturn = SPNEGO_E_UNEXPECTED_TYPE; + long nLength = 0L; + long nActualTokenLength = 0L; + + // The type BYTE must match our token data or something is badly wrong + if ( *pbTokenData == ucExpectedType ) + { + + // Check that we are pointing at the specified type + if ( ( nReturn = ASNDerCheckToken( pbTokenData, ucExpectedType, + nElementLength, nElementLength, + &nLength, &nActualTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + // Adjust for this token + nElementLength -= nActualTokenLength; + pbTokenData += nActualTokenLength; + + // Initialize the element now + pSpnegoElement->eElementType = spnegoElementType; + pSpnegoElement->iElementPresent = SPNEGO_TOKEN_ELEMENT_AVAILABLE; + pSpnegoElement->type = ucExpectedType; + pSpnegoElement->nDatalength = nLength; + pSpnegoElement->pbData = pbTokenData; + } + + } // IF type makes sense + + return nReturn; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// InitSpnegoTokenElementFromOID +// +// Parameters: +// [in] pbTokenData - Points to binary element data in +// a SPNEGO token. +// [in] nElementLength - Length of the element +// [in] spnegoElementType - Which element is this? +// [out] pSpnegoElement - Filled out with element data. +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Initializes a SpnegoElement from an OID - normally, this would have +// used the Basic Type function above, but since we do binary compares +// on the OIDs against the DER information as well as the OID, we need +// to account for that. +// +//////////////////////////////////////////////////////////////////////////// + +int InitSpnegoTokenElementFromOID( unsigned char* pbTokenData, int nElementLength, + SPNEGO_ELEMENT_TYPE spnegoElementType, + SPNEGO_ELEMENT* pSpnegoElement ) +{ + int nReturn = SPNEGO_E_UNEXPECTED_TYPE; + long nLength = 0L; + long nActualTokenLength = 0L; + + // The type BYTE must match our token data or something is badly wrong + if ( *pbTokenData == OID ) + { + + // Check that we are pointing at an OID type + if ( ( nReturn = ASNDerCheckToken( pbTokenData, OID, + nElementLength, nElementLength, + &nLength, &nActualTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + // Don't adjust any values for this function + + // Initialize the element now + pSpnegoElement->eElementType = spnegoElementType; + pSpnegoElement->iElementPresent = SPNEGO_TOKEN_ELEMENT_AVAILABLE; + pSpnegoElement->type = OID; + pSpnegoElement->nDatalength = nElementLength; + pSpnegoElement->pbData = pbTokenData; + } + + } // IF type makes sense + + return nReturn; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// InitSpnegoTokenElements +// +// Parameters: +// [in] pSpnegoToken - Points to SPNEGO_TOKEN struct +// [in] pbTokenData - Points to initial binary element +// data in a SPNEGO token. +// [in] nRemainingTokenLength - Length remaining past header +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Interprets the data at pbTokenData based on the TokenType in +// pSpnegoToken. Since some elements are optional (technically all are +// but the token becomes quite useless if this is so), we check if +// an element exists before filling out the element in the array. +// +//////////////////////////////////////////////////////////////////////////// + +int InitSpnegoTokenElements( SPNEGO_TOKEN* pSpnegoToken, unsigned char* pbTokenData, + long nRemainingTokenLength ) +{ + // + // The following arrays contain the token identifiers for the elements + // comprising the actual token. All values are optional, and there are + // no defaults. + // + + static unsigned char abNegTokenInitElements[] = + { SPNEGO_NEGINIT_ELEMENT_MECHTYPES, SPNEGO_NEGINIT_ELEMENT_REQFLAGS, + SPNEGO_NEGINIT_ELEMENT_MECHTOKEN, SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC }; + + static unsigned char abNegTokenTargElements[] = + { SPNEGO_NEGTARG_ELEMENT_NEGRESULT, SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH, + SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN, SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC }; + + int nReturn = SPNEGO_E_SUCCESS; + int nCtr = 0L; + long nElementLength = 0L; + long nActualTokenLength = 0L; + unsigned char* pbElements = NULL; + unsigned char * ptok; + long tlen, elen, len; + + // Point to the correct array + switch( pSpnegoToken->ucTokenType ) + { + case SPNEGO_TOKEN_INIT: + { + pbElements = abNegTokenInitElements; + } + break; + + case SPNEGO_TOKEN_TARG: + { + pbElements = abNegTokenTargElements; + } + break; + + } // SWITCH tokentype + + // + // Enumerate the element arrays and look for the tokens at our current location + // + + for ( nCtr = 0L; + SPNEGO_E_SUCCESS == nReturn && + nCtr < MAX_NUM_TOKEN_ELEMENTS && + nRemainingTokenLength > 0L; + nCtr++ ) + { + + // Check if the token exists + if ( ( nReturn = ASNDerCheckToken( pbTokenData, pbElements[nCtr], + 0L, nRemainingTokenLength, + &nElementLength, &nActualTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + + // Token data should skip over the sequence token and then + // call the appropriate function to initialize the element + pbTokenData += nActualTokenLength; + + // Lengths in the elements should NOT go beyond the element + // length + + // Different tokens mean different elements + if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) + { + + // Handle each element as appropriate + switch( pbElements[nCtr] ) + { + + case SPNEGO_NEGINIT_ELEMENT_MECHTYPES: + { + // + // This is a Mech List that specifies which OIDs the + // originator of the Init Token supports. + // + + nReturn = GetSpnegoInitTokenMechList( pbTokenData, nElementLength, + &pSpnegoToken->aElementArray[nCtr] ); + + } + break; + + case SPNEGO_NEGINIT_ELEMENT_REQFLAGS: + { + // + // This is a BITSTRING which specifies the flags that the receiver + // pass to the gss_accept_sec_context() function. + // + + nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, + BITSTRING, spnego_init_reqFlags, + &pSpnegoToken->aElementArray[nCtr] ); + } + break; + + case SPNEGO_NEGINIT_ELEMENT_MECHTOKEN: + { + // + // This is an OCTETSTRING which contains a GSSAPI token corresponding + // to the first OID in the MechList. + // + + nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, + OCTETSTRING, spnego_init_mechToken, + &pSpnegoToken->aElementArray[nCtr] ); + } + break; + + case SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC: + { + // + // This is an OCTETSTRING which contains a message integrity BLOB. + // + + nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, + OCTETSTRING, spnego_init_mechListMIC, + &pSpnegoToken->aElementArray[nCtr] ); + /* + * don't believe everything you read in RFCs (and MS + * sample code)... win2k is sending not an octet string, + * but a "general string", wrapped in a sequence. + */ + if (nReturn != SPNEGO_E_UNEXPECTED_TYPE) + break; + ptok = pbTokenData; + elen = nElementLength; + if ((nReturn = ASNDerCheckToken(ptok, SPNEGO_CONSTRUCTED_SEQUENCE, elen, elen, &len, &tlen)) != SPNEGO_E_SUCCESS) + break; + elen -= tlen; + ptok += tlen; + + if ((nReturn = ASNDerCheckToken(ptok, SEQ_ELM(0), elen, elen, &len, &tlen)) != SPNEGO_E_SUCCESS) + break; + elen -= tlen; + ptok += tlen; + nReturn = InitSpnegoTokenElementFromBasicType(ptok, elen, GENERALSTR, spnego_init_mechListMIC, &pSpnegoToken->aElementArray[nCtr]); + } + break; + + } // SWITCH Element + } + else + { + + switch( pbElements[nCtr] ) + { + + case SPNEGO_NEGTARG_ELEMENT_NEGRESULT: + { + // + // This is an ENUMERATION which specifies result of the last GSS + // token negotiation call. + // + + nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, + ENUMERATED, spnego_targ_negResult, + &pSpnegoToken->aElementArray[nCtr] ); + } + break; + + case SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH: + { + // + // This is an OID which specifies a supported mechanism. + // + + nReturn = InitSpnegoTokenElementFromOID( pbTokenData, nElementLength, + spnego_targ_mechListMIC, + &pSpnegoToken->aElementArray[nCtr] ); + } + break; + + case SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN: + { + // + // This is an OCTETSTRING which specifies results of the last GSS + // token negotiation call. + // + + nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, + OCTETSTRING, spnego_targ_responseToken, + &pSpnegoToken->aElementArray[nCtr] ); + } + break; + + case SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC: + { + // + // This is an OCTETSTRING which specifies a message integrity BLOB. + // + + nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, + OCTETSTRING, spnego_targ_mechListMIC, + &pSpnegoToken->aElementArray[nCtr] ); + } + break; + + } // SWITCH Element + + } // ELSE !NegTokenInit + + // Account for the entire token and following data + nRemainingTokenLength -= ( nActualTokenLength + nElementLength ); + + // Token data should skip past the element length now + pbTokenData += nElementLength; + + } // IF Token found + else if ( SPNEGO_E_TOKEN_NOT_FOUND == nReturn ) + { + // For now, this is a benign error (remember, all elements are optional, so + // if we don't find one, it's okay). + + nReturn = SPNEGO_E_SUCCESS; + } + + } // FOR enum elements + + // + // We should always run down to 0 remaining bytes in the token. If not, we've got + // a bad token. + // + + if ( SPNEGO_E_SUCCESS == nReturn && nRemainingTokenLength != 0L ) + { + nReturn = SPNEGO_E_INVALID_TOKEN; + } + + return nReturn; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// FindMechOIDInMechList +// +// Parameters: +// [in] pSpnegoElement - SPNEGO_ELEMENT for MechList +// [in] MechOID - OID we're looking for. +// [out] piMechTypeIndex - Index in the list where OID was +// found +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Walks the MechList for MechOID. When it is found, the index in the +// list is written to piMechTypeIndex. +// +//////////////////////////////////////////////////////////////////////////// + +int FindMechOIDInMechList( SPNEGO_ELEMENT* pSpnegoElement, SPNEGO_MECH_OID MechOID, + int * piMechTypeIndex ) +{ + int nReturn = SPNEGO_E_NOT_FOUND; + int nCtr = 0; + long nLength = 0L; + long nBoundaryLength = pSpnegoElement->nDatalength; + unsigned char* pbMechListData = pSpnegoElement->pbData; + + while( SPNEGO_E_SUCCESS != nReturn && nBoundaryLength > 0L ) + { + + // Use the helper function to check the OID + if ( ( nReturn = ASNDerCheckOID( pbMechListData, MechOID, nBoundaryLength, &nLength ) ) + == SPNEGO_E_SUCCESS ) + { + *piMechTypeIndex = nCtr; + } + + // Adjust for the current OID + pbMechListData += nLength; + nBoundaryLength -= nLength; + nCtr++; + + } // WHILE enuming OIDs + + return nReturn; + +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// ValidateMechList +// +// Parameters: +// [in] pbMechListData - Pointer to binary MechList data +// [in] nBoundaryLength - Length we must not exceed +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Checks the data at pbMechListData to see if it looks like a MechList. +// As part of this, we walk the list and ensure that none of the OIDs +// have a length that takes us outside of nBoundaryLength. +// +//////////////////////////////////////////////////////////////////////////// + +int ValidateMechList( unsigned char* pbMechListData, long nBoundaryLength ) +{ + int nReturn = SPNEGO_E_SUCCESS; + long nLength = 0L; + long nTokenLength = 0L; + + while( SPNEGO_E_SUCCESS == nReturn && nBoundaryLength > 0L ) + { + // Verify that we have something that at least *looks* like an OID - in other + // words it has an OID identifier and specifies a length that doesn't go beyond + // the size of the list. + nReturn = ASNDerCheckToken( pbMechListData, OID, 0L, nBoundaryLength, + &nLength, &nTokenLength ); + + // Adjust for the current OID + pbMechListData += ( nLength + nTokenLength ); + nBoundaryLength -= ( nLength + nTokenLength ); + + } // WHILE enuming OIDs + + return nReturn; + +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// IsValidMechOid +// +// Parameters: +// [in] mechOid - mechOID id enumeration +// +// Returns: +// int Success - 1 +// Failure - 0 +// +// Comments : +// Checks for a valid mechOid value. +// +//////////////////////////////////////////////////////////////////////////// + +int IsValidMechOid( SPNEGO_MECH_OID mechOid ) +{ + return ( mechOid >= spnego_mech_oid_Kerberos_V5_Legacy && + mechOid <= spnego_mech_oid_Spnego ); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// IsValidContextFlags +// +// Parameters: +// [in] ucContextFlags - ContextFlags value +// +// Returns: +// int Success - 1 +// Failure - 0 +// +// Comments : +// Checks for a valid ContextFlags value. +// +//////////////////////////////////////////////////////////////////////////// + +int IsValidContextFlags( unsigned char ucContextFlags ) +{ + // Mask out our valid bits. If there is anything leftover, this + // is not a valid value for Context Flags + return ( ( ucContextFlags & ~SPNEGO_NEGINIT_CONTEXT_MASK ) == 0 ); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// IsValidNegResult +// +// Parameters: +// [in] negResult - NegResult value +// +// Returns: +// int Success - 1 +// Failure - 0 +// +// Comments : +// Checks for a valid NegResult value. +// +//////////////////////////////////////////////////////////////////////////// + +int IsValidNegResult( SPNEGO_NEGRESULT negResult ) +{ + return ( negResult >= spnego_negresult_success && + negResult <= spnego_negresult_rejected ); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// IsValidSpnegoToken +// +// Parameters: +// [in] pSpnegoToken - Points to SPNEGO_TOKEN data structure +// +// Returns: +// int Success - 1 +// Failure - 0 +// +// Comments : +// Performs simple heuristic on location pointed to by pSpnegoToken. +// +//////////////////////////////////////////////////////////////////////////// + +int IsValidSpnegoToken( SPNEGO_TOKEN* pSpnegoToken ) +{ + int nReturn = 0; + + // Parameter should be non-NULL + if ( NULL != pSpnegoToken ) + { + // Length should be at least the size defined in the header + if ( pSpnegoToken->nStructSize >= SPNEGO_TOKEN_SIZE ) + { + // Number of elements should be >= our maximum - if it's greater, that's + // okay, since we'll only be accessing the elements up to MAX_NUM_TOKEN_ELEMENTS + if ( pSpnegoToken->nNumElements >= MAX_NUM_TOKEN_ELEMENTS ) + { + // Check for proper token type + if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType || + SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType ) + { + nReturn = 1; + } + } + + } // IF struct size makes sense + + } // IF non-NULL spnego Token + + return nReturn; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// IsValidSpnegoElement +// +// Parameters: +// [in] pSpnegoToken - Points to SPNEGO_TOKEN data structure +// [in] spnegoElement - spnegoElement Type from enumeration +// +// Returns: +// int Success - 1 +// Failure - 0 +// +// Comments : +// Checks that spnegoElement has a valid value and is appropriate for +// the SPNEGO token encapsulated by pSpnegoToken. +// +//////////////////////////////////////////////////////////////////////////// + +int IsValidSpnegoElement( SPNEGO_TOKEN* pSpnegoToken,SPNEGO_ELEMENT_TYPE spnegoElement ) +{ + int nReturn = 0; + + // Check boundaries + if ( spnegoElement > spnego_element_min && + spnegoElement < spnego_element_max ) + { + + // Check for appropriateness to token type + if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) + { + nReturn = ( spnegoElement >= spnego_init_mechtypes && + spnegoElement <= spnego_init_mechListMIC ); + } + else + { + nReturn = ( spnegoElement >= spnego_targ_negResult && + spnegoElement <= spnego_targ_mechListMIC ); + } + + } // IF boundary conditions are met + + return nReturn; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// CalculateElementArrayIndex +// +// Parameters: +// [in] pSpnegoToken - Points to SPNEGO_TOKEN data structure +// [in] spnegoElement - spnegoElement Type from enumeration +// +// Returns: +// int index in the SPNEGO_TOKEN element array that the element can +// can be found +// +// Comments : +// Based on the Token Type, calculates the index in the element array +// at which the specified element can be found. +// +//////////////////////////////////////////////////////////////////////////// + +int CalculateElementArrayIndex( SPNEGO_TOKEN* pSpnegoToken,SPNEGO_ELEMENT_TYPE spnegoElement ) +{ + int nReturn = 0; + + // Offset is difference between value and initial element identifier + // (these differ based on ucTokenType) + + if ( SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ) + { + nReturn = spnegoElement - spnego_init_mechtypes; + } + else + { + nReturn = spnegoElement - spnego_targ_negResult; + } + + return nReturn; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: +// InitTokenFromBinary +// +// Parameters: +// [in] ucCopyData - Flag indicating if data should be copied +// [in] ulFlags - Flags value for structure +// [in] pnTokenData - Binary Token Data +// [in] ulLength - Length of the data +// [out] ppSpnegoToken - Pointer to call allocated SPNEGO Token +// data structure +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Allocates a SPNEGO_TOKEN data structure and fills it out as +// appropriate based in the flags passed into the function. +// +//////////////////////////////////////////////////////////////////////////// + + +// Initializes SPNEGO_TOKEN structure from DER encoded binary data +int InitTokenFromBinary( unsigned char ucCopyData, unsigned long ulFlags, + unsigned char* pbTokenData, unsigned long ulLength, + SPNEGO_TOKEN** ppSpnegoToken ) +{ + int nReturn = SPNEGO_E_INVALID_PARAMETER; + SPNEGO_TOKEN* pSpnegoToken = NULL; + unsigned char* pbFirstElement = NULL; + long nTokenLength = 0L; + long nRemainingTokenLength = 0L; + + // Basic Parameter Validation + + if ( NULL != pbTokenData && + NULL != ppSpnegoToken && + 0L != ulLength ) + { + + // + // Allocate the empty token, then initialize the data structure. + // + + pSpnegoToken = AllocEmptySpnegoToken( ucCopyData, ulFlags, pbTokenData, ulLength ); + + if ( NULL != pSpnegoToken ) + { + + // Copy the binary data locally + + + // Initialize the token type + if ( ( nReturn = InitSpnegoTokenType( pSpnegoToken, &nTokenLength, + &nRemainingTokenLength, &pbFirstElement ) ) + == SPNEGO_E_SUCCESS ) + { + + // Initialize the element array + if ( ( nReturn = InitSpnegoTokenElements( pSpnegoToken, pbFirstElement, + nRemainingTokenLength ) ) + == SPNEGO_E_SUCCESS ) + { + *ppSpnegoToken = pSpnegoToken; + } + + } // IF Init Token Type + + // Cleanup on error condition + if ( SPNEGO_E_SUCCESS != nReturn ) + { + spnegoFreeData( pSpnegoToken ); + } + + } + else + { + nReturn = SPNEGO_E_OUT_OF_MEMORY; + } + + } // IF Valid parameters + + + return nReturn; +} diff --git a/usr/src/lib/libsmbfs/smb/spnegoparse.h b/usr/src/lib/libsmbfs/smb/spnegoparse.h new file mode 100644 index 0000000000..b874dc453d --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/spnegoparse.h @@ -0,0 +1,170 @@ +// Copyright (C) 2002 Microsoft Corporation +// All rights reserved. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" +// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +// OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY +// AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Date - 10/08/2002 +// Author - Sanj Surati + +///////////////////////////////////////////////////////////// +// +// SPNEGOPARSE.H +// +// SPNEGO Token Parser Header File +// +// Contains the definitions required to properly parse a +// SPNEGO token using ASN.1 DER helpers. +// +///////////////////////////////////////////////////////////// + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef __SPNEGOPARSE_H__ +#define __SPNEGOPARSE_H__ + +// C++ Specific +#if defined(__cplusplus) +extern "C" +{ +#endif + +// Indicates if we copy data when creating a SPNEGO_TOKEN structure or not +#define SPNEGO_TOKEN_INTERNAL_COPYPTR 0 +#define SPNEGO_TOKEN_INTERNAL_COPYDATA 0x1 + +// Internal flag dictates whether or not we will free the binary data when +// the SPNEG_TOKEN structure is destroyed +#define SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA 0x1 + + // +// Each SPNEGO Token Type can be broken down into a +// maximum of 4 separate elements. +// + +#define MAX_NUM_TOKEN_ELEMENTS 4 + +// +// Element offsets in the array +// + +// INIT elements +#define SPNEGO_INIT_MECHTYPES_ELEMENT 0 +#define SPNEGO_INIT_REQFLAGS_ELEMENT 1 +#define SPNEGO_INIT_MECHTOKEN_ELEMENT 2 +#define SPNEGO_INIT_MECHLISTMIC_ELEMENT 3 + +// Response elements +#define SPNEGO_TARG_NEGRESULT_ELEMENT 0 +#define SPNEGO_TARG_SUPPMECH_ELEMENT 1 +#define SPNEGO_TARG_RESPTOKEN_ELEMENT 2 +#define SPNEGO_TARG_MECHLISTMIC_ELEMENT 3 + +// +// Defines an individual SPNEGO Token Element. +// + +typedef struct SpnegoElement +{ + size_t nStructSize; // Size of the element structure + int iElementPresent; // Is the field present? Must be either + // SPNEGO_TOKEN_ELEMENT_UNAVAILABLE or + // SPNEGO_TOKEN_ELEMENT_AVAILABLE + + SPNEGO_ELEMENT_TYPE eElementType; // The Element Type + + unsigned char type; // Data Type + + unsigned char* pbData; // Points to actual Data + + unsigned long nDatalength; // Actual Data Length + +} SPNEGO_ELEMENT; + +// Structure size in case we later choose to extend the structure +#define SPNEGO_ELEMENT_SIZE sizeof(SPNEGO_ELEMENT) + +// +// Packages a SPNEGO Token Encoding. There are two types of +// encodings: NegTokenInit and NegTokenTarg. Each encoding can +// contain up to four distinct, optional elements. +// + +typedef struct SpnegoToken +{ + size_t nStructSize; // Size of the Token structure + unsigned long ulFlags; // Internal Structure Flags - Reserved! + int ucTokenType; // Token Type - Must be + // SPNEGO_TOKEN_INIT or + // SPNEGO_TOKEN_TARG + + unsigned char* pbBinaryData; // Points to binary token data + + unsigned long ulBinaryDataLen; // Length of the actual binary data + int nNumElements; // Number of elements + SPNEGO_ELEMENT aElementArray [MAX_NUM_TOKEN_ELEMENTS]; // Holds the elements for the token +} SPNEGO_TOKEN; + +// Structure size in case we later choose to extend the structure +#define SPNEGO_TOKEN_SIZE sizeof(SPNEGO_TOKEN) + +// +// Function definitions +// + +SPNEGO_TOKEN* AllocEmptySpnegoToken( unsigned char ucCopyData, unsigned long ulFlags, + unsigned char * pbTokenData, unsigned long ulTokenSize ); +void FreeSpnegoToken( SPNEGO_TOKEN* pSpnegoToken ); +void InitSpnegoTokenElementArray( SPNEGO_TOKEN* pSpnegoToken ); +int InitSpnegoTokenType( SPNEGO_TOKEN* pSpnegoToken, long* pnTokenLength, + long* pnRemainingTokenLength, unsigned char** ppbFirstElement ); +int InitSpnegoTokenElements( SPNEGO_TOKEN* pSpnegoToken, unsigned char* pbTokenData, + long nRemainingTokenLength ); +int GetSpnegoInitTokenMechList( unsigned char* pbTokenData, int nMechListLength, + SPNEGO_ELEMENT* pSpnegoElement ); +int InitSpnegoTokenElementFromBasicType( unsigned char* pbTokenData, int nElementLength, + unsigned char ucExpectedType, + SPNEGO_ELEMENT_TYPE spnegoElementType, + SPNEGO_ELEMENT* pSpnegoElement ); +int InitSpnegoTokenElementFromOID( unsigned char* pbTokenData, int nElementLength, + SPNEGO_ELEMENT_TYPE spnegoElementType, + SPNEGO_ELEMENT* pSpnegoElement ); +int FindMechOIDInMechList( SPNEGO_ELEMENT* pSpnegoElement, SPNEGO_MECH_OID MechOID, + int * piMechTypeIndex ); +int ValidateMechList( unsigned char* pbMechListData, long nBoundaryLength ); +int CalculateMinSpnegoInitTokenSize( long nMechTokenLength, long nMechListMICLength, + SPNEGO_MECH_OID mechOid, int nReqFlagsAvailable, + long* plTokenSize, long* plInternalLength ); +int CalculateMinSpnegoTargTokenSize( SPNEGO_MECH_OID MechType, SPNEGO_NEGRESULT spnegoNegResult, + long nMechTokenLen, + long nMechTokenMIC, long* pnTokenSize, + long* pnInternalTokenLength ); +int CreateSpnegoInitToken( SPNEGO_MECH_OID MechType, + unsigned char ucContextFlags, unsigned char* pbMechToken, + unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, + unsigned long ulMechListMICLen, unsigned char* pbTokenData, + long nTokenLength, long nInternalTokenLength ); +int CreateSpnegoTargToken( SPNEGO_MECH_OID MechType, + SPNEGO_NEGRESULT eNegResult, unsigned char* pbMechToken, + unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, + unsigned long ulMechListMICLen, unsigned char* pbTokenData, + long nTokenLength, long nInternalTokenLength ); +int IsValidMechOid( SPNEGO_MECH_OID mechOid ); +int IsValidContextFlags( unsigned char ucContextFlags ); +int IsValidNegResult( SPNEGO_NEGRESULT negResult ); +int IsValidSpnegoToken( SPNEGO_TOKEN* pSpnegoToken ); +int IsValidSpnegoElement( SPNEGO_TOKEN* pSpnegoToken,SPNEGO_ELEMENT_TYPE spnegoElement ); +int CalculateElementArrayIndex( SPNEGO_TOKEN* pSpnegoToken,SPNEGO_ELEMENT_TYPE spnegoElement ); +int InitTokenFromBinary( unsigned char ucCopyData, unsigned long ulFlags, + unsigned char* pbTokenData, unsigned long ulLength, + SPNEGO_TOKEN** ppSpnegoToken ); + + // C++ Specific +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/usr/src/lib/libsmbfs/smb/subr.c b/usr/src/lib/libsmbfs/smb/subr.c new file mode 100644 index 0000000000..46842d730f --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/subr.c @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: subr.c,v 1.19 2005/02/09 00:23:45 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/syscall.h> +#include <sys/wait.h> +#include <sys/debug.h> + +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <sysexits.h> +#include <libintl.h> + +#include <netsmb/netbios.h> +#include <netsmb/smb_lib.h> +#include <netsmb/nb_lib.h> +#include <cflib.h> +#include <err.h> + +uid_t real_uid, eff_uid; + +static int smblib_initialized; + +struct rcfile *smb_rc; + +int +smb_lib_init(void) +{ + int error; + + if (smblib_initialized) + return (0); + if ((error = nls_setlocale("")) != 0) { + fprintf(stdout, dgettext(TEXT_DOMAIN, + "%s: can't initialise locale\n"), __progname); + return (error); + } + smblib_initialized++; + return (0); +} + +/* + * Private version of strerror(3C) that + * knows our special error codes. + */ +char * +smb_strerror(int err) +{ + char *msg; + + switch (err) { + case EBADRPC: + msg = dgettext(TEXT_DOMAIN, + "remote call failed"); + break; + case EAUTH: + msg = dgettext(TEXT_DOMAIN, + "authentication failed"); + break; + default: + msg = strerror(err); + break; + } + + return (msg); +} + +/* + * Print a (descriptive) error message + * error values: + * 0 - no specific error code available; + * 1..32767 - system error + */ +void +smb_error(const char *fmt, int error, ...) { + va_list ap; + const char *cp; + int errtype; + + fprintf(stderr, "%s: ", __progname); + va_start(ap, error); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (error == -1) { + error = errno; + errtype = SMB_SYS_ERROR; + } else { + errtype = error & SMB_ERRTYPE_MASK; + error &= ~SMB_ERRTYPE_MASK; + } + switch (errtype) { + case SMB_SYS_ERROR: + if (error) + fprintf(stderr, ": syserr = %s\n", smb_strerror(error)); + else + fprintf(stderr, "\n"); + break; + case SMB_RAP_ERROR: + fprintf(stderr, ": raperr = %d (0x%04x)\n", error, error); + break; + case SMB_NB_ERROR: + cp = nb_strerror(error); + if (cp == NULL) + fprintf(stderr, ": nberr = unknown (0x%04x)\n", error); + else + fprintf(stderr, ": nberr = %s\n", cp); + break; + default: + fprintf(stderr, "\n"); + } +} + +char * +smb_printb(char *dest, int flags, const struct smb_bitname *bnp) { + int first = 1; + + strcpy(dest, "<"); + for (; bnp->bn_bit; bnp++) { + if (flags & bnp->bn_bit) { + strcat(dest, bnp->bn_name); + first = 0; + } + if (!first && (flags & bnp[1].bn_bit)) + strcat(dest, "|"); + } + strcat(dest, ">"); + return (dest); +} + +extern int home_nsmbrc; + +#ifdef DEBUG +#include "queue.h" +#include "rcfile_priv.h" + +struct rcsection *rc_findsect(struct rcfile *rcp, const char *sectname); +struct rckey *rc_sect_findkey(struct rcsection *rsp, const char *keyname); + +void +dump_props(char *where) +{ + struct rcsection *rsp = NULL; + struct rckey *rkp = NULL; + + printf("Settings %s\n", where); + SLIST_FOREACH(rsp, &smb_rc->rf_sect, rs_next) { + printf("section=%s\n", rsp->rs_name); + fflush(stdout); + + SLIST_FOREACH(rkp, &rsp->rs_keys, rk_next) { + printf(" key=%s, value=%s\n", + rkp->rk_name, rkp->rk_value); + fflush(stdout); + } + } +} +#endif + +/* + * first read ~/.smbrc, next try to merge SMB_CFG_FILE - if that fails + * because SMB_CFG_FILE doesn't exist, try to merge OLD_SMB_CFG_FILE + */ +int +smb_open_rcfile(struct smb_ctx *ctx) +{ + char *home, *fn; + int error, len; + + smb_rc = NULL; +#ifdef DEPRECATED + fn = SMB_CFG_FILE; + error = rc_merge(fn, &smb_rc); + if (error == ENOENT) { + /* + * OK, try to read a config file in the old location. + */ + fn = OLD_SMB_CFG_FILE; + error = rc_merge(fn, &smb_rc); + } +#endif + fn = "/usr/sbin/sharectl get smbfs"; + error = rc_merge_pipe(fn, &smb_rc); + if (error != 0 && error != ENOENT) + fprintf(stderr, dgettext(TEXT_DOMAIN, + "Can't open %s: %s\n"), fn, smb_strerror(errno)); +#ifdef DEBUG + dump_props("after reading global repository"); +#endif + + home = getenv("HOME"); + if (home == NULL && ctx && ctx->ct_home) + home = ctx->ct_home; + if (home) { + len = strlen(home) + 20; + fn = malloc(len); + snprintf(fn, len, "%s/.nsmbrc", home); + home_nsmbrc = 1; + error = rc_merge(fn, &smb_rc); + if (error != 0 && error != ENOENT) { + fprintf(stderr, dgettext(TEXT_DOMAIN, + "Can't open %s: %s\n"), fn, smb_strerror(errno)); + } + free(fn); + } + home_nsmbrc = 0; +#ifdef DEBUG + dump_props("after reading user settings"); +#endif + if (smb_rc == NULL) { + return (ENOENT); + } + return (0); +} + +void +smb_simplecrypt(char *dst, const char *src) +{ + int ch, pos; + + *dst++ = '$'; + *dst++ = '$'; + *dst++ = '1'; + pos = 27; + while (*src) { + ch = *src++; + if (isascii(ch)) + ch = (isupper(ch) ? ('A' + (ch - 'A' + 13) % 26) : + islower(ch) ? ('a' + (ch - 'a' + 13) % 26) : ch); + ch ^= pos; + pos += 13; + sprintf(dst, "%02x", ch); + dst += 2; + } + *dst = 0; +} + +int +smb_simpledecrypt(char *dst, const char *src) +{ + char *ep, hexval[3]; + int len, ch, pos; + + if (strncmp(src, "$$1", 3) != 0) + return (EINVAL); + src += 3; + len = strlen(src); + if (len & 1) + return (EINVAL); + len /= 2; + hexval[2] = 0; + pos = 27; + while (len--) { + hexval[0] = *src++; + hexval[1] = *src++; + ch = strtoul(hexval, &ep, 16); + if (*ep != 0) + return (EINVAL); + ch ^= pos; + pos += 13; + if (isascii(ch)) + ch = (isupper(ch) ? ('A' + (ch - 'A' + 13) % 26) : + islower(ch) ? ('a' + (ch - 'a' + 13) % 26) : ch); + *dst++ = ch; + } + *dst = 0; + return (0); +} + + +static int +safe_execv(char *args[]) +{ + int pid; + int status; + + pid = fork(); + if (pid == 0) { + (void) execv(args[0], args); + /* Changed from errx() to fprintf(stderr) -Pavan */ + fprintf(stderr, dgettext(TEXT_DOMAIN, + "%s: execv %s failed, %s\n"), __progname, + args[0], smb_strerror(errno)); + } + if (pid == -1) { + fprintf(stderr, dgettext(TEXT_DOMAIN, "%s: fork failed, %s\n"), + __progname, smb_strerror(errno)); + return (1); + } + if (wait4(pid, &status, 0, NULL) != pid) { + fprintf(stderr, dgettext(TEXT_DOMAIN, + "%s: BUG executing %s command\n"), __progname, args[0]); + return (1); + } else if (!WIFEXITED(status)) { + fprintf(stderr, dgettext(TEXT_DOMAIN, + "%s: %s command aborted by signal %d\n"), + __progname, args[0], WTERMSIG(status)); + return (1); + } else if (WEXITSTATUS(status)) { + fprintf(stderr, dgettext(TEXT_DOMAIN, + "%s: %s command failed, exit status %d: %s\n"), + __progname, args[0], WEXITSTATUS(status), + smb_strerror(WEXITSTATUS(status))); + return (1); + } + return (0); +} + + +void +dropsuid() +{ + /* drop setuid root privs asap */ + eff_uid = geteuid(); + real_uid = getuid(); + seteuid(real_uid); +} + + +#define KEXTLOAD_COMMAND "/sbin/kextload" +#define FS_KEXT_DIR "/System/Library/Extensions/smbfs.kext" +#define FULL_KEXTNAME "com.apple.filesystems.smbfs" + + +int +loadsmbvfs() +{ + char *kextargs[] = {KEXTLOAD_COMMAND, FS_KEXT_DIR, NULL}; + int error = 0; + + /* + * temporarily revert to root (required for kextload) + */ + seteuid(eff_uid); + error = safe_execv(kextargs); + seteuid(real_uid); /* and back to real user */ + return (error); +} + +#undef __progname + +char *__progname = NULL; + +char * +smb_getprogname() +{ + char *p; + + if (__progname == NULL) { + __progname = (char *)getexecname(); + if ((p = strrchr(__progname, '/')) != 0) + __progname = p + 1; + } + return (__progname); +} diff --git a/usr/src/lib/libsmbfs/smb/ui-sun.c b/usr/src/lib/libsmbfs/smb/ui-sun.c new file mode 100644 index 0000000000..7512d2c964 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/ui-sun.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2001 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Routines for interacting with the user to get credentials + * (workgroup/domain, username, password, etc.) + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <libintl.h> +#include <ctype.h> + +#include <netsmb/smb_lib.h> +#include <netsmb/smb_keychain.h> + +#define MAXLINE 127 +#define MAXPASSWD 256 /* from libc:getpass */ + +static void +smb_tty_prompt(char *prmpt, + char *buf, size_t buflen) +{ + char temp[MAXLINE+1]; + char *cp; + int ch; + + memset(temp, 0, sizeof (temp)); + + fprintf(stderr, "%s", prmpt); + cp = temp; + while ((ch = getc(stdin)) != EOF) { + if (ch == '\n' || ch == '\r') + break; + if (isspace(ch) || iscntrl(ch)) + continue; + *cp++ = ch; + if (cp == &temp[MAXLINE]) + break; + } + + /* If input empty, accept default. */ + if (cp == temp) + return; + + /* Use input as new value. */ + strncpy(buf, temp, buflen); +} + +int +smb_get_authentication( + char *dom, size_t domlen, + char *usr, size_t usrlen, + char *passwd, size_t passwdlen, + const char *systemname, struct smb_ctx *ctx) +{ + char *npw; + int error, i, kcask, kcerr; + + if (ctx->ct_flags & SMBCF_KCFOUND || ctx->ct_flags & SMBCF_KCBAD) { + ctx->ct_flags &= ~SMBCF_KCFOUND; + } else { + ctx->ct_flags &= ~(SMBCF_KCFOUND | SMBCF_KCDOMAIN); + + /* + * 1st: try lookup using system name + */ + kcerr = smbfs_keychain_chk(systemname, usr); + if (!kcerr) { + /* + * Need passwd to be not empty for existing logic. + * The string here is arbitrary (a debugging hint) + * and will be replaced in the driver by the real + * password from the keychain. + */ + strcpy(passwd, "$KC_SYSTEM"); + ctx->ct_flags |= SMBCF_KCFOUND; + if (smb_debug) { + printf("found keychain entry for" + " server/user: %s/%s\n", + systemname, usr); + } + return (0); + } + + /* + * 2nd: try lookup using domain name + */ + kcerr = smbfs_keychain_chk(dom, usr); + if (!kcerr) { + /* Need passwd to be not empty... (see above) */ + strcpy(passwd, "$KC_DOMAIN"); + ctx->ct_flags |= (SMBCF_KCFOUND | SMBCF_KCDOMAIN); + if (smb_debug) { + printf("found keychain entry for" + " domain/user: %s/%s\n", + dom, usr); + } + return (0); + } + } + + if (isatty(STDIN_FILENO)) { /* need command-line prompting? */ + if (passwd && passwd[0] == '\0') { + npw = getpassphrase(dgettext(TEXT_DOMAIN, "Password:")); + strncpy(passwd, npw, passwdlen); + } + return (0); + } + + /* + * XXX: Ask the user for help, possibly via + * GNOME dbus or some such... (todo). + */ + smb_error(dgettext(TEXT_DOMAIN, + "Cannot prompt for a password when input is redirected."), 0); + + return (ENOTTY); +} + +int +smb_browse(struct smb_ctx *ctx, int anon) +{ + /* + * Let user pick a share. + * Not supported. + */ + return (EINTR); +} diff --git a/usr/src/lib/libsmbfs/sparc/Makefile b/usr/src/lib/libsmbfs/sparc/Makefile new file mode 100644 index 0000000000..23b74c3928 --- /dev/null +++ b/usr/src/lib/libsmbfs/sparc/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libsmbfs/sparcv9/Makefile b/usr/src/lib/libsmbfs/sparcv9/Makefile new file mode 100644 index 0000000000..7443ee1806 --- /dev/null +++ b/usr/src/lib/libsmbfs/sparcv9/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) |