diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/rpcsvc | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/rpcsvc')
130 files changed, 65352 insertions, 0 deletions
diff --git a/usr/src/cmd/rpcsvc/Makefile b/usr/src/cmd/rpcsvc/Makefile new file mode 100644 index 0000000000..34e3bf375b --- /dev/null +++ b/usr/src/cmd/rpcsvc/Makefile @@ -0,0 +1,248 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# cmd/rpcsvc/Makefile + +PROTOCOL_DIR= $(ROOT)/usr/include/rpcsvc + +BINPROG= rusers rup +SBINPROG= rwall spray +RPROG= rpc.rusersd +SPROG= rpc.sprayd +WPROG= rpc.rwalld +TPROG= rpc.rstatd + +PROG= $(BINPROG) $(SBINPROG) $(RPROG) $(SPROG) $(WPROG) $(TPROG) + +MANIFEST= \ + nisplus.xml \ + rusers.xml \ + spray.xml \ + wall.xml \ + rstat.xml + +SVCMETHOD= \ + nisplus + +SCLNTOBJS= spray.o spray_clnt.o +SSVCOBJS= spray_subr.o spray_svc.o +WCLNTOBJS = rwall.o rwall_clnt.o +WSVCOBJS= rwall_subr.o rwall_svc.o +TSVCOBJS= rstat_main.o rstat_proc.o rstat_svc.o rstat_v2_svc.o rstat_v2_xdr.o + +DERIVED_FILES = rstat.x rstat.h rstat_svc.c rstat_v2.h rstat_v2_svc.c \ + rstat_v2_xdr.c spray.x spray.h spray_clnt.c spray_svc.c \ + rwall.x rwall.h rwall_svc.c rwall_clnt.c + +include ../Makefile.cmd + +ROOTMANIFESTDIR= $(ROOTSVCNETWORKRPC) +$(ROOTMANIFEST) := FILEMODE= 444 + +CPPFLAGS= -I. $(CPPFLAGS.master) +LDLIBS += -lnsl +rusers := CFLAGS += -v +$(BINPROG) := LDLIBS += -lrpcsvc +$(RPROG) := LDLIBS += -lrpcsvc +$(SPROG) := LDLIBS += -lrpcsvc +$(TPROG) := LDLIBS += -lrpcsvc +spray := LDLIBS += -lrpcsvc + +CP= cp + +SUBDIRS= rpc.bootparamd nis/utils nis/bin nis/cachemgr nis/rpc.nisd \ + nis/rpc.nispasswdd +OBJS= $(SCLNTOBJS) $(SSVCOBJS) $(WCLNTOBJS) $(WSVCOBJS) $(TSVCOBJS) +SRCS= $(OBJS:%.o=%.c) rusers.c rpc.rusersd.c rup.c + +ETCFILES= rpc + +TXTS= $(ETCFILES:%=net_files/%) + +NETSVC= $(ROOTLIB)/netsvc +NIS = $(NETSVC)/nis +RWALL= $(NETSVC)/rwall +RUSERS= $(NETSVC)/rusers +SPRAY= $(NETSVC)/spray +RSTAT= $(NETSVC)/rstat +ROOTVAR_NIS= $(ROOT)/var/nis + +ROOTDIRS= \ + $(NETSVC) \ + $(NIS) \ + $(RWALL) \ + $(RUSERS) \ + $(SPRAY) \ + $(RSTAT) \ + $(ROOTVAR_NIS) + +IBINPROG= $(BINPROG:%=$(ROOTBIN)/%) +ISBINPROG= $(SBINPROG:%=$(ROOTUSRSBIN)/%) +IWPROG= $(WPROG:%=$(RWALL)/%) +ISPROG= $(SPROG:%=$(SPRAY)/%) +IRPROG= $(RPROG:%=$(RUSERS)/%) +ITPROG= $(TPROG:%=$(RSTAT)/%) +IETCFILES= $(ETCFILES:%=$(ROOTETC)/%) + +# non-default file attributes +$(ROOTDIRS) := OWNER= root +$(ROOTDIRS) := GROUP= sys + +$(ROOTETC)/rpc := FILEMODE= 0644 +$(ROOTETC)/rpc := OWNER= root +$(ROOTETC)/rpc := GROUP= sys + +all:= TARGET= all +install:= TARGET= install +clean:= TARGET= clean +clobber:= TARGET= clobber +lint:= TARGET= lint + +rpc.rstatd:= LDLIBS += -lkstat +rwall:= CPPFLAGS += -D_REENTRANT +rpc.rwalld:= CPPFLAGS += -D_REENTRANT + +# install rules + +$(RWALL)/% $(SPRAY)/% $(RUSERS)/% $(RSTAT)/%: % + $(INS.file) + +$(ROOTETC)/%: ./net_files/% + $(INS.file) + +.KEEP_STATE: + +all: $(PROG) $(TXTS) $(SUBDIRS) + +# multi-object targets +# +spray: $(SCLNTOBJS) + $(LINK.c) $(SCLNTOBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +spray_svc.o spray_clnt.o: spray.h + +rpc.sprayd: $(SSVCOBJS) + $(LINK.c) $(SSVCOBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +rwall_svc.o rwall_clnt.o: rwall.h + +rwall: $(WCLNTOBJS) + $(LINK.c) $(WCLNTOBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +rpc.rwalld: $(WSVCOBJS) + $(LINK.c) $(WSVCOBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +rstat_main.o: rstat.h rstat_v2.h + +rstat_svc.o: rstat.h + +rstat_V2_svc.o rstat_V2_xdr.o: rstat_v2.h + +rpc.rstatd: $(TSVCOBJS) + $(LINK.c) $(TSVCOBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +install: $(PROG) $(TXTS) .WAIT $(ROOTDIRS) .WAIT $(IBINPROG) $(ISBINPROG) \ + $(IWPROG) $(ISPROG) $(IRPROG) $(ITPROG) $(IETCFILES) $(SUBDIRS) \ + $(ROOTMANIFEST) $(ROOTSVCMETHOD) + +$(ROOTDIRS): + $(INS.dir) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +# +# Derived files +# + +rstat.x: $(PROTOCOL_DIR)/rstat.x + $(RM) rstat.x + $(CP) $(PROTOCOL_DIR)/rstat.x rstat.x + +rstat.h: $(PROTOCOL_DIR)/rstat.h + $(RM) rstat.h + $(CP) $(PROTOCOL_DIR)/rstat.h rstat.h + +rstat_svc.c: rstat.x + $(RPCGEN) -C -m rstat.x > $@ + +rstat_v2.h: rstat_v2.x + $(RPCGEN) -C -h rstat_v2.x > $@ + +rstat_v2_svc.c: rstat_v2.x + $(RPCGEN) -C -m rstat_v2.x > $@ + +rstat_v2_xdr.c: rstat_v2.x + $(RPCGEN) -c rstat_v2.x > $@ + +spray.x: $(PROTOCOL_DIR)/spray.x + $(RM) spray.x + $(CP) $(PROTOCOL_DIR)/spray.x spray.x + +spray.h: $(PROTOCOL_DIR)/spray.h + $(RM) spray.h + $(CP) $(PROTOCOL_DIR)/spray.h spray.h + +spray_svc.c: spray.x + $(RPCGEN) -s datagram_v -s circuit_v spray.x > $@ + +spray_clnt.c: spray.x + $(RPCGEN) -l spray.x > $@ + +rwall.x: $(PROTOCOL_DIR)/rwall.x + $(RM) rwall.x + $(CP) $(PROTOCOL_DIR)/rwall.x rwall.x + +rwall.h: $(PROTOCOL_DIR)/rwall.h + $(RM) rwall.h + $(CP) $(PROTOCOL_DIR)/rwall.h rwall.h + +rwall_svc.c: rwall.x + $(RPCGEN) -A -s datagram_v rwall.x > $@ + +rwall_clnt.c: rwall.x + $(RPCGEN) -l -M rwall.x > $@ + +check: $(CHKMANIFEST) + +clean: $(SUBDIRS) + -$(RM) $(OBJS) $(DERIVED_FILES) + +clobber: clean $(SUBDIRS) + -$(RM) $(PROG) $(CLOBBERFILES) + +lint: + $(LINT.c) $(WCLNTOBJS:%.o=%.c) -lnsl -lrpcsvc + $(LINT.c) $(WSVCOBJS:%.o=%.c) -lnsl -lrpcsvc + $(LINT.c) rusers.c -lnsl -lrpcsvc + +FRC: diff --git a/usr/src/cmd/rpcsvc/Makefile.rpc b/usr/src/cmd/rpcsvc/Makefile.rpc new file mode 100644 index 0000000000..d1244aac94 --- /dev/null +++ b/usr/src/cmd/rpcsvc/Makefile.rpc @@ -0,0 +1,51 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# cmd/rpcsvc/Makefile.rpc + +# +# This Makefile is intended to be included by Makefiles describing +# the build of server daemon services implementing RPC protocols. +# It initializes the service manifest installation directory macro +# ROOTMANISERVERRPC to the correct values for such services. +# +# + +include $(SRC)/cmd/Makefile.cmd + +ROOTMANIRPC= $(ROOTSVCNETWORK)/rpc + +ROOTMANIFESTDIR= $(ROOTMANIRPC) +$(ROOTMANIFEST) := FILEMODE= 444 + +$(ROOTMANIRPC): + $(INS.dir) + +$(ROOTMANIRPC)/%: % + $(INS.file) + +$(ROOTMANIFEST): $(ROOTMANIFESTDIR) diff --git a/usr/src/cmd/rpcsvc/net_files/rpc b/usr/src/cmd/rpcsvc/net_files/rpc new file mode 100644 index 0000000000..4fd1179b5a --- /dev/null +++ b/usr/src/cmd/rpcsvc/net_files/rpc @@ -0,0 +1,94 @@ +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# ident "%Z%%M% %I% %E% SMI" /* Svr4.0 1.2 */ +# +# rpc +# +rpcbind 100000 portmap sunrpc rpcbind +rstatd 100001 rstat rup perfmeter +rusersd 100002 rusers +nfs 100003 nfsprog +ypserv 100004 ypprog +mountd 100005 mount showmount +ypbind 100007 +walld 100008 rwall shutdown +yppasswdd 100009 yppasswd +etherstatd 100010 etherstat +rquotad 100011 rquotaprog quota rquota +sprayd 100012 spray +3270_mapper 100013 +rje_mapper 100014 +selection_svc 100015 selnsvc +database_svc 100016 +rexd 100017 rex +alis 100018 +sched 100019 +llockmgr 100020 +nlockmgr 100021 +x25.inr 100022 +statmon 100023 +status 100024 +ypupdated 100028 ypupdate +keyserv 100029 keyserver +bootparam 100026 +sunlink_mapper 100033 +tfsd 100037 +nsed 100038 +nsemntd 100039 +showfhd 100043 showfh +ioadmd 100055 rpc.ioadmd +NETlicense 100062 +sunisamd 100065 +debug_svc 100066 dbsrv +bugtraqd 100071 +event 100101 na.event # SunNet Manager +logger 100102 na.logger # SunNet Manager +sync 100104 na.sync +hostperf 100107 na.hostperf +activity 100109 na.activity # SunNet Manager +hostmem 100112 na.hostmem +sample 100113 na.sample +x25 100114 na.x25 +ping 100115 na.ping +rpcnfs 100116 na.rpcnfs +hostif 100117 na.hostif +etherif 100118 na.etherif +iproutes 100120 na.iproutes +layers 100121 na.layers +snmp 100122 na.snmp snmp-cmc snmp-synoptics snmp-unisys snmp-utk +traffic 100123 na.traffic +ktkt_warnd 100134 +nfs_acl 100227 +sadmind 100232 +gssd 100234 +nisd 100300 rpc.nisd +nispasswd 100303 rpc.nispasswdd +ufsd 100233 ufsd +pcnfsd 150001 +ocfserv 100150 +metad 100229 metad +metamhd 100230 metamhd +metamedd 100242 metamedd +smserverd 100155 smserverd diff --git a/usr/src/cmd/rpcsvc/nis/bin/Makefile b/usr/src/cmd/rpcsvc/nis/bin/Makefile new file mode 100644 index 0000000000..bbf74d862d --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/bin/Makefile @@ -0,0 +1,66 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright (c) 1988-1992 Sun Microsystems Inc +# All Rights Reserved. +# +# Makefile for NIS+ server utility commands +# + +SCRIPTS= nissetup nisclient nisserver nispopulate nisopaccess +PROG= nisstat nisshowcache nisupdkeys nisping nisctl $(SCRIPTS) +# unsupported programs, which are built, but not installed +UPROG= nisadm +SRCS= nisadm.c nisctl.c nisstat.c nisshowcache.c nisupdkeys.c nisping.c +OBJS= nisadm.o nisctl.o nisstat.o nisshowcache.o nisupdkeys.o nisping.o + +include ../../../Makefile.cmd + +ROOTNSLIB= $(ROOT)/usr/lib/nis +ROOTNSPROG= $(PROG:%=$(ROOTNSLIB)/%) +ROOTSCRIPTS= $(SCRIPTS:%=$(ROOTNSLIB)/%) + +CPPFLAGS += -I$(SRC)/lib/libnsl/include +LDLIBS += -lnsl + +$(ROOTSCRIPTS) := FILEMODE = 755 + +.KEEP_STATE: + +all: $(PROG) $(UPROG) + +install: all $(ROOTNSLIB) $(ROOTNSPROG) + +lint: lint_SRCS + +$(ROOTNSLIB): + $(INS.dir) + +$(ROOTNSLIB)/%: % + $(INS.file) + +clean: + -$(RM) $(OBJS) + +include ../../../Makefile.targ diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisadm.c b/usr/src/cmd/rpcsvc/nis/bin/nisadm.c new file mode 100644 index 0000000000..8c481d4a5f --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/bin/nisadm.c @@ -0,0 +1,1072 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisadm.c + * + * Copyright (c) 1988-1992 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Ported from SCCS version : + * "@(#)nisadm.c 1.14 90/12/12 Copyr 1990 Sun Micro"; + * + * nisadm.c + * + * This utility is a shell interface to the NIS name service. + */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> + +extern char *optarg; +extern int optind, opterr; +#define ZVAL zattr_val.zattr_val_val +#define ZLEN zattr_val.zattr_val_len +#define nilstring(s) ((s) ? (s) : "(nil)") + +#define LOWER(c) (isupper((c)) ? _tolower((c)) : (c)) + +struct obj_defaults { + nis_name oname[512]; + nis_attr attrs[NIS_MAXATTR]; + table_col tcols[NIS_MAXCOLUMNS]; + entry_col ecols[NIS_MAXCOLUMNS]; + nis_server srvrs[NIS_MAXATTR]; + nis_name grps[NIS_MAXATTR]; + endpoint endp[NIS_MAXATTR]; + char buffer[8192]; /* String space */ +}; +oar_mask masks[6] = { + {DEFAULT_RIGHTS, NIS_DIRECTORY_OBJ}, + {DEFAULT_RIGHTS, NIS_GROUP_OBJ}, + {DEFAULT_RIGHTS, NIS_LINK_OBJ}, + {DEFAULT_RIGHTS, NIS_ENTRY_OBJ}, + {DEFAULT_RIGHTS, NIS_TABLE_OBJ}, + {0L, NIS_BOGUS_OBJ} +}; + +typedef struct obj_defaults obj_defaults; +static obj_defaults * +__get_obj_defaults() +{ + static obj_defaults *obd = NULL; + + if (obd) + return (obd); + + obd = (obj_defaults *)malloc(sizeof (obj_defaults)); + if (obd == NULL) + return (obd); + memset((char *)obd, 0, sizeof (obj_defaults)); + + return (obd); +} + +static char * +ask(buf, buflen) + char **buf; + int *buflen; +{ + char *a, *s; + + if (feof(stdin)) + return (NULL); + + a = *buf; /* Point a at the buffer */ + fflush(stdin); + if (feof(stdin)) + exit(0); /* Exit on EOF */ + if (fgets(a, *buflen, stdin) == NULL) + return (NULL); /* return NULL on EOF */ + + s = a + strlen(a); /* Point s at the end of it */ + *(s-1) = '\0'; /* Nul terminate the string */ + *buf = s; + *buflen = *buflen - (strlen(a)+1); + return (a); +} + +static char * +getname(buf, len) + char **buf; + int *len; +{ + char *a, *s; + nis_name ldir = nis_local_directory(); + + if (feof(stdin)) + return (NULL); + + a = *buf; /* Point a at the buffer */ + fflush(stdin); + if (fgets(a, *len, stdin) == NULL) + return (NULL); /* return NULL on EOF */ + + s = a + strlen(a); /* Point s at the end of it */ + *(s-1) = '\0'; /* Nul terminate the string */ + if (*(s-2) != '.') { + if (strlen(a)) /* don't prepend a dot on a null name */ + strcat(a, "."); + strcat(a, ldir); + } + s = a + strlen(a) + 1; + *buf = s; + *len = *len - (strlen(a)+1); + return (a); +} + +/* + * Convert two hex digits to one unsigned char byte. + */ +static u_char +getbyte(bp) + u_char *bp; +{ + u_char nyb, val; + + nyb = LOWER(*bp); + if (nyb >= 'a') + nyb = (nyb - 'a') + 10; + else + nyb = (nyb - '0'); + if (nyb > 15) + fprintf(stderr, "Illegal character in binary data, '%c'\n", + *bp); + val = (nyb & 0xf) << 4; + + nyb = LOWER(*(bp+1)); + if (nyb >= 'a') + nyb = (nyb - 'a') + 10; + else + nyb = (nyb - '0'); + if (nyb > 15) + fprintf(stderr, "Illegal character in binary data, '%c'\n", + *(bp+1)); + val |= (nyb & 0xf); + return (val); +} + +#define xask(x) { fflush(stdin); \ + if (feof(stdin))\ + return (NULL); \ + fgets(x, 32, stdin); \ + x[strlen(x)-1] = '\0'; } + +/* + * nis_get_object() + * + * This object will query the user for an object of the type specified + * in the 'type' variable. All of it's queries go to stdin. + */ +nis_object * +nis_get_object(name, group, owner, rights, ttl, type) + char *name; + char *group; + char *owner; + u_long rights; + u_long ttl; + zotypes type; +{ + /* + * These variables define a string buffer array that is used + * while fetching the object, later the data is cloned so we + * can toss it. Yes, it's a giant hunk to put on the stack. + */ +#define BUFLENGTH 4096 + char buffer[BUFLENGTH]; + char *bp = &buffer[0]; /* For various uses */ + int bl = BUFLENGTH; + char tmpval[32], *s; /* for fetching numbers and such */ + char *t; /* temporary */ + nis_object *obj, tmp; + int np, i, j; + u_long flags; + obj_defaults *ob_data = __get_obj_defaults(); + link_obj *li; + directory_obj *di; + entry_obj *en; + group_obj *gr; + table_obj *ta; + + memset((char *)&tmp, 0, sizeof (tmp)); + + printf("Creating NIS object... \n"); + if (! name) { + printf("Enter objects name : "); + name = getname(&bp, &bl); + if (!name) + return (NULL); + } + s = (char *) nis_leaf_of(name); + if (s) + strcpy(bp, s); + else + strcpy(bp, "<none>"); + + tmp.zo_name = bp; + bl = bl - strlen(bp) - 1; + bp = bp + strlen(bp) + 1; + + if (owner) + tmp.zo_owner = owner; + else + tmp.zo_owner = nis_local_principal(); + + if (group) + tmp.zo_group = group; + else + tmp.zo_group = nis_local_group(); + + if (rights) + tmp.zo_access = rights; + else + tmp.zo_access = DEFAULT_RIGHTS; + + if (ttl) + tmp.zo_ttl = ttl; + else + tmp.zo_ttl = 24 * 3600; + + /* name must be fully qualified */ + tmp.zo_domain = nis_domain_of(name); + if (tmp.zo_domain == NULL) + tmp.zo_domain = "."; + + if (type == NIS_BOGUS_OBJ) { + tmp.zo_data.zo_type = NIS_BOGUS_OBJ; + while (tmp.zo_data.zo_type == NIS_BOGUS_OBJ) { + printf("Enter the objects type : "); + xask(tmpval); + if (LOWER(tmpval[0]) == 'g') + tmp.zo_data.zo_type = NIS_GROUP_OBJ; + else if (LOWER(tmpval[0]) == 't') + tmp.zo_data.zo_type = NIS_TABLE_OBJ; + else if (LOWER(tmpval[0]) == 'd') + tmp.zo_data.zo_type = NIS_DIRECTORY_OBJ; + else if (LOWER(tmpval[0]) == 'e') + tmp.zo_data.zo_type = NIS_ENTRY_OBJ; + else if (LOWER(tmpval[0]) == 'l') + tmp.zo_data.zo_type = NIS_LINK_OBJ; + else if (LOWER(tmpval[0]) == 'p') + tmp.zo_data.zo_type = NIS_PRIVATE_OBJ; + } + } else + tmp.zo_data.zo_type = type; + + switch (tmp.zo_data.zo_type) { + case NIS_GROUP_OBJ : + gr = &(tmp.GR_data); + printf("Enter flags value [IRN] ? "); + xask(tmpval); + gr->gr_flags = 0; + for (s = &tmpval[0]; *s; s++) { + if (LOWER(*s) == 'i') + gr->gr_flags |= IMPMEM_GROUPS; + else if (LOWER(*s) == 'r') + gr->gr_flags |= RECURS_GROUPS; + else if (LOWER(*s) == 'n') + gr->gr_flags |= NEGMEM_GROUPS; + else { + printf("Must be some combo of :\n"); + printf("\tR = Recursive groups ok\n"); + printf("\tN = Negative groups ok\n"); + printf("\tI = Implicit groups ok\n"); + exit(1); + } + } + do { + printf("Number of members in this group ? "); + xask(tmpval); + np = atoi(tmpval); + if ((np < 1) || (np > NIS_MAXATTR)) + printf("Illegal number of members.\n"); + } while ((np < 1) || (np > NIS_MAXATTR)); + gr->gr_members.gr_members_len = np; + gr->gr_members.gr_members_val = &(ob_data->grps[0]); + for (i = 0; i < np; i++) { + printf("\tMember #%d name :\n", i); + ob_data->grps[i] = getname(&bp, &bl); + } + break; + case NIS_LINK_OBJ : + + li = &(tmp.LI_data); + type = NIS_BOGUS_OBJ; + while (type == NIS_BOGUS_OBJ) { + printf( + "Linked object, enter real type [Group/Table/Entry] :"); + xask(tmpval); + if (LOWER(tmpval[0]) == 'g') + type = NIS_GROUP_OBJ; + else if (LOWER(tmpval[0]) == 't') + type = NIS_TABLE_OBJ; + else if (LOWER(tmpval[0]) == 'd') + type = NIS_DIRECTORY_OBJ; + else if (LOWER(tmpval[0]) == 'e') + type = NIS_ENTRY_OBJ; + else + type = NIS_BOGUS_OBJ; + } + li->li_rtype = type; + do { + printf("Number of attributes in this name ? "); + xask(tmpval); + np = atoi(tmpval); + if ((np < 0) || (np > NIS_MAXATTR)) + fprintf(stderr, + "Illegal number of attributes.\n"); + } while ((np < 0) || (np > NIS_MAXATTR)); + for (i = 0; i < np; i++) { + printf("Attribute #%d\n", i); + printf("\tAttribute Name : "); + ob_data->attrs[i].zattr_ndx = ask(&bp, &bl); + printf("\tAttribute Value : "); + ob_data->attrs[i].ZVAL = ask(&bp, &bl); + ob_data->attrs[i].ZLEN = + strlen(ob_data->attrs[i].ZVAL) + 1; + } + printf("Enter Linked name : "); + li->li_name = getname(&bp, &bl); + li->li_attrs.li_attrs_len = np; + li->li_attrs.li_attrs_val = &(ob_data->attrs[0]); + break; + case NIS_TABLE_OBJ : + + ta = &(tmp.TA_data); + printf("Enter table type : "); + ta->ta_type = ask(&bp, &bl); + do { + printf("Number of columns in this table ? "); + xask(tmpval); + np = atoi(tmpval); + if ((np < 1) || (np > NIS_MAXCOLUMNS)) + printf("Illegal number of columns.\n"); + } while ((np < 1) || (np > NIS_MAXCOLUMNS)); + ta->ta_maxcol = np; + printf("Enter separator character : "); + xask(tmpval); + ta->ta_sep = (tmpval[0] == '\0') ? ' ' : tmpval[0]; + printf("Enter Search path : "); + ta->ta_path = ask(&bp, &bl); + for (i = 0; i < np; i++) { + int tmplen; + + printf("Column #%d : \n", i+1); + printf("\tEnter Name : "); + ob_data->tcols[i].tc_name = ask(&bp, &bl); + ob_data->tcols[i].tc_rights = DEFAULT_RIGHTS; + printf("\tEnter Flags [S/C/B/X] : "); + xask(tmpval); + tmplen = strlen(tmpval); + for (j = 0, flags = 0; j < tmplen; j++) { + if (LOWER(tmpval[j]) == 'x') + flags |= TA_XDR; + else if (LOWER(tmpval[j]) == 'c') + flags |= TA_CASE; + else if (LOWER(tmpval[j]) == 'b') + flags |= TA_BINARY; + else if (LOWER(tmpval[j]) == 's') + flags |= TA_SEARCHABLE; + } + if ((flags & TA_SEARCHABLE) && + (strlen(ob_data->tcols[i].tc_name) == 0)) { + fprintf(stderr, + "Can't have a searchable column with no name.\n"); + return (NULL); + + } + ob_data->tcols[i].tc_flags = flags; + } + ta->ta_cols.ta_cols_len = np; + ta->ta_cols.ta_cols_val = &(ob_data->tcols[0]); + break; + case NIS_DIRECTORY_OBJ : + + di = &(tmp.DI_data); + printf("This new directory's name : "); + di->do_name = getname(&bp, &bl); + do { + printf( + "Number of servers for this directory? "); + xask(tmpval); + np = atoi(tmpval); + if ((np < 1) || (np > NIS_MAXATTR)) + printf("Illegal number of servers.\n"); + } while ((np < 1) || (np > NIS_MAXATTR)); + + for (i = 0; i < np; i++) { + if (i == 0) + printf("Enter Master server name :"); + else + printf("Enter replicate server name :"); + ob_data->srvrs[i].name = getname(&bp, &bl); + printf("Enter Universal Address : "); + ob_data->endp[i].uaddr = ask(&bp, &bl); + ob_data->endp[i].family = "INET"; + ob_data->endp[i].proto = "TCP"; + ob_data->srvrs[i].key_type = NIS_PK_NONE; + ob_data->srvrs[i].pkey.n_len = 0; + ob_data->srvrs[i].pkey.n_bytes = NULL; + ob_data->srvrs[i].ep.ep_val = &ob_data->endp[0]; + ob_data->srvrs[i].ep.ep_len = 1; + } + di->do_servers.do_servers_len = np; + di->do_servers.do_servers_val = &ob_data->srvrs[0]; + do { + printf("Enter ns type [Z/Y/D] :"); + xask(tmpval); + if (LOWER(tmpval[0]) == 'z') + di->do_type = NIS; + else if (LOWER(tmpval[0]) == 'y') + di->do_type = SUNYP; + else if (LOWER(tmpval[0]) == 'd') + di->do_type = DNS; + else + di->do_type = IVY; + } while (di->do_type == IVY); /* XXX */ + printf("Enter time to live value : "); + xask(tmpval); + di->do_ttl = atoi(tmpval); + if (! (di->do_ttl)) + fprintf(stderr, + "Warning, ttl = 0 prohibits caching.\n"); + di->do_armask.do_armask_len = 6; + di->do_armask.do_armask_val = &masks[0]; + break; + case NIS_ENTRY_OBJ : + en = &(tmp.EN_data); + /* Presumed to be the same. */ + printf("Enter entry type : "); + en->en_type = ask(&bp, &bl); + if (!en->en_type) + return (NULL); + do { + printf("Number of columns for this entry ? "); + xask(tmpval); + np = atoi(tmpval); + if ((np < 1) || (np > NIS_MAXCOLUMNS)) + printf("Illegal number of columns.\n"); + } while ((np < 1) || (np > NIS_MAXCOLUMNS)); + + for (i = 0; i < np; i++) { + entry_col *ec; + u_char *val; + int len; + + ec = &(ob_data->ecols[i]); + printf("Column #%d :\n", i+1); + printf("\tEnter Flags [M/C/B/X] : "); + xask(tmpval); + len = strlen(tmpval); + for (j = 0, flags = 0; j < len; j++) { + if (LOWER(tmpval[j]) == 'x') + flags |= EN_XDR; + else if (LOWER(tmpval[j]) == 'c') + flags |= EN_CRYPT; + else if (LOWER(tmpval[j]) == 'b') + flags |= EN_BINARY; + else if (LOWER(tmpval[j]) == 'm') + flags |= EN_MODIFIED; + else + printf("** Unknown flag '%c'\n", + tmpval[j]); + } + ec->ec_flags = flags; + printf("\tValue : "); + val = (u_char *)ask(&bp, &bl); + len = strlen((char *) val)+1; + if (flags & EN_BINARY) { + for (j = 0; j < len; j += 2) + *(val + (j>>1)) = + getbyte(val+j); + len = len >> 1; /* Half the bytes */ + } + ec->ec_value.ec_value_val = (char *)val; + ec->ec_value.ec_value_len = len; + } + en->en_cols.en_cols_len = np; + en->en_cols.en_cols_val = &(ob_data->ecols[0]); + break; + case NIS_PRIVATE_OBJ: + printf("Data length : "); + xask(tmpval); + i = atoi(tmpval); + tmp.zo_data.objdata_u.po_data.po_data_len = i; + tmp.zo_data.objdata_u.po_data.po_data_val = bp; + for (j = 0; j < i; j++) { + xask(tmpval); + *(bp+j) = getbyte(tmpval); + } + break; + + } + obj = nis_clone_object(&tmp, NULL); /* Create a "clean" copy */ + + return (obj); +} +void +usage(nm) + char *nm; +{ + fprintf(stderr, +"usage : %s -C|a|A|r|R|m|p|l|L [-t G|E|D|L|T] [-S] [-o file] name\n", nm); +} + +char *nis_errors[] = { + "SUCCESS", + "S_SUCCESS", + "NOTFOUND", + "S_NOTFOUND", + "CACHEEXPIRED", + "NAMEUNREACHABLE", + "UNKNOWNOBJ", + "TRYAGAIN", + "SYSTEMERROR", + "CHAINBROKEN", + "PERMISSION", + "NOTOWNER", + "NOT_ME", + "NOMEMORY", + "NAMEEXISTS", + "NOTMASTER", + "INVALIDOBJ", + "BADNAME", + "NOCALLBACK", + "CBRESULTS", + "NOSUCHNAME", + "NOTUNIQUE", + "IBMODERROR", + "NOSUCHTABLE", + "TYPEMISMATCH", + "LINKNAMEERROR", + "PARTIAL", + "TOOMANYATTRS", + "RPCERROR", + "BADATTRIBUTE", + "NOTSEARCHABLE", + "CBERROR", + "FOREIGNNS", + "BADOBJECT", + "NOTSAMEOBJ", + "MODFAIL", + "BADREQUEST", + "NOTEMPTY" +}; + +static char * +text_of(s) + nis_error s; +{ + static char *bg = "BOGUS_ERROR"; + + if (s > NIS_NOTEMPTY) + return (bg); + else + return (nis_errors[s]); +} + + +void +print_stats(r) + nis_result *r; +{ + fprintf(stderr, "%s:C=%d:A=%d:D=%d:S=%d:\n", text_of(r->status), + r->cticks, r->aticks, r->dticks, r->zticks); +} + +enum op_todo {NOP, ADD, ADD_NS, REMOVE, REMOVE_OBJ, REM_DIR, MODIFY, + PRINT, LIST, CHECKPOINT, LOOKUP}; +XDR in_xdrs, out_xdrs; + +void +put_obj(obj, f) + nis_object *obj; + int f; +{ + if (f) + nis_print_object(obj); + else + xdr_nis_object(&out_xdrs, obj); +} + +nis_object * +get_obj(name, type, f) + nis_name name; + zotypes type; + int f; +{ + nis_object *obj; + char *s; + int stat; + + if (f) { + obj = nis_get_object(name, NULL, NULL, 0, 0, type); + stat = TRUE; + } else { + obj = (nis_object *)(calloc(1, sizeof (nis_object))); + stat = xdr_nis_object(&in_xdrs, obj); + if (! stat) { + free(obj); + obj = NULL; + } else if (name) { + /* We "rewrite" the name of the object. */ + free(obj->zo_name); + obj->zo_name = strdup(nis_leaf_of(name)); + free(obj->zo_domain); + obj->zo_domain = strdup(nis_domain_of(name)); + } + } + /* FIXUP for some old objects */ + if (obj) { + s = obj->zo_name; + if (s[strlen(s)-1] == '.') + s[strlen(s)-1] = '\0'; + } + + return (obj); /* will be NULL if undecodable */ +} + +/* + * Attempt to actually create the directories for a particular + * dir object. + */ +make_directory(obj) + nis_object *obj; +{ + directory_obj *da; + nis_server *srvs; + int i, ms; + nis_error status; + + da = &(obj->DI_data); + ms = da->do_servers.do_servers_len; + srvs = da->do_servers.do_servers_val; + for (i = 0; i < ms; i++) { + printf("Attempting to create directory \"%s\"\n", da->do_name); + status = nis_mkdir(da->do_name, &(srvs[i])); + } +} + +/* + * Attempt to actually remove the directories for a particular + * dir object. + */ +remove_dir(obj) + nis_object *obj; +{ + directory_obj *da; + nis_server *srvs; + int i, ms; + nis_error status; + + da = &(obj->DI_data); + ms = da->do_servers.do_servers_len; + srvs = da->do_servers.do_servers_val; + for (i = 0; i < ms; i++) { + printf("Attempting to remove directory \"%s\"\n", da->do_name); + status = nis_rmdir(da->do_name, &(srvs[i])); + } +} + + +char name_buf[1024]; /* Temp buffer for names */ +/* + * Construct a legal NIS name from the object components. + * Uses static name buffer above. + */ +char * +make_name(name, domain) + nis_name name; + nis_name domain; +{ + + strcpy(name_buf, name); + if (domain[0] != '.') + strcat(name_buf, "."); + strcat(name_buf, domain); + if (name_buf[strlen(name_buf)-1] != '.') + strcat(name_buf, "."); + return (name_buf); +} + +/* + * Main code for the nisadm command + */ +main(argc, argv) + int argc; + char *argv[]; +{ + enum op_todo op = PRINT; /* Operation to perform */ + zotypes obj_type = 0; /* Object "type" to use */ + ib_request req; /* Request to use */ + int stats = FALSE; /* Print statistics */ + u_long flags = 0, /* Lookup flags */ + err = 0; /* errors encountered */ + int interact = FALSE, /* Interactive input */ + prettyprint = FALSE; /* Print in ASCII text */ + char *s, /* Some temporaries */ + *name, /* NIS name to look up */ + *rname; /* name to look up */ + int i, j; /* More temporaries */ + nis_object *obj; /* Working object */ + char real_name[1024]; /* discovered name */ + nis_result *res; /* Operation result */ + int c; + + if (strcmp(argv[0], "zadd") == 0) + op = ADD; + else if (strcmp(argv[0], "zrem") == 0) + op = REMOVE; + else if (strcmp(argv[0], "zmod") == 0) + op = MODIFY; + else if (strcmp(argv[0], "zlist") == 0) + op = LIST; + else if (strcmp(argv[0], "zdump") == 0) { + op = LIST; + prettyprint = TRUE; + } + + + while ((c = getopt(argc, argv, "CARLarmpit:SlF:")) != -1) { + switch (c) { + case 'a' : + op = ADD; + break; + case 'A' : + op = ADD_NS; + break; + case 'R' : + op = REMOVE_OBJ; + break; + case 'r' : + op = REMOVE; + break; + case 'm' : + op = MODIFY; + break; + case 'L' : + op = LIST; + break; + case 'l' : + op = LOOKUP; + break; + case 'p' : + prettyprint = TRUE; + break; + case 'i' : + interact = TRUE; + break; + case 'S' : + stats = TRUE; + break; + case 'C' : + op = CHECKPOINT; + break; + case 'F' : + for (s = optarg; *s; s++) { + switch (*s) { + case 'h' : + flags |= HARD_LOOKUP; + break; + case 'f' : + flags |= FOLLOW_LINKS; + break; + case 'p' : + flags |= FOLLOW_PATH; + break; + case 'c' : + flags |= NO_CACHE; + break; + case 'a' : + flags |= ALL_RESULTS; + break; + case 'm' : + flags |= MASTER_ONLY; + break; + default : + fprintf(stderr, "bad flag\n"); + err++; + } + } + break; + case 't': + switch (*optarg) { + case 'G' : + obj_type = NIS_GROUP_OBJ; + break; + case 'D' : + obj_type = NIS_DIRECTORY_OBJ; + break; + case 'T' : + obj_type = NIS_TABLE_OBJ; + break; + case 'E' : + obj_type = NIS_ENTRY_OBJ; + break; + case 'L' : + obj_type = NIS_LINK_OBJ; + break; + default : + fprintf(stderr, + "unknown object type\n"); + err++; + break; + } + break; + case '?' : + usage(argv[0]); + exit(1); + default : + err++; + break; + } + } + for (name = NULL; optind < argc; optind++) { + if ((name) || (*argv[optind] == '-')) { + fprintf(stderr, "Extra input beyond name.\n"); + err++; + } else + name = argv[optind]; + } + + if (err || name == NULL) { + usage(argv[0]); + exit(1); + } + + if (name[strlen(name) - 1] != '.') { + res = nis_lookup(name, EXPAND_NAME); + if (res->status != NIS_SUCCESS) { + fprintf(stderr, "unable to locate \"%s\"\n", name); + exit(1); + } + sprintf(real_name, "%s.%s", res->objects.objects_val->zo_name, + res->objects.objects_val->zo_domain); + name = real_name; + nis_freeresult(res); + } + + if (! interact) + xdrstdio_create(&in_xdrs, stdin, XDR_DECODE); + + if (! prettyprint) + xdrstdio_create(&out_xdrs, stdout, XDR_ENCODE); + + switch (op) { + case ADD : + case ADD_NS : + while ((obj = get_obj(name, obj_type, interact))) { + + rname = make_name(obj->zo_name, obj->zo_domain); + if ((__type_of(obj) == NIS_ENTRY_OBJ) && + (op == ADD)) + res = nis_add_entry(rname, obj, 0); + else + res = nis_add(rname, obj); + if (stats) + print_stats(res); + if (res->status != NIS_SUCCESS) { + if (!stats) + print_stats(res); + err = res->status; + if (__type_of(obj) != NIS_ENTRY_OBJ) + break; + } + if (__type_of(obj) == NIS_DIRECTORY_OBJ) + make_directory(obj); + + nis_destroy_object(obj); + } + break; + + case REMOVE_OBJ : + if (interact) { + fprintf(stderr, + "This option cannot use user typed in objects.\n"); + break; + } + while ((obj = get_obj(name, obj_type, 0))) { + rname = make_name(obj->zo_name, obj->zo_domain); + if (__type_of(obj) == NIS_ENTRY_OBJ) + res = nis_remove_entry(rname, obj, 0); + else + res = nis_remove(rname, obj); + if (stats) + print_stats(res); + if (res->status != NIS_SUCCESS) { + if (!stats) + print_stats(res); + nis_print_object(obj); + err = res->status; + } + nis_destroy_object(obj); + } + break; + + case REMOVE : + if (name == NULL) { + fprintf(stderr, + "Name required for remove operation.\n"); + exit(1); + } + strcpy(name_buf, name); + if (name_buf[strlen(name_buf)-1] != '.') + strcat(name_buf, "."); + + if (obj_type == NIS_DIRECTORY_OBJ) { + res = nis_lookup(name_buf, 0); + if (NIS_RES_STATUS(res) != NIS_SUCCESS) { + err = res->status; + break; + } + obj = nis_clone_object(NIS_RES_OBJECT(res), + NULL); + nis_freeresult(res); + res = nis_remove(name_buf, obj); + if (NIS_RES_STATUS(res) == NIS_SUCCESS) { + remove_dir(obj); + } + nis_destroy_object(obj); + if ((stats) || (NIS_RES_STATUS(res) + != NIS_SUCCESS)) + print_stats(res); + err = NIS_RES_STATUS(res); + nis_freeresult(res); + break; + } + nis_get_request(name_buf, NULL, NULL, &req); + if (req.ibr_srch.ibr_srch_len) + res = nis_remove_entry(name_buf, NULL, 0); + else + res = nis_remove(name_buf, NULL); + nis_free_request(&req); + if ((stats) || (NIS_RES_STATUS(res) != NIS_SUCCESS)) + print_stats(res); + if (res->status != NIS_SUCCESS) + err = res->status; + break; + + case MODIFY : + while ((obj = get_obj(name, obj_type, interact))) { + rname = make_name(obj->zo_name, obj->zo_domain); + if (__type_of(obj) == NIS_ENTRY_OBJ) + res = nis_modify_entry(rname, obj, 0); + else + res = nis_modify(rname, obj); + if (stats) + print_stats(res); + if (res->status != NIS_SUCCESS) { + if (!stats) + print_stats(res); + err = res->status; + } + nis_destroy_object(obj); + } + break; + + case LOOKUP : + case LIST : + if (name == NULL) { + fprintf(stderr, + "Name required for List or Lookup operation.\n"); + exit(1); + } + strcpy(name_buf, name); + if (name_buf[strlen(name_buf)-1] != '.') + strcat(name_buf, "."); + + nis_get_request(name_buf, NULL, NULL, &req); + if ((op == LIST) || (req.ibr_srch.ibr_srch_len != 0)) + res = nis_list(name_buf, flags, NULL, NULL); + else + res = nis_lookup(req.ibr_name, flags); + nis_free_request(&req); + if (res->status == NIS_SUCCESS) { + if (stats) + print_stats(res); + for (i = 0; i < res->objects.objects_len; i++) { + put_obj((res->objects.objects_val)+ i, + prettyprint); + } + } + if (stats) + print_stats(res); + + if (res->status != NIS_SUCCESS) { + if (!stats) + print_stats(res); + err = res->status; + break; + } + break; + + case PRINT : + while ((obj = get_obj(name, obj_type, interact))) { + put_obj(obj, prettyprint); + nis_destroy_object(obj); + } + break; + + case CHECKPOINT : + if (! name) { + fprintf(stderr, + "Name required for Checkpoint operation."); + err++; + break; + } + strcpy(name_buf, name); + if (name_buf[strlen(name_buf)-1] != '.') + strcat(name_buf, "."); + + res = nis_checkpoint(name_buf); + if (stats) + print_stats(res); + if (res->status != NIS_SUCCESS) { + if (!stats) + print_stats(res); + err = res->status; + } + break; + + default : + fprintf(stderr, "Unknown operation requested.\n"); + err = -1; + break; + + } + + if (! interact) + xdr_destroy(&in_xdrs); + else + printf("\n"); + + if (! prettyprint) + xdr_destroy(&out_xdrs); + + exit(err); +} diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisclient.sh b/usr/src/cmd/rpcsvc/nis/bin/nisclient.sh new file mode 100644 index 0000000000..273e107b5e --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/bin/nisclient.sh @@ -0,0 +1,1754 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# nisclient -- script to setup NIS+ clients + +# +# print_usage(): ask user if they want to see detailed usage msg. +# +print_usage() +{ + echo + get_yesno " Do you want to see more information on this command? \\n\ + (type 'y' to get a detailed description, 'n' to exit)" + if [ $ANS = "n" -o $ANS = "N" ] + then + echo + return 1 + else + print_more + fi + exit 1 +} + + +# +# print_more(): print the usage message. +# +print_more() +{ + more << EOF +USAGE: + o to create credentials for NIS+ client or NIS+ principals: + $PROG -c [-o] [-v] [-x] [-l <NIS+_password>] [-d <NIS+_domain>] + <name> ... + + o to initialize NIS+ client machines: + $PROG -i [-S 0|2] [-v] [-x] -h <NIS+_server_host> + [-a <NIS+_server_addr>] [-d <NIS+_domain>] + + o to initialize NIS+ users: + $PROG -u [-v] [-x] + + o to restore the network service environment: + $PROG -r + +OPTIONS: + -a <NIS+_server_addr> + specifies the IP address for the NIS+ server. This option is + *ONLY* used with the "-i" option. + + -c adds DES credentials for NIS+ principals. + + -d <NIS+_domain> + specifies the NIS+ domain where the credential should be created + when used in conjuction with the -c option. It specifies the + name for the new NIS+ domain when used in conjuction with the + -i option. The default is your current domain name. + + -h <NIS+_server_host> + specifies the NIS+ server's hostname. This option is *ONLY* + used with the "-i" option. + + -i initializes a NIS+ client machine. Also see the -S option. + + -l <network_password> + specifies the network password for the clients. This option is + *ONLY* used with the "-c" option. If this option is not specified, + this script will prompt you for the network password. + + -o overwrite existing credential entries. The default is not + to overwrite. This is *ONLY* used with the "-c" option. + + -r restores the network service environment. + + -S 0|2 + specifies the authentication level for the NIS+ client. Level 0 is + for unauthenticated clients and level 2 is for authenticated (DES) + clients. The default is to set up with level 2 authentication. + This option is *ONLY* used with -i option. nisclient always uses + level 2 authentication (DES) for both -c and -u options. There is + no need to run nisclient with -u and -c for level 0 authentication. + + -u initializes a NIS+ user. + + -v runs this script in verbose mode. + + -x turns the "echo" mode on. This script just prints the commands + that it would have executed. The commands are printed with + leading "+++". Note that the commands are not actually executed. + The default is off. + +EOF +} + + +print_ciru_usage() +{ + if [ "$ERRciru_OPTION" = "$ciru_OPTION" ] + then + echo "**WARNING: You have specified the '$ciru_OPTION' option twice." + return 0 + fi + echo + echo "**ERROR: You have specified the '$ERRciru_OPTION' option after" + echo " having selected the '$ciru_OPTION' option." + echo "Please select only one of these options: '-c', '-i', '-r' or '-u'." + print_usage + exit 1 +} + + +#^L +# Generic Routines: +# ----------------- +# +# This section contains general routines. +# get_ans() - prompts the message and waits for an input +# get_yesno() - prompts the message and waits for a Y or N answer +# tolower(): converts upper to lower case. +# + +# +# get_ans(): gets an answer from the user. +# $1 instruction/comment/description/question +# $2 default value +# +get_ans() +{ + if [ -z "$2" ] + then + echo "$1 \c" + else + echo "$1 [$2] \c" + fi + read ANS + if [ -z "$ANS" ] + then + ANS=$2 + fi +} + + +########## get_yesno constants: +## +## There are two very common phrases passed to get_yesno: +## These have been parameterized to provide "ease of use". +## Thus, there are three message "types" which are possible: +## --$CONTINUE: "Do you want to continue? (type 'y' to continue, 'n' to exit this script)" +## --$CONFIRM: "Is this information correct? (type 'y' to accept, 'n' to change)" +## --actual string is passed. +## +## If the message is $CONTINUE, get_yesno will exit if the response is no. +## +########### +CONTINUE=2 +CONFIRM=1 +# +# get_yesno(): get the yes or no answer. +# $1 message type or message. +# +# +# +get_yesno() +{ + ANS="X" + + case $1 in + $CONTINUE ) + INFOTEXT="Do you want to continue? (type 'y' to continue, 'n' to exit this script)" + ;; + $CONFIRM ) + INFOTEXT="Is this information correct? (type 'y' to accept, 'n' to change)" + ;; + *) INFOTEXT="$1" + ;; + esac + + while [ "$ANS" != "y" -a "$ANS" != "n" -a "$ANS" != "Y" -a "$ANS" != "N" ] + do + get_ans "$INFOTEXT" "" + done + + if [ "$1" = "$CONTINUE" ]; then + if [ $ANS = "n" -o $ANS = "N" ] + then + exit + fi + fi + +} + + +# +# tolower(): converts upper to lower case. +# $1 string to convert +# +tolower() +{ + echo "$1" | tr '[A-Z]' '[a-z]' +} + + +# +# smf(5) routines +# restart_instance() - restart instance or enable if not enabled +# + +# +# restart_instance [-t] instance_fmri +# +restart_instance() { + if [ "$1" = "-t" ]; then + flag=-t + shift + else + flag= + fi + + if [ "`/usr/bin/svcprop -p restarter/state $1`" = "disabled" ]; + then + /usr/sbin/svcadm enable $flag $1 + else + /usr/sbin/svcadm restart $1 + fi +} + + +#^L +# Common Routines: +# --------------- +# +# This section contains common routines for the script. +# init() - initializes all the variables +# parse_arg() - parses the command line arguments +# + +# +# init(): initializes variables and options +# +init() +{ + + VERB='> /dev/null' + PROG=`basename $0` + ECHO="eval" + BACKUP=no_nisplus + DOM=`nisdefaults -d` + NODOT=`echo $DOM | sed -e "s/\.$//"` + SEC_DOM_NODOT="" + SEC_DOM_DOT="" + SEC=x + PAS="" + GEN=FALSE + DEFSEC=x + OWRITE="false" + HOST= + ADDR= + + OS=`uname -r | cut -d. -f1` + OSVER=`uname -r | cut -d. -f2` + + # + # The following files need special handling: + # - they must be backed up and removed before initializing the client. + # These files are then automatically generated by nisinit and chkey + # during the initialization process. + # - during the restore process, these files must be removed if the + # backup files are not found. + # + DEL_REST_FILES="/var/nis/NIS_COLD_START /var/nis/client_info /etc/.rootkey" + + # + # The following file needs to be removed before the initialization + # and restoring process. There is no need to backup this file because + # this file is generated by the nis_cachemgr automatically. + # + DELETE_FILES="/var/nis/NIS_SHARED_DIRCACHE /var/nis/.pref_servers" + + # + # RESTORE_FILES variable contains a list of files that need to + # be restored if the backup file exists. Otherwise, the current + # file should be left alone. + # + # May have to restore the /etc/inet/ipnodes file if the client is + # initialized with an IPv6 address for the NIS+ server. + # + RESTORE_FILES="/etc/defaultdomain $HOSTS_FILE" + + # + # REST_DOM_DIR variable is the same as RESTORE_FILES except for + # directories. Specifically, this is for directories which are + # *not* generated by this script, rather just need renaming to + # avoid conflicts in a NIS+ client environment. + # + REST_DOM_DIR="/var/yp/binding/$NODOT" + + # + # The following section is needed for 4.x support. + # + # Again, /etc/inet/ipnodes file may need to be added in the future. + + HOSTS_FILE=/etc/inet/hosts + PATH=/usr/lib/nis:/usr/sbin:/usr/bin:/usr/lib/netsvc/yp:$PATH; export PATH + RESTORE_FILES="$RESTORE_FILES /etc/nsswitch.conf" + YPPROC="ypbind -broadcast" +} + + +# +# parse_arg(): parses the input arguments. +# It returns the number to be shift in the argument list. +# +parse_arg() +{ + while getopts "a:cd:g:h:ik:l:orS:uvx" ARG + do + case $ARG in + a) ADDR=$OPTARG;; + c) if [ -z "$ACTION" ] + then + ACTION="create" + ciru_OPTION="-c" + else + ERRciru_OPTION="-c" + print_ciru_usage + fi;; + d) if [ "`echo $OPTARG | sed -e 's/.*\(.\)$/\1/'`" != "." ] + then + NODOT=$OPTARG + DOM=${NODOT}. + else + DOM=$OPTARG + NODOT=`echo $DOM | sed -e "s/\.$//"` + fi + REST_DOM_DIR="/var/yp/binding/$NODOT";; + g) GEN="TRUE" + GENFILE=$OPTARG;; + h) HOST=$OPTARG;; + i) if [ -z "$ACTION" ] + then + ACTION="init" + ciru_OPTION="-i" + else + ERRciru_OPTION="-i" + print_ciru_usage + fi;; + k) if [ "`echo $OPTARG | sed -e 's/.*\(.\)$/\1/'`" != "." ] + then + SEC_DOM_NODOT=$OPTARG + SEC_DOM_DOT=${NODOT}. + else + SEC_DOM_DOT=$OPTARG + SEC_DOM_NODOT=`echo $SEC_DOM_DOT | sed -e "s/\.$//"` + fi;; + l) PAS=$OPTARG;; + o) OWRITE="";; + r) if [ -z "$ACTION" ] + then + ACTION="restore" + ciru_OPTION="-r" + else + ERRciru_OPTION="-r" + print_ciru_usage + fi;; + S) VALUE=`expr "$OPTARG" : "\([02]\)"` + if [ -z "$VALUE" ] + then + echo "**ERROR: invalid security level." + echo " It must be either 0 or 2." + exit 1 + fi + SEC=$VALUE;; + v) VERB="";; + u) if [ -z "$ACTION" ] + then + ACTION="user" + ciru_OPTION="-u" + else + ERRciru_OPTION="-u" + print_ciru_usage + fi;; + x) ECHO="echo +++";; + \?) print_usage + exit 1;; + *) echo "**ERROR: Should never get to this point!!!!!" + print_usage + exit 1;; + esac + done + return `expr $OPTIND - 1` +} + + +#^L +# Common Routines used by -c and -i: +# ---------------------------------- +# +# This section contains routines to setup a client. +# setup_backup() - sets up the the backup files +# setup_domain() - sets up the domain +# setup_server() - sets up the server information +# setup_switch() - sets up the switch +# setup_security()- sets up the local keys +# do_nisinit() - runs the nisinit command +# rm_servdata() - deletes /var/nis/data, /var/nis/trans.log & +# /var/nis/data.dict* +# + +# +# setup_backup(): backup all the files specfied in variable $RESTORE_FILES +# to <file>.$BACKUP and save a temporary copy of previous backup. +# +setup_backup() +{ + eval "echo setting up backup files... $VERB" + + trap restore_tmp 2 + + # ... save a copy of current backup copies + for FILE in $RESTORE_FILES $DEL_REST_FILES + do + if [ -f $FILE.$BACKUP ] + then + $ECHO mv $FILE.$BACKUP $FILE.$BACKUP.$$ > /dev/null & + fi + done + + # ... save a copy of these special files: coldstart and rootkey + for FILE in $DEL_REST_FILES + do + if [ -f $FILE ] + then + eval "echo saving a copy of $FILE... $VERB" + $ECHO cp $FILE $FILE.$BACKUP + fi + done + + trap restore_here 2 +} + + +# +# setup_domain(): sets up "domainname" and "/etc/defaultdomain" with the specified +# domain information. +# +setup_domain() +{ + echo "setting up domain information \"$DOM\"..." + if [ `nisdefaults -d` != $DOM ] + then + # NODOT is used to support 4.x YP clients + $ECHO domainname $NODOT + fi + + # ... we need to check the /etc/defaultdomain file separately. + # We cannot assume that the /etc/defaultdomain is set to the + # same value as the domainname is. + # + DEFDOM=`sed -n -e "1p" /etc/defaultdomain` + if [ "$DEFDOM" != $NODOT ] + then + $ECHO "cp /etc/defaultdomain /etc/defaultdomain.$BACKUP > /dev/null" + $ECHO "domainname > /etc/defaultdomain" + fi + echo "" + + # ... save a copy of current backup copies just like for + # $RESTORE_FILES and $DEL_REST_FILES, but since this script + # does not generate a new version of the directories, we + # just rename it, and restore later if necessary. + for FILE in $REST_DOM_DIR + do + if [ -d $FILE ] + then + if [ -d $FILE.$BACKUP ] + then + $ECHO mv -f $FILE.$BACKUP $FILE.$BACKUP.$$ > /dev/null & + fi + $ECHO mv -f $FILE $FILE.$BACKUP > /dev/null & + fi + done +} + + + +# +# setup_server(): adds server information into /etc/hosts file. +# Initializes $ADDR & $HOST (addr & name of place to get COLDSTART file) +# +# Will have to modify this function to account for IPV6 addresses being +# passed as the server address (when the NIS+ servers start running over IPV6). + +setup_server() +{ + eval "echo setting up NIS+ server information... $VERB" + while [ -z "$HOST" ] + do + get_ans "Type server's hostname:" "" + HOST=$ANS + done + # Parse $HOSTS_FILE, first kill all comments, + # then check for $HOST as a word by itself, + # then take just the first such line. + # Characters within [] are <space> and <tab>. + ENTRY=`sed -e 's/#.*$//' $HOSTS_FILE | \ + awk '/[ ]'$HOST'([ ]|$)/ \ + { print $0; exit}'` + if [ "$ENTRY" ] + then + if [ -z "$ADDR" ] + then + eval "echo $HOST is already in the /etc/hosts file. $VERB" + echo "" + ADDR=`echo $ENTRY | awk '{print $1}'` + return + else + OLD_ADDR=`echo $ENTRY | awk '{print $1}'` + $ECHO "cp $HOSTS_FILE $HOSTS_FILE.$BACKUP" + $ECHO "sed -e \"/$ENTRY/s/^$OLD_ADDR/$ADDR/\" \ + $HOSTS_FILE > /tmp/hosts.$$" + $ECHO mv /tmp/hosts.$$ $HOSTS_FILE > /dev/null + return + fi + elif [ -z "$ADDR" ] + then + DEFADDR="" + zone=`/usr/bin/zonename` + PROC=`/usr/bin/pgrep -z $zone ypbind` + if [ ! -z "$PROC" ] + then + # ... try yp maps + DEFADDR=`ypmatch $HOST hosts 2> /dev/null | cut -d' ' -f1` + else + # ... try nisplus tables + if [ -f /var/nis/NIS_COLD_START ] + then + DEFADDR=`nismatch -P $HOST hosts.org_dir 2> /dev/null | cut -d' ' -f3` + fi + fi + while [ -z "$ADDR" ] + do + get_ans "Type server ${HOST}'s IP address:" "$DEFADDR" + ADDR=$ANS + done + fi + + $ECHO "cp $HOSTS_FILE $HOSTS_FILE.$BACKUP" + $ECHO "echo $ADDR $HOST >> $HOSTS_FILE" + eval "echo $VERB" +} + + +# +# setup_switch(): copies the nisplus switch configuration file to +# nsswitch.conf. +# +setup_switch() +{ + echo "setting up the name service switch information..." + $ECHO "/bin/rm -f /etc/.rootkey 2> /dev/null" + diff /etc/nsswitch.conf /etc/nsswitch.nisplus > /dev/null + if [ $? -eq 0 ] + then + eval "echo The switch configuration file is already set to use NISPLUS. $VERB" + echo "" + restart_instance network/rpc/keyserv:default + return + fi + + $ECHO "cp /etc/nsswitch.conf /etc/nsswitch.conf.$BACKUP" + $ECHO "cp /etc/nsswitch.nisplus /etc/nsswitch.conf" + + restart_instance network/rpc/keyserv:default + echo "" +} + + +# +# print_passwd_request() +# Utility print routine for setup_security & old_setup_security +# Made a routine to provide consistency in outputting messages, +# and so only 1 place has to be changed. +# $1 is the same as $1 for the calling routine. +# +print_passwd_request() +{ + eval "echo setting up security information for $1... $VERB" + echo "At the prompt below, type the network password (also known" + echo "as the Secure-RPC password) that you obtained either" + echo "from your administrator or from running the nispopulate script." +} + + +# +# setup_security(): runs chkey to change the network password same as the +# login passwd. +# $1 specifies root or user setup +# +setup_security() +{ + if [ $1 = "root" ] + then + MESS="root" + else + MESS="user" + fi + print_passwd_request $1 + $ECHO "chkey -p > /dev/null" + if [ $? -ne 0 ] + then + echo "**ERROR: chkey failed." + echo "" + echo "The network password that you have entered is invalid." + echo "If this machine was initialized before as a NIS+ client," + echo "please enter the $MESS login password as the network" + echo "password." + echo "Or re-type the network password that your administrator" + echo "gave you." + echo "" + + $ECHO "chkey -p > /dev/null" + if [ $? -ne 0 ] + then + echo "**ERROR: chkey failed again." + echo "Please contact your network administrator to verify your network password." + if [ $1 = "root" ] + then + restore_here + fi + fi + fi + echo "" + echo "Your network password has been changed to your login one." + echo "Your network and login passwords are now the same." + echo "" +} + + +# +# do_nisinit(): runs nisinit. +# use $ADDR, as /etc/hosts isn't read if nis+ running +# +# Will have to modify this function for working with IPV6 NIS+ servers +do_nisinit() +{ + eval "echo running nisinit command ... $VERB" + + $ECHO "/bin/rm -f $DELETE_FILES 2> /dev/null" + $ECHO "mv -f /var/nis/NIS_COLD_START /var/nis/NIS_COLD_START.$BACKUP 2> /dev/null" + if [ -z "$SEC_DOM_DOT" ] + then + eval "echo nisinit -c -H $ADDR ... $VERB" + $ECHO "nisinit -c -H $ADDR > /dev/null" + else + eval "echo nisinit -c -k $SEC_DOM_DOT -H $ADDR ... $VERB" + $ECHO "nisinit -c -k $SEC_DOM_DOT -H $ADDR > /dev/null" + fi + if [ $? -ne 0 ] + then + echo "**ERROR: nisinit failed." + restore_here + fi + echo "" +} + + + +# +# rm_servdata(): removes the directory /var/nis/data, the trans.log file, +# and the /var/nis/data.dict* file(s) if the client machine is a NIS+ +# server machine. +# +rm_servdata() +{ + $ECHO "rm -f /var/nis/trans.log > /dev/null" + $ECHO "rm -f /var/nis/data.dict* > /dev/null" + $ECHO "rm -rf /var/nis/data > /dev/null" + echo "" +} + + + +# +# +# Client initialization Routine: +# ------------------------------- +# +# This section contains routine to initialize a client. +# init_client() - the main module to initialize a client +# + +# +# init_client(): initializes client +# +init_client() +{ + eval "echo initializing client machine... $VERB" + WHO=`id | sed -e "s/uid=[0-9]*(\([^ )]*\)).*/\1/"` + if [ $WHO != "root" ] + then + echo "**ERROR: You must be root to use the -i option." + exit 1 + fi + + echo "" + if [ ! -z "$SEC_DOM_DOT" ] + then + eval "echo comparing the system domain and the secured RPC domain... $VERB" + check_rpc_domain + if [ "$SEC_DOM_DOT" != "$DOM" ] + then + echo "Initializing client `uname -n` for Secure RPC domain \"$SEC_DOM_NODOT\"." + echo "The system default domainname will be set to a different domain \"$DOM\"." + else + echo "Initializing client `uname -n` for domain \"$DOM\"." + fi + else + echo "Initializing client `uname -n` for domain \"$DOM\"." + fi + + + if [ -d /var/nis/data ] + then + echo "" + echo "WARNING: this machine serves NIS+ directories. Once this script is" + echo "executed, you will not be able to restore the existing NIS+ server" + echo "environment. Are you sure you want to proceed?" + get_yesno "(type 'y' to continue, 'n' to exit this script)" + if [ $ANS = "n" -o $ANS = "N" ] + then + exit + fi + echo "Once initialization is done, you will need to reboot your" + echo "machine." + echo "" + else + echo "Once initialization is done, you will need to reboot your" + echo "machine." + echo "" + get_yesno $CONTINUE + echo "" + fi + + eval "echo killing NIS and/or NIS+ processes... $VERB" + svcadm disable network/nis/client + svcadm disable -t network/rpc/nisplus + eval "echo stopping nscd ... $VERB" + svcadm disable -t system/name-service-cache + eval "echo '' $VERB" + + setup_backup + + setup_server + + setup_domain + + setup_switch + + do_nisinit + + if [ "$SEC" = "2" ] + then + eval "echo -S 2 option specified, setting up security... $VERB" + setup_security root + elif [ "$SEC" = "x" ] + then + LOOKUP_DOM=${SEC_DOM_DOT:-$DOM} + if nistest '[cname='`nisdefaults -h`'],cred.org_dir'.$LOOKUP_DOM; + then + eval "echo credential exists for setting up security... $VERB" + setup_security root + fi + fi + + svcadm enable network/rpc/nisplus + eval "echo starting nscd ... $VERB" + svcadm enable system/name-service-cache + + remove_tmp + + # As this operation is likely configuration changing, restart the + # name-services milestone (such that configuration-sensitive services + # are in turn restarted). + /usr/sbin/svcadm restart milestone/name-services + + echo "Client initialization completed!!" + echo "Please reboot your machine for changes to take effect." +} + + +# +# +# User initialization Routine: +# ----------------------------- +# +# This section contains routine to initialize an user. +# init_user() - the main module to initialize an user +# + +# +# init_user(): initializes user +# +init_user() +{ + if [ $OS -eq 4 ] + then + echo "**ERROR: NIS+ client libraries are not available in 4.x" + echo " You must access NIS+ tables through NIS (YP)." + exit 1 + fi + + eval "echo initializing user information... $VERB" + WHO=`id | sed -e "s/uid=[0-9]*(\([^ )]*\)).*/\1/"` + if [ $WHO = "root" ] + then + echo "**ERROR: You cannot use the -u option as a root user." + print_usage + exit 1 + fi + + if [ "$SEC" = "2" ] + then + eval "echo -S 2 option specified, setting up security... $VERB" + setup_security user + eval "echo User initialization completed!! $VERB" + elif [ "$SEC" = "0" ] + then + echo "**WARNING: it is not necessary to initialize NIS+ users" + echo " for unauthenticated users." + elif [ "$SEC" = "x" ] + then + WHO=`id | sed -e "s/uid=[0-9]*(\([^ )]*\)).*/\1/"` + if nistest '[cname='$WHO.$DOM'],cred.org_dir'.$DOM; + then + eval "echo credential exists for setting up security... $VERB" + setup_security user + eval "echo User initialization completed!! $VERB" + else + echo "**WARNING: you do not have NIS+ credentials." + echo " You can either use NIS+ service as an unauthenticated user" + echo " or ask your network administrator to create a credentital for you." + fi + fi +} + + + + +# +# +# Restore Routines: +# ----------------- +# +# This section contains routines to restore the environment. +# restore_domain()- restores the domain +# restore_switch()- restores the switch +# mv_file() - moves file.$BACKUP file +# restore_file() - restores all the files +# restore_nis() - restores all NIS+ files +# restore_service() +# - restart either NIS or NIS+ daemons +# restore() - the main module to restore the environment +# restore_here() - same as restore() except it's called from init_client() +# restore_tmp() - restores temporary backup files for previous backups +# remove_tmp() - remove the backup files for the previous backups +# + +# +# restore_domain(): resets the "domainname" according to "/etc/defaultdomain". +# +restore_domain() +{ + DEFDOM=`sed -n -e "1p" /etc/defaultdomain` + NODOT=`echo $DEFDOM | sed -e "s/\.$//"` + echo "restoring domain to $DEFDOM..." + if [ "${NODOT}." != `nisdefaults -d` ] + then + $ECHO domainname ${NODOT} + fi + echo "" +} + + +# +# restore_switch(): restarts the switch if necessary. +# restarts nscd to ensure that it tracks the switch +# +restore_switch() +{ + eval "echo '' $VERB" + restart_instance network/rpc/keyserv:default + eval "echo restarting nscd ... $VERB" + restart_instance system/name-service-cache:default +} + + +# +# mv_file(): moves file.$BACKUP to file. +# $1 file name to be moved. +# +mv_file() +{ + $ECHO "mv -f $1.$BACKUP $1 > /dev/null" +} + + +# +# restore_file(): restores the file specified. +# $1 the file to be restored +# This routine will restore any file that has the backup file <file>.$BACKUP. +# The following table shows which backup files are created when the nisclient +# script is run based on the previous name service setup. +# +# Files | NIS -> NIS+ | NIS+ -> NIS+ +# -----------+---------------+---------------- +# rootkey | no | yes +# coldstart | no | yes +# client_info| no | yes +# switch | yes | maybe +# hosts | maybe | maybe +# domain | maybe | maybe +# +# Based on this table, those special files defined in $DEL_REST_FILES need to +# be restored if any files from $RESTORE_FILES were restored (specially switch), +# or if backup files for all the files in $DEL_REST_FILES were found. +# +restore_file() +{ + for FILE in $RESTORE_FILES + do + if [ -f $FILE.$BACKUP ] + then + if mv_file $FILE; + then + RESTORE=TRUE + echo " File $FILE restored!" + else + echo "**ERROR: could not restore file $FILE" + exit 1 + fi + fi + done + + for FILE in $REST_DOM_DIR + do + if [ -d $FILE.$BACKUP ] + then + if mv_file $FILE; + then + RESTORE=TRUE + echo " Directory $FILE restored!" + else + echo "**ERROR: could not restore directory $FILE" + exit 1 + fi + fi + done + + # ... check if the backup files exist for the special files in + # $DEL_REST_FILES + if [ "$RESTORE" != "TRUE" ] + then + for FILE in $DEL_REST_FILES + do + if [ ! -f $FILE.$BACKUP ] + then + return + fi + done + fi + + # ... restore special files: coldstart and rootkey + # If the backup file exists, then restore. Otherwise, remove + # the file. + # + for FILE in $DEL_REST_FILES + do + if [ -f $FILE.$BACKUP ] + then + if mv_file $FILE; + then + RESTORE="TRUE" + echo " File $FILE restored!" + else + echo "**ERROR: could not restore file $FILE" + exit 1 + fi + else + if [ -f $FILE ] + then + $ECHO "/bin/rm -f $FILE 2> /dev/null" + fi + fi + done +} + + +# +# restore_nis(): removes the cache files: NIS_SHARED_DIRCACHE and +# .pref_servers. +# +restore_nis() +{ + # ... remove the dircache file + for CACHE in $DELETE_FILES + do + if [ -f $CACHE ] + then + $ECHO "/bin/rm -f $CACHE 2> /dev/null" + fi + done +} + + +# +# restore_service(): starts ypbind by checking the switch file. +# If NIS+ service is to be restored, it will start nis_cachemgr. +# +restore_service() +{ + SWITCH=`grep "^[a-z]*:" /etc/nsswitch.conf` + + if [ $OS -eq 5 ] + then + echo $SWITCH | grep -s "\<nisplus\>" >/dev/null + if [ $? -eq 0 ] + then + echo "Will not start up ypbind process because the nsswitch.conf" + echo "file shows that you are not using NIS." + echo "restarting nis_cachemgr process..." + restart_instance network/rpc/nisplus:default + echo "" + return + else + svcadm disable network/rpc/nisplus:default + fi + fi + + echo $SWITCH | grep -s "\<nis\>" >/dev/null + if [ $? -eq 0 ] + then + echo "restarting NIS (YP) process..." + svcadm enable network/nis/client:default + echo "" + fi +} + + +# +# restore(): restores the previous network information service. +# +restore() +{ + if [ $OS -eq 4 ] + then + echo "**ERROR: NIS+ client libraries are not available in 4.x" + echo " You must access NIS+ tables through NIS (YP)." + exit 1 + fi + + WHO=`id | sed -e "s/uid=[0-9]*(\([^ )]*\)).*/\1/"` + if [ $WHO != "root" ] + then + echo "This script must be run as root ..." + exit 1 + fi + + echo "This script will restore the previous network information" + echo "service. It recovers all the files with the no_nisplus " + echo "extension and restarts either NIS+ or NIS client processes" + echo "according to the /etc/nsswitch.conf configuration file." + echo "" + echo "Once restore is done, you will need to reboot your machine." + get_yesno $CONTINUE + + svcadm disable -t network/rpc/nisplus + svcadm disable -t network/nis/client + + restore_file + + if [ "$RESTORE" = "TRUE" ] + then + restore_domain + + restore_switch + + restore_service + + echo "Client files restored!" + echo "Please reboot your machine for changes to take effect." + else + echo "Nothing to restore from!" + echo "This script can only restore from NIS+ setup done with" + echo "nisclient command." + fi + + # Again: a configuration changing operation, so restart the + # name-services milestone. + /usr/sbin/svcadm restart milestone/name-services +} + + +# +# restore_here(): same as restore() except it's called from init_client() +# when it fails to initialize a client. +# +restore_here() +{ + echo "" + echo "Restoring your network service..." + echo "" + + svcadm disable -t network/rpc/nisplus + svcadm disable -t network/nis/client + + restore_file + + if [ "$RESTORE" = "TRUE" ] + then + restore_domain + + restore_switch + + restore_service + + echo "Client files restored!" + fi + restore_tmp + + /usr/sbin/svcadm restart milestone/name-services + exit 1 +} + + +# +# restore_tmp(): restores temporary backup files for previous backups. +# +restore_tmp() +{ + # ... restore from temporary backup files for previous backup + for FILE in $RESTORE_FILES $DEL_REST_FILES + do + if [ -f $FILE.$BACKUP.$$ ] + then + eval "echo restoring the temporary backup file for $FILE... $VERB" + $ECHO mv -f $FILE.$BACKUP.$$ $FILE.$BACKUP > /dev/null + fi + done + for FILE in $REST_DOM_DIR + do + if [ -d $FILE.$BACKUP.$$ ] + then + eval "echo restoring the temporary backup directory for $FILE... $VERB" + $ECHO mv -f $FILE.$BACKUP.$$ $FILE.$BACKUP > /dev/null + fi + done + exit 1 +} + + +# +# remove_tmp(): remove the backup files for the previous backups +# +remove_tmp() +{ + for FILE in $RESTORE_FILES $DEL_REST_FILES + do + if [ -f $FILE.$BACKUP.$$ ] + then + eval "echo removing the temporary backup file for $FILE... $VERB" + $ECHO "/bin/rm -f $FILE.$BACKUP.$$" + fi + done + for FILE in $REST_DOM_DIR + do + if [ -d $FILE.$BACKUP.$$ ] + then + eval "echo removing the temporary backup directory for $FILE... $VERB" + $ECHO "/bin/rm -rf $FILE.$BACKUP.$$" + fi + done +} + + +# +# +# Adding client Routines: +# ----------------------- +# +# This section contains routines to add a client or a principal into +# credential table. +# check_perm() - check for the write permission of a given object +# check_domainname() +# - check to see if the domainname has at least 2 components +# check_rpc_domain() +# - compares the system default domain and the secured +# domain defines by the -k option +# check_domain() - check a directory is valid and get the servers' +# info +# check_type() - check the type of name specified +# add_LOCALcred() - adds the LOCAL credential into the credential table +# add_DEScred() - adds the DES credential into the credential table +# add_RSAcred() - adds the RSA credential into the credential table +# add_cred() - adds credentials into the credential table +# insert_cred() - the main module to add credentials +# + +# +# check_perm(): checks if we have write permission to the NIS+ table +# This should be replaced with nisaccess command when it's available +# $1 the table to be checked. +# +check_perm() +{ + if [ "$ECHO" = "echo" ] + then + return + fi + + echo "checking $1 permission..." + MYPRINC=`nisdefaults -p` + if [ $MYPRINC = "nobody" ] + then + if nistest -a n=c $1; + then + return + else + return 1 + fi + fi + + DUMMY=`nisls -ld $1` + if [ $? -ne 0 ] + then + exit 1 + fi + OWN=`echo $DUMMY | cut -d" " -f3` + if [ "$OWN" = $MYPRINC ] + then + if nistest -a o=c $1; + then + return + else + return 1 + fi + fi + + DUMMY=`nisls -ldg $1` + if [ $? -ne 0 ] + then + exit 1 + fi + OWN=`echo $DUMMY | cut -d" " -f3` + if [ ! -z "$OWN" ] + then + if nisgrpadm -t -s "$OWN" $MYPRINC; + then + if nistest -a g=c $1; + then + return + else + return 1 + fi + fi + fi + + if nistest -a w=c $1; + then + return + else + return 1 + fi +} + + +# +# check_domainname(): check validity of a domain name. Currently we check +# that it has at least two components. +# $1 the domain name to be checked +# +check_domainname() +{ + if [ ! -z "$1" ] + then + t=`expr "$1" : '[^.]\{1,\}[.][^.]\{1,\}'` + if [ "$t" = 0 ] + then + echo '**ERROR: invalid domain name ('$1')' + echo ' It must have at least two components.' + echo ' For example, "company.com.".' + print_usage + exit 1 + fi + fi +} + +# +# check_rpc_domain(): compares the secured domain against the system default +# domain. We check to make sure that the local system domain (defined in +# the /etc/defaultdomain or by the -d option if specified) is at the same +# level or below the domain specified by the -k option. This is required +# mainly to support server living in its own domain fix. +check_rpc_domain() +{ + if [ -z "$SEC_DOM_DOT" ] + then + return + fi + + if nistest -c $DOM le $SEC_DOM_DOT; + then + : + else + echo '***ERROR: invalid -k domain name ('$SEC_DOM_NODOT')' + echo ' The system default domain ('$NODOT') must be either the same' + echo ' as or a descendant of the Secure RPC domain ('$SEC_DOM_NODOT')' + echo ' where the key is stored.' + echo ' For example, if the system default domain is defined as' + echo ' (sub.company.com), then the -k domain can be defined as either' + echo ' sub.company.com or company.com.' + exit 1 + fi +} + + +# +# check_domain(): checks if it's a valid domain and get the server's info. +# +check_domain() +{ + if [ "$ECHO" = "echo" ] + then + return + fi + + echo "checking $DOM domain..." + if nistest -t D $DOM; + then + : + else + echo "**ERROR: Don't know about the domain \"$DOM\"." + echo " Please check the domain name." + exit 1 + fi + + niscat -o $DOM > /tmp/$PROG.$$ + if [ $? -ne 0 ] + then + rm -f /tmp/$PROG.$$ > /dev/null + exit 1 + fi + SERVER=`sed -n -e "s/ Name *: //p" /tmp/$PROG.$$ | sed -n -e "1p"` + rm -f /tmp/$PROG.$$ > /dev/null + SER_NAME=`echo $SERVER | cut -d. -f1` + SER_DOM=`echo $SERVER | cut -d. -f2-` + if [ $SER_DOM = $SER_NAME ] + then + $SER_DOM=$DOM + fi + + # + # Will have to check ipnodes.org_dir also when NIS+ server has IPv6 + # interfaces. + # + DUMMY=`nismatch -P $SER_NAME hosts.org_dir.$SER_DOM` + if [ $? -eq 0 ] + then + SERVER=`echo $DUMMY | sed -n -e "1p"` + SER_ADDR=`echo $SERVER | cut -d" " -f3` + else + DUMMY=`grep -v "^#" $HOSTS_FILE | grep -i -s $SER_NAME` + if [ $? -eq 0 ] + then + SERVER=`echo $DUMMY | sed -e "s/ / /g"` + SER_ADDR=`echo $SERVER | cut -d" " -f1` + else + echo "**ERROR: Couldn't get the server ${SER_NAME}'s address." + exit 1 + fi + fi + echo "" +} + + + +# +# check_type(): checks what type of credential to add into the credential +# table. It tries to match the given name agains the passwd.org_dir and +# hosts.org_dir tables. It assigns the value of "user" if the name is found +# in the passwd table and "host" if it's found in the hosts table. +# *** The name cannot be in both table *** +# $1 name +# +check_type() +{ + eval "echo checking info type for $NAME... $VERB" + UTYPE="FALSE" + HTYPE="FALSE" + ARG=$1 + LARG=$1 + NEWNAME="" + + # Checking both for IPV4 and IPV6 hosts + DUM1=`nismatch -P name=$ARG hosts.org_dir.$DOM`; + + if [ $? -ne 0 ] + then + if nistest ipnodes.org_dir.$DOM; + then + DUM1=`nismatch -P name=$ARG ipnodes.org_dir.$DOM`; + fi + fi + + if [ -n "$DUM1" ] + then + H_NAME=`echo $DUM1 | cut -d' ' -f1` + H_ALIAS=`echo $DUM1 | cut -d' ' -f2` + if [ "`tolower $H_NAME`" != "`tolower $H_ALIAS`" ] + then + echo "**WARNING: $H_ALIAS is an alias name for host $H_NAME." + echo "You cannot create credential for host alias." + get_yesno "Do you want to create the credential for $H_NAME?" + if [ $ANS = "y" -o $ANS = "Y" ] + then + NEWNAME=$H_NAME + LARG=$H_NAME + HTYPE="TRUE" + else + HTYPE="SKIP" # for error message handling + fi + else + HTYPE="TRUE" + fi + fi + + if nistest '[name='$LARG'],passwd.org_dir.'$DOM; + then + if [ $HTYPE = "TRUE" ] + then + echo "**ERROR: this name \"$LARG\" is in both the passwd and hosts tables." + echo " You cannot have an username same as the hostname." + return 1 + fi + UTYPE="TRUE" + fi + + if [ $UTYPE = "TRUE" ] + then + TYPE="user" + elif [ $HTYPE = "TRUE" ] + then + TYPE="host" + else + if [ $HTYPE != "SKIP" ] + then + echo "**ERROR: invalid name \"$LARG\"." + echo " It is neither an host nor an user name." + fi + return 1 + fi +} + + + + +# +# add_LOCALcred(): adds the LOCAL credential into the credential table. +# +add_LOCALcred() +{ + echo "adding LOCAL credential for $1..." + + DUMMY=`nismatch -P name=$1 passwd.org_dir.$DOM` + if [ $? -ne 0 ] + then + return 1 + fi + UID=`echo $DUMMY | cut -d: -f3` + + if [ $? -ne 0 ] + then + return 1 + fi + $ECHO nisaddcred -p $UID -P $1.$DOM local $DOM + return $? +} + + + + +# +# add_RSAcred(): adds the RSA credential into the credential table. +# $1 either username or hostname +# $2 password if specified +# +add_RSAcred() +{ + echo "adding RSA credential for $1..." + + if [ $# -eq 1 ] + then + $ECHO nisaddcred -p $1.$DOM -P $1.$DOM rsa $DOM + return $? + fi + + echo "nisaddcred -l $2 -p $1.$DOM -P $1.$DOM rsa $DOM" + return $? +} + +# +# add_DEScred(): adds the DES credential into the credential table. +# $1 either username or hostname +# $2 password if specified +# +add_DEScred() +{ + echo "adding DES credential for $1..." + + if [ $# -eq 2 ] + then + LPASS="-l $2" + else + LPASS="" + fi + if [ $TYPE = "user" ] + then + $ECHO nisaddcred $LPASS -p unix.${UID}@$NODOT -P $1.$DOM des $DOM + else + $ECHO nisaddcred $LPASS -p unix.$1@$NODOT -P $1.$DOM des $DOM + fi + return $? +} + + + + +# +# add_cred(): adds the credential. +# $* names to be added. +# +add_cred() +{ + while [ $# -ne 0 ] + do + NAME=$1 + check_type $NAME + + if [ $? -eq 0 ] + then + if [ ! -z "$NEWNAME" ] + then + NAME=$NEWNAME + fi + + DUMMY=`nismatch $NAME.$DOM cred.org_dir.$DOM > /dev/null` + if [ $? -eq 0 ] + then + if [ -z "$OWRITE" ] + then + echo ... overwriting the existing entry for principal $NAME! + else + echo ... principal $NAME already exist -- skipped! + shift + continue + fi + fi + + if [ $TYPE = "user" ] + then + if add_LOCALcred $NAME; + then + eval echo ... added LOCAL credential for $NAME. $VERB + else + eval "echo ... could not add LOCAL credential for $NAME. $VERB" + TYPE="fail" + fi + fi + + if [ $TYPE != "fail" ] + then + case $SEC in + 2) SECTYPE=DES;; + 3) SECTYPE=RSA;; + esac + + if add_${SECTYPE}cred $NAME $PAS; + then + eval "echo ... added $SECTYPE credential for $NAME. $VERB" + else + eval "echo ... could not add $SECTYPE credential for $NAME. $VERB" + TYPE="fail" + fi + fi + echo "" + + if [ $TYPE = "host" ] + then + DOIT=DOIT + elif [ $TYPE = "user" ] + then + USERDOIT=DOIT + fi + + fi + shift + done +} + + + +# +# insert_cred(): adds NIS+ client or principals credentials +# +insert_cred() +{ + if [ $# -eq 0 ] + then + echo "**ERROR: missing hostnames or usernames." + print_usage + exit 1 + fi + + if [ "$SEC" = "0" ] + then + echo "**WARNING: it is not necessary to create NIS+ credentials" + echo " for unauthenticated users." + return + fi + if [ "$SEC" != "2" -a "$SEC" != "x" ] + then + echo "**ERROR: invalid security level $SEC." + fi + SEC=2 + + case $SEC in + 2) MESS=DES;; + 3) MESS=RSA;; + esac + echo "" + echo "You will be adding $MESS credentials in domain $DOM for" + echo $* + echo "" + if [ -z "$OWRITE" ] + then + echo "** nisclient will overwrite existing entries in the credential" + echo "** table for hosts and users specified above." + else + echo "** nisclient will not overwrite any existing entries in the" + echo "** credential table." + fi + echo "" + get_yesno $CONTINUE + echo "" + + check_domain + + check_perm cred.org_dir.$DOM + if [ $? -ne 0 ] + then + echo "Sorry, no permission to create credentials!" + exit 1 + fi + echo "" + + add_cred $* + + if [ "$DOIT" = "DOIT" ] + then + if [ $GEN = "TRUE" ] + then + $ECHO "echo '#!/bin/sh' > $GENFILE" + $ECHO "echo '#' > $GENFILE" + $ECHO "echo '' > $GENFILE" + $ECHO "echo "nisclient -i -h $SER_NAME -a $SER_ADDR -d $DOM" > $GENFILE" + fi + echo "For all new NIS+ clients added, you will need to run the" + echo "following on the client's machine:" + echo "nisclient -i -h $SER_NAME -a $SER_ADDR -d $DOM" + fi + + if [ "$USERDOIT" = "DOIT" ] + then + echo "" + echo "For all new NIS+ users added, you will need to update" + echo "their keys on all machines that they are currently logged" + echo "in by running keylogin(1), chkey(1), or nisclient(1M)." + fi +} + + + +# +# +# * * * MAIN * * * +# + +# Display the obsolescence message in all the cases +echo "" +echo "******** ******** WARNING ******** ********" +echo "NIS+ might not be supported in a future release. Tools to aid" +echo "the migration from NIS+ to LDAP are available in the Solaris 9" +echo "operating environment. For more information, visit" +echo "http://www.sun.com/directory/nisplus/transition.html" +echo "******** ******** ******* ******** ********" +echo "" + +init + +umask 22 + +parse_arg $* +shift $? + +check_domainname "$DOM" + +case $ACTION in +"init") + init_client;; +"user") + init_user;; +"create") + insert_cred $*;; +"restore") + restore;; +*) + echo "**ERROR: you must specify one of the these options: -c, -i, -u, -r." + print_usage + exit 1 +esac diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisctl.c b/usr/src/cmd/rpcsvc/nis/bin/nisctl.c new file mode 100644 index 0000000000..1c541a0437 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/bin/nisctl.c @@ -0,0 +1,222 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisctl.c + * + * Copyright (c) 1988-1992 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisctl.c + * + * This program is used to control the operation of NIS+ servers. + * It may be used to turn debugging on and off, and to flush + * various caches and print statistics. + */ + +#include <stdio.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> + +extern int optind; +extern char *optarg; + +nis_tag mytags[64]; + +usage(s) + char *s; +{ + fprintf(stderr, +"usage: %s [-M] [-s] [-v on|off] [-f dgot [-n obj_name]] [-H host] [domain]\n", + s); + fprintf(stderr, "\t-M: heap memory\n"); + fprintf(stderr, "\t-s: statistics\n"); + fprintf(stderr, "\t-v: verbose mode\n"); + fprintf(stderr, + "\t-f: flush cache (dir, group, object, table) [-n obj_name]\n"); + fprintf(stderr, "At least one of the above options must be " + "specified\n"); + exit(1); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + int i; + nis_error error; + nis_tag *res; + nis_result *lres; + nis_object *obj; + nis_server **srvs, **x; + nis_name domain; + char dname[1024], *s; + char server[NIS_MAXNAMELEN]; + + server[0] = NULL; + i = 0; + + /* nisctl cannot be used without any options */ + if (argc == 1) { + usage(argv[0]); + exit(1); + } + + while ((c = getopt(argc, argv, "Msv:n:f:H:")) != -1) { + switch (c) { + case 'M' : + mytags[0].tag_val = ""; + mytags[0].tag_type = TAG_HEAP; + i = 1; + break; + case 'f' : + for (s = optarg; *s; s++) { + mytags[i].tag_val = "flush"; + switch (*s) { + case 'g': + mytags[i].tag_type = + TAG_GCACHE_ALL; + break; + case 'd': + mytags[i].tag_type = + TAG_DCACHE_ALL; + break; + case 'o': + mytags[i].tag_type = + TAG_OCACHE; + break; + case 't': + mytags[i].tag_type = + TAG_TCACHE_ALL; + break; + default: + usage(argv[0]); + } + i++; + } + break; + case 'n': + /* specifies a particular entry to flush */ + if (i == 0) { + fprintf(stderr, "-f option required\n"); + usage(argv[0]); + } + if (i > 1) { + fprintf(stderr, + "only one option allowed\n"); + usage(argv[0]); + } + i = 0; /* kludge - reset it */ + mytags[i].tag_val = optarg; + switch (mytags[i].tag_type) { + case TAG_GCACHE_ALL: + mytags[i].tag_type = TAG_GCACHE_ONE; + break; + case TAG_TCACHE_ALL: + mytags[i].tag_type = TAG_TCACHE_ONE; + break; + case TAG_DCACHE_ALL: + mytags[i].tag_type = TAG_DCACHE_ONE; + break; + case TAG_OCACHE: + fprintf(stderr, + "Object flushing not supported\n"); + usage(argv[0]); + default: + usage(argv[0]); + } + i = 1; /* reset it back */ + break; + case 'v' : + if (strcmp(optarg, "1") == 0) + mytags[i].tag_val = "on"; + else if (strcmp(optarg, "0") == 0) + mytags[i].tag_val = "off"; + else + mytags[i].tag_val = optarg; + mytags[i++].tag_type = TAG_DEBUG; + break; + case 's' : + mytags[i].tag_val = optarg; + mytags[i++].tag_type = TAG_STATS; + break; + case 'H' : + if (!optarg) + usage(argv[0]); + if (optarg[strlen(optarg) -1] == '.') + strcpy(server, optarg); + else + sprintf(server, "%s.%s", optarg, + nis_local_directory()); + break; + default : + usage(argv[0]); + exit(1); + } + } + if (optind < argc) + domain = argv[optind]; + else + domain = nis_local_directory(); + + lres = nis_lookup(domain, EXPAND_NAME); + if (lres->status != NIS_SUCCESS) { + fprintf(stderr, "\"%s\" : %s\n", domain, + nis_sperrno(lres->status)); + exit(1); + } + obj = lres->objects.objects_val; + sprintf(dname, "%s.%s", obj->zo_name, obj->zo_domain); + srvs = (nis_server **)nis_getservlist(dname); + if (! srvs) { + fprintf(stderr, + "Unable to get list of servers serving \"%s\"\n", dname); + exit(1); + } + + for (x = srvs; *x; x++) { + if (server[0] && nis_dir_cmp(server, (*x)->name) != SAME_NAME) + continue; + printf("Setting State on : '%s'\n", (*x)->name); + error = nis_servstate(*x, mytags, i, &res); + if (error == NIS_SUCCESS) { + if (res == NULL) { + fprintf(stderr, "Set server state failed\n"); + nis_freeservlist(srvs); + exit(1); + } + printf("%s\n", res->tag_val); + } else { + fprintf(stderr, "Set server state failed : %s\n", + nis_sperrno(error)); + nis_freeservlist(srvs); + exit(1); + } + nis_freetags(res, i); + } + nis_freeservlist(srvs); + exit(0); +} diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisopaccess.sh b/usr/src/cmd/rpcsvc/nis/bin/nisopaccess.sh new file mode 100644 index 0000000000..ca48dee411 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/bin/nisopaccess.sh @@ -0,0 +1,298 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 (c) 1999 by Sun Microsystems, Inc. +# All rights reserved. + +#pragma ident "%Z%%M% %I% %E% SMI" + +EXIT=255 +TBLNAME=proto_op_access +TBLTYPE=proto_op_access_tbl +VERBOSE=0 +REMOVE=0 +LIST=0 + +print_usage() +{ + echo "Usage: $0 [-v] directory operation rights" + echo " $0 [-r] [-v] directory operation" + echo " $0 [-l] [-v] directory [operation]" +} + +# no_dot(): check if arg has a trailing dot. +no_dot() +{ + if [ "`echo $1 | sed -e 's/.*\(.\)$/\1/'`" != "." ] + then + return 0 + fi + return 1 +} + +# parse_opt(): Parse options. Returns the number of arguments to shift +# in order to get to the non-option arguments. +parse_opt() +{ + while getopts "vrl" ARG + do + case $ARG in + v) VERBOSE=1;; + r) REMOVE=1;; + l) LIST=1;; + \?) print_usage + exit $EXIT;; + *) print_usage + exit $EXIT + esac + done + return `expr $OPTIND - 1` +} + +# unknown_op(): Check that operation (NIS_PING etc.) is known +unknown_op() +{ + case $OPERATION in + NIS_CHECKPOINT|nis_checkpoint|CHECKPOINT|checkpoint) + OPERATION=NIS_CHECKPOINT;; + NIS_CPTIME|nis_cptime|CPTIME|cptime) + OPERATION=NIS_CPTIME;; + NIS_MKDIR|nis_mkdir|MKDIR|mkdir) + OPERATION=NIS_MKDIR;; + NIS_PING|nis_ping|PING|ping) + OPERATION=NIS_PING;; + NIS_RMDIR|nis_rmdir|RMDIR|rmdir) + OPERATION=NIS_RMDIR;; + NIS_SERVSTATE|nis_servstate|SERVSTATE|servstate) + OPERATION=NIS_SERVSTATE;; + NIS_STATUS|nis_status|STATUS|status) + OPERATION=NIS_STATUS;; + *) return 0;; + esac + return 1 +} + +# print_rights(): parse and print access rights +print_rights() +{ + op=$1 + shift + rights="<unknown>" + owner="<none>" + group="<none>" + while [ $# -gt 0 ]; do + case $1 in + Owner) + shift + if [ $1 = ":" ]; then + shift + if [ $1 != "Group" ]; then + owner=$1 + shift + fi + fi;; + Group) + shift + if [ $1 = ":" ]; then + shift + if [ $1 != "Access" ]; then + group=$1 + shift + fi + fi;; + Access) + shift + if [ $1 = "Rights" ]; then + shift + if [ $1 = ":" ]; then + shift + rights=$1 + while [ $# -gt 0 ]; do + shift + done + fi + fi;; + *) + shift;; + esac + done + echo "$op\t $rights\t $owner\t $group" + return $? +} + +# print_op(): print access rights for the specified operation +print_op() +{ + nismatch op=$1 $TBLNAME.$DIRECTORY > /dev/null 2>&1 + if [ $? -eq 0 ]; then + print_rights $1 `niscat -oM \[op=$1\]$TBLNAME.$DIRECTORY` + fi + return $? +} + +# Main + +PATH=/usr/lib/nis:/usr/sbin:/usr/bin + +# Parse the options, if any +parse_opt $* +shift $? + +# Check that we've got the correct number of arguments +case $# in +1) if [ $LIST -eq 0 ]; then + print_usage + exit $EXIT + fi + DIRECTORY=$1;; + +2) if [ $LIST -eq 0 -a $REMOVE -eq 0 ]; then + print_usage + exit $EXIT + elif [ $LIST -ne 0 -a $REMOVE -ne 0 ]; then + echo "The -l and -r options are mutually exclusive" + exit $EXIT + fi + if [ $LIST -ne 0 ]; then + LIST=2 + fi + DIRECTORY=$1 + OPERATION=$2 + if unknown_op; then + echo "Unknown operation $OPERATION" + exit $EXIT + fi;; + +3) if [ $LIST -ne 0 -o $REMOVE -ne 0 ]; then + print_usage + exit $EXIT + fi + DIRECTORY=$1 + OPERATION=$2 + if unknown_op; then + echo "Unknown operation $OPERATION" + exit $EXIT + fi + RIGHTS=$3;; + +*) print_usage + exit $EXIT;; +esac + +# If no trailing dot in directory name, add the domain name +if no_dot $DIRECTORY; +then + DIRECTORY=$DIRECTORY.`domainname`. +fi + +# Does the directory exist ? +niscat -o $DIRECTORY > /dev/null 2>&1 +STAT=$? +if [ $STAT -ne 0 ]; then + echo "$DIRECTORY: no such NIS+ directory" + exit $STAT +fi + +# Does the table exist ? +niscat -o $TBLNAME.$DIRECTORY > /dev/null 2>&1 +TBLEXISTS=$? + +# List all or just one operation +if [ $LIST -eq 1 ]; then + if [ $TBLEXISTS -ne 0 ]; then + echo "No operation access table for $DIRECTORY" + exit 0 + fi + if [ $VERBOSE -eq 1 ]; then + echo "Listing access for all operations for $DIRECTORY" + fi + echo "Operation\t n---o---g---w---\t Owner\t\t\t Group" + echo "" + for OP in NIS_CHECKPOINT NIS_CPTIME NIS_MKDIR NIS_PING NIS_RMDIR \ + NIS_SERVSTATE NIS_STATUS; do + print_op $OP + STAT=$? + done + exit $STAT +elif [ $LIST -eq 2 ]; then + if [ $TBLEXISTS -ne 0 ]; then + echo "No operation access table for $DIRECTORY" + exit 0 + fi + if [ $VERBOSE -eq 1 ]; then + echo "Listing access for $OPERATION for $DIRECTORY" + fi + print_op $OPERATION + exit $? +fi + +# Remove an operation +if [ $REMOVE -eq 1 ]; then + if [ $TBLEXISTS -ne 0 ]; then + echo "No operation access table for $DIRECTORY" + exit 0 + fi + nismatch op=$OPERATION $TBLNAME.$DIRECTORY > /dev/null 2>&1 + if [ $? -ne 0 ]; then + if [ $VERBOSE -eq 1 ]; then + echo "No $OPERATION access control for $DIRECTORY" + fi + exit 0 + else + if [ $VERBOSE -eq 1 ]; then + echo "Removing $OPERATION access control for $DIRECTORY" + fi + nistbladm -R \[op=$OPERATION\]$TBLNAME.$DIRECTORY + exit $? + fi +fi + +# Create the table if it doesn't exist already +if [ $TBLEXISTS -ne 0 ]; then + if [ $VERBOSE -eq 1 ]; then + echo "Creating access control table in $DIRECTORY" + fi + nistbladm -c $TBLTYPE op=SI,o=rmcd,g=r,w=r,n=r subop=SI,o=rmcd,g=r,w=r,n=r $TBLNAME.$DIRECTORY + STAT=$? + if [ $STAT -ne 0 ]; then + exit $STAT + fi +fi + +# If no entry for the operation, create it with requested rights +nismatch op=$OPERATION $TBLNAME.$DIRECTORY > /dev/null 2>&1 +if [ $? -ne 0 ]; then + if [ $VERBOSE -eq 1 ]; then + echo "Creating $OPERATION access control entry for $DIRECTORY" + fi + nistbladm -a -D access=$RIGHTS op=$OPERATION $TBLNAME.$DIRECTORY + exit $? +fi + +# Modify existing entry +if [ $VERBOSE -eq 1 ]; then + echo "Changing $OPERATION access control entry for $DIRECTORY" +fi +nischmod $RIGHTS \[op=$OPERATION\]$TBLNAME.$DIRECTORY + +exit $? diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisping.c b/usr/src/cmd/rpcsvc/nis/bin/nisping.c new file mode 100644 index 0000000000..dab6f7ff4b --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/bin/nisping.c @@ -0,0 +1,355 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisping.c + * + * This program will ping a server in an attempt to get it to resynchronize + * with the master NIS+ server. It also performs database checkpoints. + */ + +#include <stdio.h> +#include <sys/types.h> +#include <rpc/types.h> +#include <rpc/rpc.h> +#include <rpc/clnt.h> +#include <rpcsvc/nis.h> +#include <sys/time.h> + +extern void __nis_pingproc(nis_server *, nis_name, u_long); +extern bool_t xdr_nis_name(); +extern bool_t xdr_cp_result(); + +#define ROOT_OBJ "root.object" +extern nis_name __nis_local_root(); + +#define NIS_PROG 100300 +#define NIS_VERSION 3 +#define NIS_CPTIME 18 +#define NIS_CHECKPOINT 19 + +/* + * Return values (from the man page): + * -1 No servers were contacted, or the server + * specified by the -H switch could not be con- + * tacted. + * + * 0 Success. + * + * 1 Some, but not all, servers were successfully + * contacted. + */ +#define PING_SUCCESS 0 +#define PING_SOME 1 +#define PING_NONE -1 + +/* + * nis_checkpoint() + * + * This function will ask the indicated replicate to checkpoint itself + */ +static nis_error +nis_checkpnt(srv, name) + nis_server *srv; + nis_name name; +{ + CLIENT *clnt; + enum clnt_stat status; + cp_result res; + struct timeval tv; + + clnt = nis_make_rpchandle(srv, 0, NIS_PROG, NIS_VERSION, + ZMH_DG|ZMH_AUTH|ZMH_NOFALLBACK, 1024, 512); + /* If we can't contact it, return the safe answer */ + if (! clnt) { + return (NIS_NAMEUNREACHABLE); + } + + tv.tv_sec = 10; + tv.tv_usec = 0; + status = clnt_call(clnt, NIS_CHECKPOINT, xdr_nis_name, (char *) &name, + xdr_cp_result, (char *) &res, tv); + if (status != RPC_SUCCESS) { + printf("nisping: RPC error on server %s, error %s\n", + srv->name, clnt_sperrno(status)); + res.cp_status = NIS_RPCERROR; + } + clnt_destroy(clnt); + return (res.cp_status); +} + +/* + * nis_cptime() + * + * This function will ask the indicated replicate for the last + * update it has seen to the given directory. + */ +static nis_error +nis_cptime(srv, name, utime) + nis_server *srv; + nis_name name; + u_long *utime; +{ + CLIENT *clnt; + enum clnt_stat status; + struct timeval tv; + nis_error res; + + clnt = nis_make_rpchandle(srv, 0, NIS_PROG, NIS_VERSION, + ZMH_DG|ZMH_AUTH|ZMH_NOFALLBACK, 1024, 512); + + /* If we can't contact it, return the safe answer */ + if (! clnt) { + *utime = 0; + return (NIS_RPCERROR); + } + /* Only wait 10 seconds */ + tv.tv_sec = 10; + tv.tv_usec = 0; + status = clnt_call(clnt, NIS_CPTIME, xdr_nis_name, (char *) &name, + xdr_u_long, (char *) utime, tv); + res = (status != RPC_SUCCESS) ? NIS_RPCERROR : NIS_SUCCESS; + clnt_destroy(clnt); + return (res); +} + +static void +usage(s) + char *s; +{ + fprintf(stderr, "usage: %s [-uf] [-H hostname] [-r|directory]\n", s); + fprintf(stderr, " %s -C [-a] [-H hostname] [directory]\n", s); + + exit(PING_NONE); +} + +static +int +match_host(char *host, char *target) +{ + int len = strlen(host); + + if (strncasecmp(host, target, len) == 0 && + (target[len] == '.' || target[len] == '\0')) + return (1); + + return (0); +} + +extern int optind; +extern char *optarg; + + +main(argc, argv) + int argc; + char *argv[]; +{ + nis_server *srvs; + nis_object *obj; + int c; + int i, ns, force = 0, uponly = 0; + int checkpoint_all = 0; + int chkpnt = 0; + u_long updtm, reptm; + nis_error status; + nis_name domain; + char dname[1024], obj_desc[1024]; + char *host = NULL; + nis_result *res; + int root_object = 0; + int flag = EXPAND_NAME|USE_DGRAM; + int tries = 0; + int successes = 0; + + while ((c = getopt(argc, argv, "CH:ufra")) != -1) { + switch (c) { + case 'f': + force = 1; + break; + case 'u': + uponly = 1; + break; + case 'C' : + chkpnt = 1; + break; + case 'H' : + host = optarg; + break; + case 'r' : + root_object = 1; + break; + case 'a' : + checkpoint_all = 1; + break; + default : + usage(argv[0]); + } + } + + if (optind < argc) { + if (root_object) + usage(argv[0]); + domain = argv[optind]; + } else + domain = nis_local_directory(); + + if (chkpnt == 0 && checkpoint_all) + usage(argv[0]); + + if (root_object) { + if (chkpnt) { + fprintf(stderr, + "%s: no need to checkpoint root object.\n", + argv[0]); + usage(argv[0]); + } + domain = __nis_local_root(); + if (domain == 0) { + fprintf(stderr, + "%s: cannot get name of root directory.\n", + argv[0]); + exit(PING_NONE); + } + } + + if (!uponly) + flag += MASTER_ONLY; + res = nis_lookup(domain, flag); + if (res->status != NIS_SUCCESS) { + fprintf(stderr, "%s: %s\n", domain, nis_sperrno(res->status)); + exit(PING_NONE); + } + obj = res->objects.objects_val; + sprintf(dname, "%s.%s", obj->zo_name, obj->zo_domain); + if (__type_of(obj) != NIS_DIRECTORY_OBJ) { + fprintf(stderr, "\"%s\" : not a directory.\n", dname); + exit(PING_NONE); + } + srvs = obj->DI_data.do_servers.do_servers_val; + ns = obj->DI_data.do_servers.do_servers_len; + + if (root_object) + strcpy(obj_desc, "root object"); + else + sprintf(obj_desc, "directory %s", dname); + + if (host) { /* if a specific host has been specified */ + for (i = 0; i < ns; ++i) { + if (match_host(host, srvs[i].name)) + break; + } + if (i == ns) { + fprintf(stderr, "Host %s does not serve \"%s\".\n", + host, obj_desc); + exit(PING_NONE); + } + } + + /* set real target name for root object. */ + if (root_object) + strcpy(dname, ROOT_OBJ); + + if (! chkpnt && (ns == 1) && ! uponly) { + printf("\"%s\" : no replicas\n", root_object? obj_desc : dname); + exit(PING_NONE); + } + + if (chkpnt) + printf("Checkpointing %s serving directory \"%s\" :\n", + (host) ? "host" : "replicas", dname); + else if (uponly) + printf("Last updates for \"%s\" : \n", obj_desc); + else + printf("Pinging %s serving \"%s\" :\n", + (host) ? "host" : "replicas", obj_desc); + + printf("Master server is \"%s\"\n", srvs[0].name); + status = nis_cptime(&srvs[0], dname, &updtm); + if (status != NIS_SUCCESS) + printf("\tUnable to fetch update time from master server.\n"); + else if (!updtm) + printf("\tNo last update time available for \"%s\".\n", + obj_desc); + else + printf("\tLast update occurred at %s\n", + ctime((time_t *)&updtm)); + /* + * Need to increment success count if -H <master> or only 1 server. + */ + if ((!host || match_host(host, srvs[0].name)) && + (status == NIS_SUCCESS)) { + tries++; + successes++; + } + for (i = 0; i < ns; i++) { + if (host && !match_host(host, srvs[i].name)) + continue; + if ((i == 0) && ! chkpnt) + continue; + printf("%s server is \"%s\"\n", (i) ? "Replica" : "Master", + srvs[i].name); + if (chkpnt) { + if (checkpoint_all) + status = nis_checkpnt(&srvs[i], ""); + else + status = nis_checkpnt(&srvs[i], dname); + + if (status != NIS_SUCCESS) { + printf("checkpoint failed : %s\n", + nis_sperrno(status)); + } else + printf("checkpoint scheduled on \"%s\".\n", + srvs[i].name); + } else { + status = nis_cptime(&srvs[i], dname, &reptm); + if (status == NIS_SUCCESS) { + if (!reptm) + printf( + "\tNo last update available for \"%s\".\n", + obj_desc); + else + printf("\tLast Update seen was %s\n", + ctime((time_t *)&reptm)); + if (! uponly && updtm && + ((reptm < updtm) || force)) { + printf("\tPinging ... \"%s\"\n", + srvs[i].name); + __nis_pingproc(&srvs[i], dname, updtm); + } + } else + printf("\tUnavailable.\n\n"); + } + tries++; + if (status == NIS_SUCCESS) successes++; + } + if (successes == 0) + exit(PING_NONE); + if (successes < tries) + exit(PING_SOME); + exit(PING_SUCCESS); +} diff --git a/usr/src/cmd/rpcsvc/nis/bin/nispopulate.sh b/usr/src/cmd/rpcsvc/nis/bin/nispopulate.sh new file mode 100644 index 0000000000..360c4d3d1f --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/bin/nispopulate.sh @@ -0,0 +1,1576 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 (c) 1992-1997 by Sun Microsystems, Inc. +# All rights reserved. +# +# ident "%Z%%M% %I% %E% SMI" +# +# nispopulate -- script to populate NIS+ tables + + +# +# print_usage(): ask user if they want to see detailed usage msg. +# +print_usage() +{ + echo + get_yesno " Do you want to see more information on this command? \\n\ + (type 'y' to get a detailed description, 'n' to exit)" + if [ $ANS = "y" -o $ANS = "Y" ] + then + print_more + fi + echo "" + exit 1 +} + +print_more() +{ + more << EOF +USAGE: + o to populate the table from NIS maps: + $PROG -Y [-f] [-n] [-S 0|2] [-u] [-v] [-x] -h <NIS_server_host> + [-a <NIS_server_addr>] [-l <network_passwd>] + [-d <NIS+_domain>] -y <NIS_domain> [table] ... + + o to populate the table from files: + $PROG -F [-f] [-u] [-v] [-x] [-S 0|2] [-d <NIS+_domain>] + [-l <network_passwd>] [-p <directory_path>] [table] ... + + o to populate the credential table from hosts/passwd tables: + $PROG -C [-f] [-v] [-x] [-d <NIS+_domain>] + [-l <network_passwd>] [hosts|passwd] +OPTIONS: + -a <NIS_server_addr> + specifies the IP address for the NIS server. This option is + *ONLY* used with the "-Y" option. + + -C populate the NIS+ credential table from passwd and hosts tables + using DES authentication (security level 2). + + -d <NIS+_domain> + specifies the NIS+ domain. The default is the local domain. + + -F populate NIS+ tables from files. + + -f forces this script to populate the NIS+ tables without prompting + for confirmation. + + -h <NIS_server_host> + specifies the NIS server hostname to copy the NIS map from. This + is *ONLY* used with the "-Y" option. This host must be already + exist in either the NIS+ hosts table or /etc/hosts file. If the + hostname is not defined, this script will prompt you for its IP + address. + + -l <network_passwd> + specifies the network password for populating the NIS+ credential + table. This is *ONLY* used when you are populating the hosts and + passwd tables. The default passwd is "nisplus". + + -n do not overwrite local NIS maps in /var/yp/<NISdomain> + directory if they already exist. The default is to overwrite + the existing NIS maps in the local /var/yp/<NISdomain> + directory. This is *ONLY* used with the "-Y" option. + + -p <directory_path> + specifies the directory path where the files are stored. + This is *ONLY* used with the "-F" option. The default is the + current working directory. + + -S 0|2 + specifies the authentication level for the NIS+ client. Level 0 is + for unauthenticated clients and no credentials will be created for + users and hosts in the specified domain. Level 2 is for authenticated + (DES) clients and DES credentials will be created for users and hosts + in the specified domain. The default is to set up with level 2 + authentication (DES). There is no need to run nispopulate with -C + for level 0 authentication. + + -u updates the NIS+ tables (ie., adds, deletes, modifies) from either + files or NIS maps. This option should be used to bring an NIS+ + table up to date when there are only a small number of changes. + The default is to add to the NIS+ tables without deleting any + existing entries. Also, see the -n option for updating NIS+ + tables from existing maps in the /var/yp directory. + + -v runs this script in verbose mode. + + -x turns the "echo" mode on. This script just prints the commands + that it would have executed. The commands are printed with + leading "+++". Note that the commands are not actually executed. + The default is off. + + -Y populate the NIS+ tables from NIS maps. + + -y <NIS_domain> + specifies the NIS domain to copy the NIS maps from. This is + *ONLY* used with the "-Y" option. The default domain name is + the same as the local domain name. +EOF +} + +print_CFY_usage() +{ + if [ "$ERRCFY_OPTION" = "$CFY_OPTION" ] + then + echo "**WARNING: You have specified the '$CFY_OPTION' option twice." + return + fi + echo + echo "**ERROR: You have specified the '$ERRCFY_OPTION' option after" + echo " having selected the '$CFY_OPTION' option." + echo "Please select only one of these options: '-C', '-F' or '-Y'." + print_usage + exit 1 +} + + +#NOTE: +#Standard NIS+ table names are: +#$MAPS shadow +#(shadow map is only used when populating from files) + + +# +# Generic Routines: +# ----------------- +# +# This section contains general routines. +# get_ans() - prompts the message and waits for an input +# get_yesno() - prompts the message and waits for a Y or N answer +# tolower() - converts upper to lower case. +# + +# +# get_ans(): gets an asnwer from the user. +# $1 instruction/comment/description/question +# $2 default value +# +get_ans() +{ + if [ -z "$2" ] + then + if [ $OS -eq 5 ] + then + echo "$1 \c" + else + echo -n "$1 " + fi + else + if [ $OS -eq 5 ] + then + echo "$1 [$2] \c" + else + echo -n "$1 [$2] " + fi + fi + read ANS + if [ -z "$ANS" ] + then + ANS=$2 + fi +} + + +########## get_yesno constants: +## +## There are two very common phrases passed to get_yesno: +## These have been parameterized to provide "ease of use". +## Thus, there are three message "types" which are possible: +## --$CONTINUE: "Do you want to continue? (type 'y' to continue, 'n' to exit this script)" +## --$CONFIRM: "Is this information correct? (type 'y' to accept, 'n' to change)" +## --actual string is passed. +## +## If the message is $CONTINUE, get_yesno will exit if the response is no. +## +########### +CONTINUE=2 +CONFIRM=1 +# +# get_yesno(): get the yes or no answer. +# $1 message type or message. +# +# +# +get_yesno() +{ + ANS="X" + + case $1 in + $CONTINUE ) + INFOTEXT="Do you want to continue? (type 'y' to continue, 'n' to exit this script)" + ;; + $CONFIRM ) + INFOTEXT="Is this information correct? (type 'y' to accept, 'n' to change)" + ;; + *) INFOTEXT="$1" + ;; + esac + + while [ "$ANS" != "y" -a "$ANS" != "n" -a "$ANS" != "Y" -a "$ANS" != "N" ] + do + get_ans "$INFOTEXT" "" + done + + if [ "$1" = "$CONTINUE" ]; then + if [ $ANS = "n" -o $ANS = "N" ] + then + exit + fi + fi + +} + + +# +# tolower(): converts upper to lower case. +# $1 string to convert +# +tolower() +{ + echo "$1" | tr '[A-Z]' '[a-z]' +} + + + +# +# Common Routines: +# --------------- +# +# This section contains common routines for the script. +# init() - initializes all the variables +# parse_arg() - parses the command line arguments +# check_perm() - checks for the write permission for an object +# get_security() - gets the security information +# update_info() - updates the setup information +# print_info() - prints the setup information +# confirm() - prompts the user for confirmation +# is_standard() - check if it's a member of the standard maps +# + +# +# init(): initializes variables and options +# +init() +{ + + PROG=`basename $0` + CFY_OPTION="" # Keep track of Y, F or C option for error msgs. + ERRCFY_OPTION="" # 2nd C, F or Y error option. + VERB='> /dev/null' # NULL or "> /dev/null" + VERB_OPT='' # verbose option for nisaddent + ECHO="eval" # eval or echo + BACKUP="no_nisplus" + DOM=`nisdefaults -d` # domainname with DOT + NODOT=`echo $DOM | sed -e "s/\.$//"` + YPDOM="$NODOT" # YPdomain + + ACTION="" # master or replica + FORCE="" # NULL or TRUE + YPHOST="" # NULL or <YPhostname> + DIRPATH="" # directory path where the files are + PASSWD="nisplus" # credential password + UPDATE="" # nisaddent update option + NEW="overwrite" # overwrite local NIS maps + NSSWITCH="/etc/nsswitch.conf" + + STANDARD="" # prints either standard or non-standard + + OS=`uname -r | cut -d. -f1` + SEC=2 # security level + TMPDIR=${TMPDIR:-/tmp} # temporary directory + MAPS="auto_master auto_home ethers group hosts ipnodes networks passwd protocols services rpc netmasks bootparams netgroup aliases timezone" + MAPS="$MAPS auth_attr exec_attr prof_attr user_attr audit_user" + + if [ $OS -eq 5 ] + then + HOSTS_FILE=/etc/inet/hosts + PATH=/usr/lib/nis:/usr/sbin:/usr/bin:/usr/lib/netsvc/yp:$PATH; export PATH + else + HOSTS_FILE=/etc/hosts + PATH=/usr/etc/nis:/usr/etc:/usr/bin:/usr/etc/yp:$PATH; export PATH + fi + +} + + + + +# +# parse_arg(): parses the input arguments. +# It returns the number to be shift in the argument list. +# +parse_arg() +{ + while getopts "a:Cd:Ffh:l:np:S:uvxYy:" ARG + do + case $ARG in + a) ADDR=$OPTARG;; + C) if [ -z "$ACTION" ] + then + ACTION="cred" + CFY_OPTION="-C" + else + ERRCFY_OPTION="-C" + print_CFY_usage + fi;; + + d) if [ "`echo $OPTARG | sed -e 's/.*\(.\)$/\1/'`" != "." ] + then + NODOT=$OPTARG + DOM=${NODOT}. + else + DOM=$OPTARG + NODOT=`echo $DOM | sed -e "s/\.$//"` + fi ;; + F) if [ -z "$ACTION" ] + then + ACTION="file" + CFY_OPTION="-F" + else + ERRCFY_OPTION="-F" + print_CFY_usage + fi;; + f) FORCE="TRUE";; + h) YPHOST=$OPTARG;; + l) PASSWD=$OPTARG;; + n) NEW="";; + p) if [ -d $OPTARG ] + then + DIRPATH=$OPTARG + else + echo "**ERROR: directory $OPTARG does not exist." + exit 1 + fi;; + S) VALUE=`expr "$OPTARG" : "\([02]\)"` + if [ -z "$VALUE" ] + then + echo "**ERROR: invalid security level." + echo " It must be either 0 or 2." + echo " This can only be used with -F and -Y options." + exit 1 + fi + SEC=$VALUE;; + u) UPDATE="-m";; + v) VERB="" + VERB_OPT="-v";; + x) ECHO="echo +++";; + Y) if [ -z "$ACTION" ] + then + ACTION="yp" + CFY_OPTION="-Y" + else + ERRCFY_OPTION="-Y" + print_CFY_usage + fi;; + y) if [ "`echo $OPTARG | sed -e 's/.*\(.\)$/\1/'`" != "." ] + then + YPDOM=$OPTARG + else + YPDOM=`echo $OPTARG | sed -e "s/\.$//"` + fi ;; + \?) print_usage ;; + *) echo "**ERROR: Should never get to this point!!!!!" + print_usage ;; + esac + done + return `expr $OPTIND - 1` +} + + + + +# +# get_security(): gets the security information +# +get_security() +{ + while [ /bin/true ] + do + get_ans "Security level (2=DES, 3=RSA):" $SEC + VALUE=`expr "$ANS" : "\([23]\)"` + if [ -z "$VALUE" -o "$VALUE" -lt 2 -o "$VALUE" -gt 3 ] + then + echo "**ERROR: invalid security level." + echo " It must be either 2 or 3." + else + SEC=$VALUE + break + fi + done + SEC=$ANS +} + + + +# +# update_info(): updates the information. +# +update_info() +{ + echo "" + # ...domainname + get_ans "NIS+ domain name:" $DOM + if [ "`echo $ANS | sed -e 's/.*\(.\)$/\1/'`" != "." ] + then + NODOT=$ANS + DOM=${ANS}. + else + DOM=${ANS} + NODOT=`echo $ANS | sed -e "s/\.$//"` + fi + + case $ACTION in + "yp") # ...YP domainname + while [ /bin/true ] + do + get_ans "NIS domain name:" $YPDOM + if [ ! -z "$ANS" ] + then + if [ "`echo $ANS | sed -e 's/.*\(.\)$/\1/'`" != "." ] + then + YPDOM=${ANS} + else + YPDOM=`echo $ANS | sed -e "s/\.$//"` + fi + break + fi + echo "**WARNING: you must specify the NIS domain name." + done + + # ...YP host name + while [ /bin/true ] + do + get_ans "NIS Hostname:" $YPHOST + if [ ! -z "$ANS" ] + then + YPHOST=$ANS + break + fi + echo "**WARNING: you must specify the NIS server hostname." + done + ;; + "file") # ...directory path for the files + DEFAULT=${DIRPATH:-"(current directory)"} + while [ /bin/true ] + do + get_ans "Directory Path:" "$DEFAULT" + if [ "$ANS" != "$DEFAULT" ] + then + if [ -d $ANS ] + then + DIRPATH=$ANS + break + else + echo "**ERROR: directory $ANS does not exist." + echo " Please try again." + fi + else + break + fi + done + ;; + "cred") # ... security level + get_security + + # ... credential password + get_ans "Credential password:" "$PASSWD" + PASSWD=$ANS + ;; + esac +} + + +# +# print_info(): prints the information on the screen. +# +print_info() +{ + # ...domainname + echo "NIS+ domain name : $DOM" + + # ...YP info + case $ACTION in + "yp") echo "NIS (YP) domain : ${YPDOM:-(not available)}" + echo "NIS (YP) server hostname : ${YPHOST:-(not available)}" + ;; + "file") echo "Directory Path : ${DIRPATH:-(current directory)}" + ;; + "cred") case $SEC in + 0) MESS="0=NO_SEC";; + 1) MESS="1=SYS";; + 2) MESS="2=DES";; + 3) MESS="3=RSA";; + *) MESS="INVALID";; + esac + echo "Security Level : $MESS" + echo "Credential Password : $PASSWD" + ;; + esac +} + + +# +# confirm(): asks for user confirmation. If declined, then it will step +# the user through a question answer session. +# +confirm() +{ + while [ /bin/true ] + do + echo "" + print_info + echo "" + + get_yesno $CONFIRM + if [ $ANS = "y" -o $ANS = "Y" ] + then + return + fi + + update_info + done +} + + + +# +# check_perm(): checks if we have write permission to the NIS+ object +# This should be replaced with nisaccess command when it's available +# $1 the table to be checked. +# +check_perm() +{ + eval "echo checking $1 permission... $VERB" + MYPRINC=`nisdefaults -p` + if [ $MYPRINC = "nobody" ] + then + if nistest -a n=c $1; + then + return + else + return 1 + fi + fi + + DUMMY=`nisls -ld $1` + if [ $? -ne 0 ] + then + exit 1 + fi + OWN=`echo $DUMMY | cut -d" " -f3` + if [ "$OWN" = $MYPRINC ] + then + if nistest -a o=c $1; + then + return + else + return 1 + fi + fi + + DUMMY=`nisls -ldg $1` + if [ $? -ne 0 ] + then + exit 1 + fi + OWN=`echo $DUMMY | cut -d" " -f3` + if [ ! -z "$OWN" ] + then + if nisgrpadm -t -s "$OWN" $MYPRINC; + then + if nistest -a g=c $1; + then + return + else + return 1 + fi + fi + fi + + if nistest -a w=c $1; + then + return + else + return 1 + fi +} + + +# +# is_standard(): checks if the argument passed is a member of the standard +# maps. It returns standard if the argument is a member, else it returns +# non-standard. +# $1 the table name to be checked. +# +is_standard() +{ + for EACHT in $MAPS + do + V1=`echo "$1" | tr '[A-Z]' '[a-z]'` + if [ $V1 = $EACHT ] + then + echo "standard" + return + fi + done + echo "non-standard" + return +} + + + +# +# Populate from YP Routines: +# -------------------------- +# +# This section contains the routine to populate the table from YP maps. +# It will copy the maps from the YP server if not already exists in the +# /var/yp/<YPdomain>. +# from_yp() - populates the NIS+ tables from YP map. +# + +# +# yp_trans(): translate a name type to NIS map name for ypxfr. +# $1 type name +# NOTE: netid.byname map is not supported. +# +yp_trans() +{ + if [ $# -eq 0 ] + then + return + fi + + case $1 in + "aliases") echo "mail.aliases";; + "bootparams") echo "bootparams";; + "ethers") echo "ethers.byaddr";; + "group") echo "group.byname";; + "hosts") echo "hosts.byaddr";; + "ipnodes") echo "ipnodes.byaddr";; + "netmasks") echo "netmasks.byaddr";; + "networks") echo "networks.byname";; + "passwd") echo "passwd.byname";; + "shadow") echo "passwd.byname";; + "protocols") echo "protocols.byname";; + "rpc") echo "rpc.bynumber";; + "services") echo "services.byname";; + "netgroup") echo "netgroup";; + "timezone") echo "timezone.byname";; + + auto.*) echo $1;; + auto_*) echo `echo $1 | sed -e 's/\([^_]*\)_\(.*\)/\1.\2/'`;; + *) echo $1;; + esac +} + + + +# +# nisplus_trans(): translate a name type to NIS+ map name for ypxfr. +# $1 name type +# NOTE: netid.byname map is not supported. +# +nisplus_trans() +{ + if [ $# -eq 0 ] + then + return + fi + + case $1 in + "shadow") echo "passwd";; + "aliases") echo "mail_aliases";; + *.*) echo $1 | sed -e 's/\./_/';; + *) echo $1;; + esac +} + +# +# print_interrupt_warning +# +# used by from_yp & from_files to caution user. +# +print_interrupt_warning() +{ + echo "**WARNING: Interrupting this script after choosing to continue" + echo "may leave the tables only partially populated. This script does" + echo "not do any automatic recovery or cleanup." + echo "" +} + +# +# from_yp(): populates the NIS+ tables from YP map. +# +from_yp() +{ + ERRFOUND="" + if [ -z "$FORCE" ] + then + confirm + else + echo "" + print_info + fi + + if [ -z "$YPDOM" -o -z "$YPHOST" ] + then + echo "" + echo "**ERROR: you must specify both the NIS domain name (-y)" + echo " and the NIS server hostname (-h)." + print_usage + fi + + # + # Try to determine IP address. If it was specified on the + # command line, we use that. If that fails, we try going through + # the switch (with the getent command). If that fails, we + # look in the /etc/hosts file. If we still don't get it, + # we ask. If we have to ask, then we add the IP address + # to the hosts file so we won't have to bug them if this + # script is run again (adding the entry is a little incorrect, + # but quite helpful). + # + + # May have to modify at this point to support IPV6 + + if [ -z "$ADDR" ] + then + ADDR=`getent hosts "$YPHOST" | awk '{print $1}'` + fi + + if [ -z "$ADDR" ] + then + ADDR=`grep -s -i "\<$YPHOST\>" $HOSTS_FILE | grep -s -v "^#" | awk '{print $1}'` + fi + + if [ -z "$ADDR" ] + then + get_ans "Type the IP address for NIS (YP) server ${YPHOST}:" "" + ADDR=$ANS + if [ ! -f $HOSTS_FILE.$BACKUP ] + then + $ECHO cp $HOSTS_FILE $HOSTS_FILE.$BACKUP + fi + $ECHO "echo $ADDR $YPHOST >> $HOSTS_FILE" + fi + + if [ $# -eq 0 ] + then + TABLES=$MAPS + STANDARD=standard + else + TABLES=$* + STANDARD=following + fi + + # ...remove the "." from the domainname + YPDOM=`echo $YPDOM | sed -e "s/\.$//"` + + + echo "" + echo "This script will populate the $STANDARD NIS+ tables for domain " + echo "$DOM from the NIS (YP) maps in domain ${YPDOM}:" + echo $TABLES + echo "" + + print_interrupt_warning + + if [ -z "$FORCE" ] + then + get_yesno $CONTINUE + echo "" + fi + + if [ "$SEC" != "0" ] + then + check_nsswitch + fi + + if [ ! -d /var/yp/$YPDOM ] + then + eval "echo creating /var/yp/$YPDOM... $VERB" + $ECHO mkdir -p /var/yp/$YPDOM + fi + + # ... populating standard files + for EACH in $TABLES + do + # ... check if table exits + NISTAB=`nisplus_trans $EACH` + if nistest -t T $NISTAB.org_dir.$DOM; + then + eval "echo $NISTAB.org_dir.$DOM OK... $VERB" + else + echo "**ERROR($?): table $NISTAB.org_dir.$DOM does not exist." + echo " $NISTAB table will not be loaded." + ERRFOUND="$ERRFOUND $NISTAB" + if [ "$ECHO" = "eval" ] + then + continue + fi + fi + + # ...transfer the YP map from YP server + YPNAME=`yp_trans $EACH` + YPXFR="ypxfr" + TMPYPF=$TMPDIR/ypxfr.$$ + if [ ! -z "$NEW" ] + then + if [ -f /var/yp/$YPDOM/${YPNAME}.dir ] + then + eval "echo removing existing local NIS \(YP\) map... $VERB" + $ECHO /bin/rm -f /var/yp/$YPDOM/${YPNAME}.* + fi + eval "echo copying NIS \(YP\) map from server... $VERB" + $ECHO "$YPXFR -c -d $YPDOM -h $ADDR $YPNAME > $TMPYPF" + if [ $? -ne 0 ] + then + cat $TMPYPF + /bin/rm -f $TMPYPF > /dev/null + echo "**ERROR: NIS map transfer failed." + echo " $NISTAB table will not be loaded." + echo "" + ERRFOUND="$ERRFOUND $NISTAB" + continue + fi + /bin/rm -f $TMPYPF > /dev/null + else + if [ -f /var/yp/$YPDOM/${YPNAME}.dir ] + then + eval "echo using the existing NIS \(YP\) map... $VERB" + else + eval "echo copying NIS \(YP\) map from server... $VERB" + $ECHO "$YPXFR -c -d $YPDOM -h $ADDR $YPNAME > $TMPYPF" + if [ $? -ne 0 ] + then + cat $TMPYPF + /bin/rm -f $TMPYPF > /dev/null + echo "**ERROR: NIS map transfer failed." + echo " $NISTAB table will not be loaded." + echo "" + ERRFOUND="$ERRFOUND $NISTAB" + continue + fi + /bin/rm -f $TMPYPF > /dev/null + fi + fi + + # ...special conversion for netgroup, timezone and auto.master + # maps + OK="yes" + if [ $EACH = "netgroup" -a `tolower $YPDOM` != `tolower $NODOT` ] + then + eval "echo converting $EACH map... $VERB" + TMPFILE=$TMPDIR/netgroup.$$ + $ECHO "makedbm -u /var/yp/$YPDOM/netgroup \ + | sed -e s/$YPDOM/$NODOT/g > $TMPFILE" + if [ $? -eq 0 ] + then + eval "echo makedbm -u OK... $VERB" + $ECHO makedbm $TMPFILE \ + /var/yp/$YPDOM/netgroup + else + OK="" + fi + if [ $? -ne 0 -o -z "$OK" ] + then + echo "**WARNING: NIS netgroup map conversion failed." + echo " netgroup table will not be loaded." + ERRFOUND="$ERRFOUND $NISTAB" + $ECHO "/bin/rm -f $TMPFILE > /dev/null" + continue + fi + $ECHO "/bin/rm -f $TMPFILE > /dev/null" + elif [ $EACH = "timezone" ] + then + eval "echo converting $EACH map... $VERB" + TMPFILE=$TMPDIR/timezone.$$ + $ECHO "makedbm -u /var/yp/$YPDOM/timezone.byname \ + > $TMPFILE" + if [ $? -eq 0 ] + then + eval "echo makedbm -u OK... $VERB" + $ECHO "grep -i '[^\.]$NODOT' $TMPFILE > /dev/null" + if [ $? -eq 1 ] + then + LOCAL_TZ=`grep -i "^$YPDOM" $TMPFILE | \ + cut -d' ' -f2` + if [ -z "$LOCAL_TZ" ] + then + echo "**WARNING: couldn't convert timezone!" + echo "Please manually add timezone for $NODOT.\n" + else + LOCAL_TZ="$LOCAL_TZ $NODOT" + fi + fi + else + echo "**WARNING: NIS timezone map conversion failed." + echo " timezone table will not be loaded." + ERRFOUND="$ERRFOUND $NISTAB" + $ECHO "/bin/rm -f $TMPFILE > /dev/null" + continue + fi + $ECHO "/bin/rm -f $TMPFILE > /dev/null" + elif [ $NISTAB = "auto_master" ] + then + eval "echo converting $EACH map... $VERB" + TMPFILE=$TMPDIR/auto.master.$$ + $ECHO "makedbm -u /var/yp/$YPDOM/auto.master \ + | sed "s/auto./auto_/g" > $TMPFILE" + if [ $? -eq 0 ] + then + eval "echo makedbm -u OK... $VERB" + $ECHO makedbm $TMPFILE \ + /var/yp/$YPDOM/auto.master + else + OK="" + fi + if [ $? -ne 0 -o -z "$OK" ] + then + echo "**WARNING: NIS auto.master map conversion failed." + echo " auto.master table will not be loaded." + ERRFOUND="$ERRFOUND $NISTAB" + $ECHO "/bin/rm -f $TMPFILE > /dev/null" + continue + fi + $ECHO "/bin/rm -f $TMPFILE > /dev/null" + fi + + STAND=`is_standard $EACH` + echo "populating $NISTAB table from $YPDOM NIS (YP) domain..." + if [ `expr "$YPNAME" : 'auto\.'` -eq 5 -o \ + $STAND = "non-standard" ] + then + eval "echo adding $STAND key-value table $NISTAB... $VERB" + $ECHO nisaddent $VERB_OPT $UPDATE -y $YPDOM \ + -Y $YPNAME -t $NISTAB.org_dir key-value $DOM + else + eval "echo adding standard table $NISTAB... $VERB" + if [ "$EACH" = "aliases" ] + then + $ECHO nisaddent $VERB_OPT $UPDATE -y $YPDOM $EACH $DOM + else + $ECHO nisaddent $VERB_OPT $UPDATE -y $YPDOM $NISTAB $DOM + fi + fi + if [ $? -eq 1 ] + then + echo "**WARNING: failed to populate $NISTAB table." + ERRFOUND="$ERRFOUND $NISTAB" + else + if [ $NISTAB = "timezone" -a ! -z "$LOCAL_TZ" ] + then + echo "$LOCAL_TZ" | \ + nisaddent $VERB_OPT -a $NISTAB $DOM + fi + if [ $? -eq 1 ] + then + echo "**WARNING: failed to populate $NISTAB table." + ERRFOUND="$ERRFOUND $NISTAB" + else + echo "$NISTAB table done." + if [ $EACH = "hosts" -o $EACH = "ipnodes" -o $EACH = "passwd" ] + then + if [ "$SEC" != "0" ] + then + add_cred_auto $EACH + fi + fi + fi + fi + echo "" + done + + do_password_print + + if [ -z "$ERRFOUND" ] + then + echo "" + echo "Done!" + else + echo "" + echo "nispopulate failed to populate the following tables:" + echo "$ERRFOUND" + exit 1 + fi +} + + + +# +# Populate from files Routines: +# ----------------------------- +# +# This section contains the routine to populate the table from files. +# from_files() - populates the NIS+ tables from files. +# $* table types to be added, defaults to all standard tables +# + +# +# from_files(): populates the NIS+ tables from files. +# +from_files() +{ + ERRFOUND="" + if [ -z "$FORCE" ] + then + confirm + else + echo "" + print_info + fi + + # shadow file is only supported in 5.x + if [ $OS -eq 5 ] + then + MAPS="$MAPS shadow" + fi + + if [ $# -eq 0 ] + then + TABLES=$MAPS + STANDARD="standard" + else + TABLES=$* + STANDARD="following" + fi + echo "" + echo "This script will populate the $STANDARD NIS+ tables for domain " + echo "$DOM from the files in ${DIRPATH:-current directory}:" + echo $TABLES + echo "" + + print_interrupt_warning + + if [ -z "$FORCE" ] + then + get_yesno $CONTINUE + echo "" + fi + + if [ "$SEC" != "0" ] + then + check_nsswitch + fi + + # ... populating standard files + DIRPATH=${DIRPATH:-'.'} + for EACH in $TABLES + do + # ... check if table exits + NISTAB=`nisplus_trans $EACH` + if nistest -t T $NISTAB.org_dir.$DOM; + then + eval "echo $NISTAB.org_dir.$DOM OK... $VERB" + else + echo "**ERROR($?): table $NISTAB.org_dir.$DOM does not exist." + echo " $NISTAB table will not be loaded." + ERRFOUND="$ERRFOUND $NISTAB" + if [ "$ECHO" = "eval" ] + then + continue + fi + fi + + if [ -f ${DIRPATH}/$EACH ] + then + STAND=`is_standard $EACH` + echo populating $NISTAB table from file ${DIRPATH}/$EACH... + if [ `expr "$EACH" : 'auto[._]'` -eq 5 -o \ + $STAND = "non-standard" ] + then + eval "echo adding $STAND key-value table $NISTAB... $VERB" + $ECHO nisaddent $VERB_OPT $UPDATE -f \ + ${DIRPATH}/$EACH -t \ + $NISTAB.org_dir key-value $DOM + else + eval "echo adding standard table $NISTAB... $VERB" + if [ $EACH = "aliases" -o $EACH = "shadow" ] + then + $ECHO nisaddent $VERB_OPT $UPDATE -f ${DIRPATH}/$EACH $EACH $DOM + else + $ECHO nisaddent $VERB_OPT $UPDATE -f ${DIRPATH}/$EACH $NISTAB $DOM + fi + fi + if [ $? -eq 1 ] + then + echo "**WARNING: failed to populate $NISTAB table." + ERRFOUND="$ERRFOUND $NISTAB" + else + echo "$NISTAB table done." + if [ $EACH = "hosts" -o $EACH = "ipnodes" -o $EACH = "passwd" ] + then + if [ "$SEC" != "0" ] + then + add_cred_auto $EACH + fi + fi + fi + else + echo "**WARNING: file ${DIRPATH}/$EACH does not exist!" + echo " $NISTAB table will not be loaded." + ERRFOUND="$ERRFOUND $NISTAB" + fi + echo "" + done + + do_password_print + + if [ -z "$ERRFOUND" ] + then + echo "" + echo "Done!" + else + echo "" + echo "nispopulate failed to populate the following tables:" + echo "$ERRFOUND" + exit 1 + fi +} + + + + +# +# Populate the credential table Routines: +# -------------------------------------- +# +# This section contains the routine to populate the credential table from +# either passwd or hosts tabls. Default is both passwd and hosts. +# +# add_cred() - routine to populate credential +# add_cred_auto() - routine to populate credential automatically +# after populating the passwd or hosts table. +# +# print_passwd__add_cred +# print_host__add_cred +# These routines are used by do_cred to create a shell +# commands file that contains a routine (add_cred) which +# is called by each entry in the respective table. +# do_cred subsequently runs this shell script. +# +# do_cred() - populates the NIS+ credential table. +# $* tables to populate from, defaults to both passwd and hosts +# tables. +# do_print_password() - this routine is used to print the +# password used for the credential entries at the END of +# populating all tables (per request of tech pubs). +# + +# +# Check that "publickey: nisplus" appears in nsswitch.conf. We +# Do this by stripping comments and the running an awk script. +# The awk script searches for the publickey line and then looks +# for "nisplus" within the line. It prints: +# +# 0 - no publickey entry in nswitch.conf +# 1 - no "nisplus" in publickey entry +# 2 - publickey entry is okay (has "nisplus") +# + +check_nsswitch() { + + cat > /tmp/t.$$ <<'EOF' + /^[ ]*publickey[ ]*:/ { + for (i=2; i<=NF; i++) + if ($(i) == "nisplus") { + found = 2; + exit; + } + found = 1; + exit; + } + END { + if (found) + print found + else + print 0 + } +EOF + + if [ ! -f $NSSWITCH ] + then + echo "**ERROR: the $NSSWITCH file does not exist." + exit 1 + fi + + t=`cat $NSSWITCH | sed 's/#.*//' | awk -f /tmp/t.$$` + + /bin/rm -f /tmp/t.$$ + + if [ "$t" -eq 0 ] + then + echo "**ERROR: there is no publickey entry in $NSSWITCH." + echo "It should be:" + echo " publickey: nisplus" + exit 1 + elif [ "$t" -eq 1 ] + then + echo "**ERROR: the publickey entry in $NSSWITCH is:" + grep '^[ ]*publickey[ ]*:' $NSSWITCH > /tmp/t.$$ + echo " `cat /tmp/t.$$`" + echo "It should be:" + echo " publickey: nisplus" + /bin/rm -f /tmp/t.$$ + exit 1 + fi +} + +# +# add_cred(): populate the NIS+ credential from hosts, ipnodes or passwd +# tables. This is for -C option: +# $* tables to populate from. +# +add_cred() +{ + if [ -z "$FORCE" ] + then + confirm + else + echo "" + print_info + fi + if [ "$SEC" = "0" ] + then + echo "***WARNING: no credential will be created for level 0 security." + exit + fi + + if [ $# -eq 0 ] + then + TABS="passwd hosts ipnodes" + else + TABS=$* + fi + + echo "" + echo "This script will populate the NIS+ credential tables for domain " + echo "$DOM from the following table(s): $TABS" + echo "" + + if [ -z "$FORCE" ] + then + get_yesno $CONTINUE + echo "" + fi + do_cred $TABS +} + +# +# add_cred_auto(): populate the NIS+ credential tables automatically +# after populating the passwd or hosts tables. +# $* table to populate from +# +add_cred_auto() +{ + echo "" + echo "Populating the NIS+ credential table for domain $DOM" + echo "from $1 table." + echo "" + do_cred $1 +} + +# +################################## +# SHELL FILE CREATION ROUTINES +################################## +# +# These routines create the actual cmd files that +# are used in do_cred to load the passwd, hosts and ipnodes tables. +# +################################## +print_passwd__add_cred() +{ +### 3 strings are dependent upon user options. +## as listed under "1." "2. & 3." +#### +# 1. the form of the nisaddcred command: +#### +case $SEC in +2) NISADDCRED="nisaddcred -l $PASSWD -p unix.\$2@$NODOT -P \$1.$DOM des $DOM $VERB" + ;; +3) NISADDCRED="nisaddcred -l $PASSWD -p \$1.$DOM -P \$1.$DOM rsa $DOM $VERB" + ;; +esac +# 2. & 3. the lines output if verbose has been selected: +if [ -z "$VERB" ]; then + VERB1='echo " ...$1 already exists"' + VERB2='echo " ...added $1"' +else + VERB1="" + VERB2="" +fi + +cat << EOF > $TMPFILE + +################### +# WHAT THE PASSWORD FILE LOOKS LIKE +################### +#! /bin/sh + +# \$1 user name +# \$2 user id + +ERR=2 +add_cred() +{ + DUMMY=\`nismatch \$1.'$DOM' cred.org_dir.'$DOM' > /dev/null\` + if [ \$? -eq 0 ]; then + $VERB1 + return + fi + nisaddcred -p \$2 -P \$1.$DOM local $DOM $VERB + if [ \$? -eq 0 ]; then + $NISADDCRED + if [ \$? -eq 0 ]; then + $VERB2 + ERR=0 + else + ERR=1 + fi + else + ERR=1 + fi +} + +EOF +################## +# END OF PASSWORD FILE +################## +} + + +print_host__add_cred() +{ +### 3 strings are dependent upon user options. +## as listed under "1." "2. & 3." +#### +# 1. the form of the nisaddcred command: +#### +case $SEC in +2) NISADDCRED="nisaddcred -l $PASSWD -p unix.\$1@$NODOT -P \$1.$DOM des $DOM $VERB" + ;; +3) NISADDCRED="nisaddcred -l $PASSWD -p \$1.$DOM -P \$1.$DOM rsa $DOM $VERB" + ;; +esac +# 2. & 3. the lines output if verbose has been selected: +if [ -z "$VERB" ]; then + VERB1='echo " ...$1 already exists"' + VERB2='echo " ...added $1"' +else + VERB1="" + VERB2="" +fi + +cat << EOF > $TMPFILE + +################### +# WHAT THE HOSTS FILE LOOKS LIKE +################### +#! /bin/sh + +# \$1 host name + +ERR=2 +add_cred() +{ + DUMMY=\`nismatch \$1.$DOM cred.org_dir.$DOM > /dev/null\` + if [ \$? -eq 0 ]; then + $VERB1 + return + fi + $NISADDCRED + if [ \$? -eq 0 ]; then + ERR=0 + $VERB2 + else + ERR=1 + fi +} + +EOF +################## +# END OF HOSTS FILE +################## +} + + +# +# +# do_cred(): populates the NIS+ credential table. +# +do_cred() +{ + CRED_ERRFOUND=99 + + if [ $# -eq 0 ] + then + TABLES="passwd hosts ipnodes" + else + TABLES=$* + fi + + # ... populating the credential table + for EACH in $TABLES + do + # ... check if table exits + case $EACH in + "passwd") + TMPFILE=$TMPDIR/passwd_$$ + print_passwd__add_cred + + echo dumping passwd table... + $ECHO "niscat -M passwd.org_dir.$DOM | \ + awk -F: '{ printf (\"add_cred %s %s\n\", \$1, \$3) }' \ + >> $TMPFILE" + if [ $? -ne 0 ] + then + DUMP_ERR=1 + else + echo 'exit' >> $TMPFILE + DUMP_ERR=0 + fi + ;; + "hosts") + TMPFILE=$TMPDIR/hosts_$$ + print_host__add_cred + + echo dumping hosts table... + $ECHO "niscat -M hosts.org_dir.$DOM | \ + awk '{ printf (\"add_cred %s\n\", \$1) }' | \ + sort | uniq >> $TMPFILE" + if [ $? -ne 0 ] + then + DUMP_ERR=1 + else + echo 'exit' >> $TMPFILE + DUMP_ERR=0 + fi + ;; + "ipnodes") + TMPFILE=$TMPDIR/ipnodes_$$ + print_host__add_cred + + echo dumping ipnodes table... + $ECHO "niscat -M ipnodes.org_dir.$DOM | \ + awk '{ printf (\"add_cred %s\n\", \$1) }' | \ + sort | uniq >> $TMPFILE" + if [ $? -ne 0 ] + then + DUMP_ERR=1 + else + echo 'exit' >> $TMPFILE + DUMP_ERR=0 + fi + ;; + *) echo "Don't know how to do >>$EACH<<" + exit;; + esac + + $ECHO chmod +x $TMPFILE + if [ $DUMP_ERR -eq 0 ] + then + echo "loading credential table..." + $ECHO $TMPFILE + CRED_ERRFOUND=$? + if [ $CRED_ERRFOUND -eq 0 ] + then + if [ -z "$CREDTABLESADDED" ] + then + CREDTABLESADDED=$EACH + else + CREDTABLESADDED="$CREDTABLESADDED and $EACH" + fi + fi + else + echo "**ERROR: failed dumping $EACH table." + CRED_ERRFOUND=1 + fi + echo + $ECHO "/bin/rm $TMPFILE > /dev/null" + done + + if [ $CRED_ERRFOUND -eq 0 ] + then + echo "" + echo "The credential table for domain $DOM has been populated." + echo + echo "The password used will be $PASSWD." + echo + else + echo "" + echo "nispopulate failed to populate the credential table." + return 1 + fi +} + +# +# Routine to print password for hosts, ipnodes and password table, +# at END of populating all of the tables. +# +do_password_print() +{ + if [ ! -z "$CREDTABLESADDED" ] + then + echo + echo + echo "Credentials have been added for the entries in the" + echo "$CREDTABLESADDED table(s). Each entry was given a default" + echo "network password (also known as a Secure-RPC password)." + echo "This password is:" + echo + echo " $PASSWD" + echo + echo "Use this password when the nisclient script requests the" + echo "network password." + echo + fi +} + +# +# +# * * * MAIN * * * +# + +init + +parse_arg $* +shift $? + +case $ACTION in +"yp") + from_yp $*;; +"file") + from_files $*;; +"cred") + add_cred $* + do_password_print;; +*) + echo + echo "**ERROR: you must specify one of these options: -C, -F, or -Y" + print_usage +esac diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisserver.sh b/usr/src/cmd/rpcsvc/nis/bin/nisserver.sh new file mode 100644 index 0000000000..9c1cdf6225 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/bin/nisserver.sh @@ -0,0 +1,1728 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# nisserver -- script to setup NIS+ servers + +nisplus_fmri=network/rpc/nisplus:default + +# +# print_usage(): ask user if they want to see detailed usage msg. +# +print_usage() +{ + echo + get_yesno " Do you want to see more information on this command? \\n\ + (type 'y' to get a detailed description, 'n' to exit)" + if [ $ANS = "n" -o $ANS = "N" ] + then + echo + return 1 + else + print_more + fi + exit 1 +} + + + +# +# print_more(): print the usage message. +# +print_more() +{ + more << EOF +USAGE: + o to set up root master server: + $PROG -r [-f] [-v] [-x] [-Y] [-d <NIS+_domain>] + [-g <NIS+_groupname>] [-l <NIS+_passwd>] + + o to set up non-root master server: + $PROG -M [-f] [-v] [-x] [-Y] [-d <NIS+_domain>] + [-g <NIS+_groupname>] [-h <NIS+_server_host>] + + o to set up replica server: + $PROG -R [-f] [-v] [-x] [-Y] [-d <NIS+_domain>] + [-h <NIS+_server_host>] + +OPTIONS: + -d <NIS+_domain> + specifies the name for the NIS+ domain. The default is your + local domain. + + -f forces the NIS+ server setup without prompting for confirmation. + + -g <NIS+_groupname> + specifies the NIS+ group name for the new domain. This option is + not valid with "-R" option. The default group is admin.<domain>. + + -h <NIS+_server_host> + specifies the hostname for the NIS+ server. It must be a + valid host in the local domain. Use a fully qualified + hostname (for example, hostx.xyz.sun.com.) to specify a host + outside of your local domain. The default for non-root master + server setup is to use the same list of servers as the parent + domain. The default for replica server setup is the local + hostname. This option is *ONLY* used for non-root master or + replica servers setup. + + -l <network_password> + specifies the network password with which to create the + credentials for the root master server. This option is *ONLY* + used for master root server setup (-r). If this option is not + specified, this script will prompt you for the login password. + + -M sets up the specified host as a non-root master server. + Make sure that rpc.nisd(1M) is running on the new master server + before this command is executed. + + -R sets up the specified host as a replica server. Make sure that + rpc.nisd(1M) is running on the new replica server. + + -r sets up the server as a root master server. Use the -R option + to set up a root replica server. + + -v runs this script in verbose mode. + + -x turns the "echo" mode on. This script just prints the commands + that it would have executed. The commands are printed with + leading "+++". Note that the commands are not actually executed. + The default is off. + + -Y sets up an NIS+ server with NIS-compatibility mode. The default + is to set up the server without NIS-compatibility mode. +EOF +} + + + +# +# print MR usage +# +print_MRr_usage() +{ + if [ "$ERRMRr_OPTION" = "$MRr_OPTION" ] + then + echo "**WARNING: You have specified the '$MRr_OPTION' option twice." + return 0 + fi + echo + echo "**ERROR: You have specified the '$ERRMRr_OPTION' option after" + echo " having selected the '$MRr_OPTION' option." + echo "Please select only one of these options: '-M', '-R', or '-r'." + print_usage + exit 1 +} + + + +# +# Generic Routines: +# ----------------- +# +# This section contains general routines. +# get_ans() - prompts the message and waits for an input +# get_yesno() - prompts the message and waits for a y or n answer +# restart_process() +# - kills and starts a specified process +# kill_process() - kills a specified process +# check_dot() - check if the argument ends with a dot +# check_host() - check if the hostname specified is a valid one +# tolower(): converts upper to lower case. +# + +# +# get_ans(): gets an asnwer from the user. +# $1 instruction/comment/description/question +# $2 default value +# +get_ans() +{ + if [ -z "$2" ] + then + echo "$1 \c" + else + echo "$1 [$2] \c" + fi + read ANS + if [ -z "$ANS" ] + then + ANS=$2 + fi +} + + + +########## get_yesno constants: +## +## There are two very common phrases passed to get_yesno: +## These have been parameterized to provide "ease of use". +## Thus, there are three message "types" which are possible: +## --$CONTINUE: "Do you want to continue? (type 'y' to continue, 'n' to exit this script)" +## --$CONFIRM: "Is this information correct? (type 'y' to accept, 'n' to change)" +## --actual string is passed. +## +## If the message is $CONTINUE, get_yesno will exit if the response is no. +## +########### +CONTINUE=2 +CONFIRM=1 +# +# get_yesno(): get the yes or no answer. +# $1 message type or message. +# +# +# +get_yesno() +{ + ANS="X" + + case $1 in + $CONTINUE ) + INFOTEXT="Do you want to continue? (type 'y' to continue, 'n' to exit this script)" + ;; + $CONFIRM ) + INFOTEXT="Is this information correct? (type 'y' to accept, 'n' to change)" + ;; + *) INFOTEXT="$1" + ;; + esac + + while [ "$ANS" != "y" -a "$ANS" != "n" -a "$ANS" != "Y" -a "$ANS" != "N" ] + do + get_ans "$INFOTEXT" "" + done + + if [ "$1" = "$CONTINUE" ]; then + if [ $ANS = "n" -o $ANS = "N" ] + then + exit + fi + fi + +} + + + +# +# check_dot(): checks if the argument specified ends with a dot. +# $1 argument to be checked +# +check_dot() +{ + if [ "`echo $1 | sed -e 's/.*\(.\)$/\1/'`" != "." ] + then + return 1 + fi + return 0 +} + + + +# +# check_host(): checks if the host specified is in the credential table of +# the its domain. If it's a valid host, then it'll assign the host +# principal name to HOSTPRINC. +# $1 host name (this can be a fully qualified name) +# +check_host() +{ + if [ -z "$1" ] + then + return 1 + fi + + if check_dot $1; + then + HOSTPRINC=$1 + MESS="principal" + LDOM=`echo $1 | sed -e 's/[^\.]*\.\(.*\)$/\1/'` + else + LDOM=`nisdefaults -d` + HOSTPRINC=$1.$LDOM + MESS="host" + fi + + nismatch $HOSTPRINC cred.org_dir.$LDOM > /dev/null + if [ $? -eq 0 ] + then + return 1 + fi + echo "**ERROR: the principal name for host $1 is not defined in domain" + echo "\"$LDOM\". You must either add the credential for host $1" + echo "in domain \"$LDOM\" or specify a fully qualified hostname (with" + echo "the ending dot \".\") if the principal name is defined in a" + echo "different domain. Use nisclient -c to create the host credential." + return 0 +} + + + +# +# tolower_single(): converts upper to lower case and single token. +# Single token means the first token if the argument contains "." +# dots as in the fully qualified hostname. +# $1 string to convert +# +tolower_single() +{ + echo "$1" | tr '[A-Z]' '[a-z]' | cut -d. -f1 +} + + + +# +# tolower(): converts upper to lower case. +# $1 string to convert +# +tolower() +{ + echo "$1" | tr '[A-Z]' '[a-z]' +} + + +# +# smf(5) routines +# restart_instance() - restart instance or enable if not enabled +# + +# +# restart_instance [-t] instance_fmri +# +restart_instance() { + if [ "$1" = "-t" ]; then + flag=-t + shift + else + flag= + fi + + if [ "`/usr/bin/svcprop -p restarter/state $1`" = "disabled" ]; + then + /usr/sbin/svcadm enable $flag $1 + else + /usr/sbin/svcadm restart $1 + fi +} + +# +# Common Routines: +# --------------- +# +# This section contains common routines for master and replica setups for +# root and non-root domains. +# init() - initializes all the variables +# parse_arg() - parses the command line arguments +# get_security() - gets the security information +# update_info() - updates the setup information +# print_info() - prints the setup information +# confirm() - confirms the setup information +# setup_domain() - sets up the domain +# setup_switch() - sets up the switch +# nis_server() - get the server's name for a domain +# is_server() - checks if the specified host is already a server +# for current domain $DOM. +# check_perm() - checks for the write permission for an object +# nis_chown() - changes the owner for a domain + + +# +# init(): initializes variables and options +# +init() +{ + + PROG=`basename $0` + VERB='> /dev/null' # NULL or "> /dev/null" + ECHO="eval" # eval or echo + BACKUP=no_nisplus # backup suffix + DOM=`nisdefaults -d` # domainname with DOT + NODOT=`echo $DOM | sed -e "s/\.$//"` + # domainname without DOT + SEC=2 # 2=DES or 3=RSA + ACTION="" # master or replica + ROOT="nonroot" # nonroot or root + FORCE="" # NULL or TRUE + GROUP="" # NULL or <group-name> + HOST="" # NULL or <hostname> + YP="" # NULL or -Y + DEFSEC=2 # default security +## The following variable allows for variation in a specific user message: + WITHOUT="without" # without YP compatibility + + OS=`uname -r | cut -d. -f1` + OSVER=`uname -r | cut -d. -f2` + LOCALHOST=`uname -n` + PATH=/usr/lib/nis:/usr/sbin:/usr/bin:/usr/lib/netsvc/yp:$PATH; export PATH + LOCALHOST=`tolower_single $LOCALHOST` +} + + + +# +# parse_arg(): parses the input arguments. +# It returns the number to be shift in the argument list. +# +parse_arg() +{ + while getopts "d:fg:h:l:MRrvxY" ARG + do + case $ARG in + d) if [ "`echo $OPTARG | sed -e 's/.*\(.\)$/\1/'`" != "." ] + then + NODOT=$OPTARG + DOM=${NODOT}. + else + DOM=$OPTARG + NODOT=`echo $DOM | sed -e "s/\.$//"` + fi ;; + f) FORCE="TRUE";; + g) if [ "`echo $OPTARG | sed -e 's/.*\(.\)$/\1/'`" != "." ] + then + echo "**ERROR: you must specify a fully qualified groupname." + exit 1 + fi + GROUP=$OPTARG;; + h) HOST=$OPTARG;; + l) PASSWD="-l $OPTARG";; + + M) if [ -z "$ACTION" ] + then + ACTION="master" + MRr_OPTION="-M" + else + ERRMRr_OPTION="-M" + print_MRr_usage + fi;; + R) if [ -z "$ACTION" ] + then + ACTION="replica" + MRr_OPTION="-R" + else + ERRMRr_OPTION="-R" + print_MRr_usage + fi;; + r) ROOT="root" + if [ -z "$ACTION" ] + then + ACTION="master" + MRr_OPTION="-r" + else + ERRMRr_OPTION="-r" + print_MRr_usage + fi;; + v) VERB="";; + x) ECHO="echo +++";; + Y) YP="-Y" + WITHOUT="with";; + \?) print_usage + exit 1;; + *) echo "**ERROR: Should never get to this point!!!!!" + print_usage + exit 1;; + esac + done + return `expr $OPTIND - 1` +} + + + +# +# check_rootgrp(): check if the group name specified is an valid group for +# root master server setup. +# $1 the groupname to be checked +# +check_rootgrp() +{ + if [ ! -z "$1" ] + then + if [ $ROOT = "root" ] + then + GDOM=`expr "$1" : '[^\.]*\.\(.*\)'` + if [ "`tolower $GDOM`" != "`tolower $DOM`" ] + then + return 1 + fi + fi + fi +} + + + +# +# check_domainname(): check validity of a domain name. Currently we check +# that it has at least two components. +# $1 the domain name to be checked +# +check_domainname() +{ + if [ ! -z "$1" ] + then + t=`expr "$1" : '[^.]\{1,\}[.][^.]\{1,\}'` + if [ "$t" = 0 ] + then + echo '**ERROR: invalid domain name ('$1')' + echo ' It must have at least two components.' + echo ' For example, "company.com.".' + print_usage + exit 1 + fi + fi +} + + + +# +# get_security(): gets the security information +# +get_security() +{ + while [ /bin/true ] + do + get_ans "Security level (2=DES, 3=RSA):" $SEC + VALUE=`expr "$ANS" : "\([23]\)"` + if [ -z "$VALUE" -o "$VALUE" -lt 2 -o "$VALUE" -gt 3 ] + then + echo "**ERROR: invalid security level." + echo " It must be either 2 or 3." + else + SEC=$VALUE + break + fi + done + SEC=$ANS +} + + + +# +# update_info(): updates the information. +# +update_info() +{ + echo "" + # ...domainname + get_ans "Domain name:" $DOM + if [ "`echo $ANS | sed -e 's/.*\(.\)$/\1/'`" != "." ] + then + NODOT=$ANS + DOM=${NODOT}. + else + DOM=$ANS + NODOT=`echo $DOM | sed -e "s/\.$//"` + fi + + # ...host name + if [ $ACTION = "replica" ] + then + while [ /bin/true ] + do + get_ans "NIS+ Hostname:" $HOST + if [ "$HOST" = "$ANS" ] + then + break + fi + if check_host $ANS; + then + HOST=$ANS + break + fi + done + elif [ $ROOT = "nonroot" ] + then + DEFAULT=${HOST:-"(use ${PAR_DOM} servers)"} + while [ /bin/true ] + do + get_ans "NIS+ Hostname:" "$DEFAULT" + if [ "$ANS" = "$DEFAULT" ] + then + break + fi + if check_host $ANS; + then + HOST=$ANS + break + fi + done + fi + + # ...group name + if [ $ACTION = "master" ] + then + DEFAULT=${GROUP:-admin.$DOM} + while [ /bin/true ] + do + get_ans "NIS+ group:" $DEFAULT + if [ "$ANS" != "$DEFAULT" ] + then + if check_dot $ANS; + then + if check_rootgrp $ANS; + then + GROUP=$ANS + break + fi + echo "**ERROR: invalid group name." + echo " It must be a group in the $DOM domain." + continue + fi + echo "**ERROR: NIS+ group name must end with a \".\"." + else + break + fi + done + fi + + # ...YP compatibility + if [ $ACTION = "master" ] + then + DEFAULT=1 + if [ -z "$YP" ] + then + DEFAULT=0 + fi + ANS=2 + while [ $ANS -ne 0 -a $ANS -ne 1 ] + do + get_ans "NIS (YP) compatibility (0=off, 1=on):" $DEFAULT + done + if [ $ANS -eq 1 ] + then + YP="-Y" + echo "" + else + YP="" + fi + fi +} + +# +# print_info(): prints the information on the screen. +# +print_info() +{ + # ...domainname + echo "Domain name : $DOM" + + # ...hostname + if [ $ACTION = "replica" ] + then + USE_HOST=TRUE + echo "NIS+ server : $HOST" + elif [ $ROOT = "nonroot" ] + then + USE_HOST=TRUE + echo "NIS+ server : "${HOST:-"(use $PAR_DOM servers)"} + fi + + # ...admin group + if [ $ACTION = "master" ] + then + MESS="admin.$DOM" + echo "NIS+ group : ${GROUP:-$MESS}" + fi + + # ...YP compatibility + if [ $ACTION = "master" ] + then + MESS=ON + if [ -z "$YP" ] + then + MESS="OFF" + fi + echo "NIS (YP) compatibility : $MESS" + fi + + # ...security level + if [ $ACTION = "master" ] + then + case $SEC in + 0) MESS="0=NO_SEC";; + 1) MESS="1=SYS";; + 2) MESS="2=DES";; + 3) MESS="3=RSA";; + *) MESS="INVALID";; + esac + echo "Security level : $MESS" + fi +} + + + +# +# confirm(): asks for user confirmation. If declined, then it will step +# the user through a question answer session. +# +confirm() +{ + while [ /bin/true ] + do + echo "" + print_info + echo "" + + get_yesno $CONFIRM + if [ $ANS = "y" -o $ANS = "Y" ] + then + return + fi + + update_info + done +} + + + +# +# setup_domain(): sets up "domainname" and "/etc/defaultdomain" with the +# specified domain information. +# +setup_domain() +{ + echo "setting up domain information \"$DOM\" ..." + if [ `nisdefaults -d` != $DOM ] + then + # NODOT is used to support 4.x YP clients + $ECHO domainname $NODOT + if [ ! -f /etc/defaultdomain.$BACKUP ] + then + $ECHO mv /etc/defaultdomain /etc/defaultdomain.$BACKUP + fi + $ECHO "domainname > /etc/defaultdomain" + fi + echo "" +} + + + +# +# setup_switch(): copies the nisplus switch configuration file to +# nsswitch.conf. +# +setup_switch() +{ + if [ $OS -eq 5 ] + then + echo "setting up switch information ..." + diff /etc/nsswitch.conf /etc/nsswitch.nisplus > /dev/null + if [ $? -eq 0 ] + then + eval "echo switch configuration file already set to use NIS+. $VERB" + restart_instance network/rpc/keyserv:default + echo "" + return + fi + + if [ ! -f /etc/nsswitch.conf.$BACKUP ] + then + $ECHO mv /etc/nsswitch.conf /etc/nsswitch.conf.$BACKUP + fi + $ECHO cp /etc/nsswitch.nisplus /etc/nsswitch.conf + echo "" + fi + + $ECHO "rm -f /etc/.rootkey > /dev/null" + restart_instance network/rpc/keyserv:default +} + + + +# +# nis_server(): returns the master server for specified domain. +# $1 domain +# +nis_server() +{ + niscat -M -o $1 > /tmp/$PROG.$$ + if [ $? -ne 0 ] + then + rm -f /tmp/$PROG.$$ > /dev/null + exit 1 + fi + ALLSERVERS="" + exec < /tmp/$PROG.$$ + while read LINE + do + EA=`echo $LINE | sed -n -e "s/^Name : \([^\'].*\)/\1/p"` + if [ ! -z "$EA" ] + then + ALLSERVERS="${ALLSERVERS} $EA" + fi + done + exec < /dev/tty + GROUPSERVER=`sed -n -e "s/Group[ ]*: //p" /tmp/$PROG.$$` + MASTER_SERVER=`echo $ALLSERVERS | cut -d' ' -f1` + rm -f /tmp/$PROG.$$ > /dev/null + eval "echo nis_servers: ALLSERVERS=$ALLSERVERS ... $VERB" +} + + + +# +# is_server(): checks if the specified host is already a server for +# current domain $DOM. +# $1 server principal +# +is_server() +{ + V1=`tolower $1` + for EA in $ALLSERVERS + do + if [ "$V1" = "`tolower $EA`" ] + then + return 0 + fi + done + return 1 +} + + + +# +# check_perm(): checks if we have write permission to the NIS+ object +# This should be replaced with nisaccess command when it's available +# $1 the table to be checked. +# +check_perm() +{ + if [ "$ECHO" = "echo" ] + then + return + fi + + eval "echo checking $1 permission ... $VERB" + MYPRINC=`nisdefaults -p` + if [ $MYPRINC = "nobody" ] + then + if nistest -a n=c $1; + then + return + else + return 1 + fi + fi + + DUMMY=`nisls -ld $1` + if [ $? -ne 0 ] + then + exit 1 + fi + OWN=`echo $DUMMY | cut -d" " -f3` + if [ "$OWN" = $MYPRINC ] + then + if nistest -a o=c $1; + then + return + else + return 1 + fi + fi + + DUMMY=`nisls -ldg $1` + if [ $? -ne 0 ] + then + exit 1 + fi + OWN=`echo $DUMMY | cut -d" " -f3` + if [ ! -z "$OWN" ] + then + if nisgrpadm -t -s "$OWN" $MYPRINC; + then + if nistest -a g=c $1; + then + return + else + return 1 + fi + fi + fi + + if nistest -a w=c $1; + then + return + else + return 1 + fi +} + + + +# +# nis_chown(): changes the owner for the entire domain specified. +# $1 the new owner's principal +# $2 the domain +# +nis_chown() +{ + if [ "$ECHO" = "echo" ] + then + return + fi + + nisls org_dir.$2 > /tmp/${PROG}_nisls.$$ + sed -e "1d" -e "s/^\(.*\)/nischown $1 \1.org_dir.$2/" \ + /tmp/${PROG}_nisls.$$ > /tmp/${PROG}_chown.$$ + chmod +x /tmp/${PROG}_chown.$$ + /tmp/${PROG}_chown.$$ + rm -f /tmp/${PROG}_chown.$$ /tmp/${PROG}_nisls.$$ > /dev/null + $ECHO nischown $1 org_dir.$2 groups_dir.$2 $2 +} + + + +# +# setup_properties(): modifies repository properties such that we run +# with the proper options. +# $* list of servers +# +setup_properties() +{ + if [ ! -z "$YP" -o "$SEC" -ne $DEFSEC ]; then + if [ -z "$YP" ]; then + eval "echo YP emulation disabled ... $VERB" + emulyp_active=`/usr/bin/svcprop -p \ + application/emulate_yp $nisplus_fmri` + [ $? != 0 ] || \ + if [ "$emulyp_active" = "true" ]; then + /usr/sbin/svccfg -s $nisplus_fmri \ + setprop application/emulate_yp = \ + boolean: false + fi + else + eval "echo YP emulation enabled ... $VERB" + echo "" + if /usr/bin/svcprop -q -p application $nisplus_fmri; + then :; else + /usr/sbin/svccfg -s $nisplus_fmri \ + addpg application application + fi + /usr/sbin/svccfg -s $nisplus_fmri \ + setprop application/emulate_yp = boolean: true + fi + + if [ "$SEC" -ne $DEFSEC ]; then + eval "echo setting security to $SEC ... $VERB" + if /usr/bin/svcprop -q -p application $nisplus_fmri; + then :; else + /usr/sbin/svccfg -s $nisplus_fmri \ + addpg application application + fi + /usr/sbin/svccfg -s $nisplus_fmri \ + setprop application/security = count: $SEC + else + eval "echo using default security ... $VERB" + /usr/sbin/svccfg -s $nisplus_fmri \ + delprop application/security + fi + fi + + # Finally, make nis_cachemgr run with -i for the duration of the + # current OS instantiation. + # + # Create application_ovr property group. Suppress + # errors as it may already exist. + # + if /usr/bin/svcprop -q -p application_ovr $nisplus_fmri; then :; else + /usr/sbin/svccfg -s $nisplus_fmri \ + addpg application_ovr application P + fi + /usr/sbin/svccfg -s $nisplus_fmri \ + setprop application_ovr/clear_cache = boolean: true + + /usr/sbin/svcadm refresh network/rpc/nisplus:default +} + + +# +# root master setup Routine: +# ------------------------- +# +# This section contains the routine to setup a ROOT master server. +# NOTE: you can only configure your local machine as a root master. You +# cannot configure other machines across the net. +# root_master() - sets up the local machine as a root master server +# + +# +# root_master(): sets up a the local machine as the root master server. +# +root_master() +{ + WHO=`id | sed -e "s/uid=[0-9]*(\([^ )]*\)).*/\1/"` + if [ $WHO != "root" ] + then + echo "This script must be run as root ..." + exit 1 + fi + + if [ ! -z "$HOST" -a "`tolower_single $HOST`" != "$LOCALHOST" ] + then + echo "**ERROR: you cannot set up $HOST remotely." + echo " To set up $HOST as an NIS+ root master server, run" + echo " nisserver on $HOST." + exit 1 + fi + HOST=$LOCALHOST + + if [ -z "$SEC" ] # NOTE: This conditional currently always false + then + echo "" + echo "You must specify the security level:" + eval "echo get security info ... $VERB" + get_security + echo "" + fi + + echo "This script sets up this machine \"$LOCALHOST\" as an NIS+" + echo "root master server for domain $DOM." + + if [ -z "$FORCE" ] + then + confirm + else + echo "" + print_info + fi + + echo "" + echo "This script will set up your machine as a root master server for" + echo "domain $DOM $WITHOUT NIS compatibility at security level 2." + echo "" + if [ -f /var/nis/NIS_COLD_START ] + then + echo "WARNING: this script removes directories and files" + echo "related to NIS+ under /var/nis directory with the" + echo "exception of the client_info NIS_COLD_START file which" + echo "will be renamed to <file>.${BACKUP}. If you want to save" + echo "these files, you should abort from this script now to" + echo "save these files first." + echo "" + if [ -d /var/nis/data ] + then + echo "WARNING: once this script is executed, you will not be able to" + echo "restore the existing NIS+ server environment. However, you can" + echo "restore your NIS+ client environment using \"nisclient -r\"" + echo "with the proper domain name and server information." + else + echo "Use \"nisclient -r\" to restore your NIS+ client environment." + fi + echo "" + else + echo "Use \"nisclient -r\" to restore your current network service environment." + echo "" + fi + + if [ -z "$FORCE" ] + then + get_yesno $CONTINUE + echo "" + fi + + setup_domain + + setup_switch + + eval "echo killing NIS and NIS+ processes ... $VERB" + /usr/sbin/svcadm disable network/nis/client + /usr/sbin/svcadm disable -t network/rpc/nisplus + eval "echo stopping nscd ... $VERB" + /usr/sbin/svcadm disable -t system/name-service-cache + eval "echo '' $VERB" + + eval "echo setup NIS_GROUP environment variable ... $VERB" + GROUP=${GROUP:-admin.$DOM} + $ECHO "NIS_GROUP=$GROUP; export NIS_GROUP" + eval "echo '' $VERB" + + # Save NIS_COLD_START file + if [ -f /var/nis/NIS_COLD_START ] + then + $ECHO cp /var/nis/NIS_COLD_START /var/nis/NIS_COLD_START.$BACKUP + fi + + eval "echo rm /var/nis files ... $VERB" + $ECHO "rm -f /var/nis/NIS_COLD_START > /dev/null" + $ECHO "rm -f /var/nis/NIS_SHARED_DIRCACHE > /dev/null" + $ECHO "rm -f /var/nis/.NIS_PRIVATE_DIRCACHE > /dev/null" + $ECHO "rm -f /var/nis/client_info > /dev/null" + $ECHO "rm -f /var/nis/.pref_servers > /dev/null" + $ECHO "rm -f /var/nis/trans.log > /dev/null" + $ECHO "rm -f /var/nis/data.dict* > /dev/null" + $ECHO "rm -rf /var/nis/data > /dev/null" + eval "echo '' $VERB" + + echo "running nisinit ..." + $ECHO "nisinit -r" + if [ $? -ne 0 ] + then + echo "**ERROR: it failed to initialize the root server." + exit 1 + fi + echo "" + + echo "starting root server at security level 0 to create credentials..." + $ECHO "rpc.nisd -S 0" + echo "" + sleep 2 + + echo "running nissetup to create standard directories and tables ..." + $ECHO "nissetup $YP" + if [ $? -ne 0 ] + then + echo "**ERROR: it failed to create the tables." + exit 1 + fi + if [ $OS -ne 5 -o $OSVER -lt 3 ] + then + $ECHO "nischmod n+r cred.org_dir.$DOM" + fi + echo "" + + echo "adding credential for $HOST.$DOM.." + case $SEC in + 2) $ECHO "nisaddcred $PASSWD des > /dev/null";; + 3) $ECHO "nisaddcred $PASSWD rsa > /dev/null";; + *) ;; + esac + if [ $? -ne 0 ] + then + echo "**ERROR: it failed to add the credential for root." + exit 1 + fi + sleep 1 + + echo "" + echo "creating NIS+ administration group: ${GROUP} ..." + $ECHO "nisgrpadm -c $GROUP > /dev/null" + if [ $? -ne 0 ] + then + echo "**WARNING: failed to create the $GROUP group." + echo " You will need to create this group manually:" + echo " 1. /usr/bin/nisgrpadm -c $GROUP" + echo " 2. /usr/bin/nisgrpadm -a $GROUP $HOST.$DOM" + else + echo "adding principal ${HOST}.${DOM} to $GROUP ..." + $ECHO "nisgrpadm -a $GROUP ${HOST}.${DOM} > /dev/null" + if [ $? -ne 0 ] + then + echo "**WARNING: failed to add new member $HOST.$DOM into" + echo "the $GROUP group." + echo " You will need to add this member manually:" + echo " 1. /usr/bin/nisgrpadm -a $GROUP $HOST.$DOM" + fi + fi + echo "" + + eval "echo updating the keys for directories ... $VERB" + $ECHO "nisupdkeys $DOM > /dev/null" + if [ $? -ne 0 ] + then + echo "WARNING: nisupdkeys failed on directory $DOM" + echo " You will need to run nisupdkeys manually:" + echo " 1. /usr/lib/nis/nisupdkeys $DOM" + echo "" + fi + $ECHO "nisupdkeys org_dir.$DOM > /dev/null" + if [ $? -ne 0 ] + then + echo "WARNING: nisupdkeys failed on directory org_dir.$DOM" + echo " You will need to run nisupdkeys manually:" + echo " 1. /usr/lib/nis/nisupdkeys org_dir.$DOM" + echo "" + fi + $ECHO "nisupdkeys groups_dir.$DOM > /dev/null" + if [ $? -ne 0 ] + then + echo "WARNING: nisupdkeys failed on directory groups_dir.$DOM" + echo " You will need to run nisupdkeys manually:" + echo " 1. /usr/lib/nis/nisupdkeys groups_dir.$DOM" + echo "" + fi + eval "echo $VERB" + + if [ $OS -ne 5 -o $OSVER -lt 3 ] + then + eval "echo change group owner for $DOM.. $VERB" + $ECHO nischgrp $GROUP $DOM + # ... g=rmcd is just a temporary fix for nisinit bug + eval "echo add read access for nobody ... $VERB" + $ECHO nischmod n+r,g=rmcd $DOM + fi + + pkill -z `/sbin/zonename` -x rpc.nisd + + setup_properties + + echo "restarting NIS+ root master server at security level $SEC ..." + $ECHO /usr/sbin/svcadm enable network/rpc/nisplus + + eval "echo starting Name Service Cache Daemon nscd ... $VERB" + $ECHO /usr/sbin/svcadm enable system/name-service-cache + + echo "" + echo "This system is now configured as a root server for domain $DOM" + echo "You can now populate the standard NIS+ tables by using the" + echo "nispopulate script or /usr/lib/nis/nisaddent command." +} + + + +# +# Non-root master setup Routine: +# ------------------------------ +# +# This section contains the routine to setup a non-ROOT master server. +# NOTE: If the -h <hostname> is specified, then it will configure the +# specified host as the master of the new domain. Otherwise, it will +# use the same servers information as in the parent domain. +# nonroot_master() +# - sets up a non-root master server. +# + +# +# nonroot_master(): sets up a nonroot master server. +# +nonroot_master() +{ + # ... local variables + PROMOTE="make" + + # ...check parent domain + PAR_DOM=`expr "$DOM" : '[^\.]*\.\(.*\)'` + if nistest -t D $PAR_DOM; + then + : + else + echo "**ERROR: $PAR_DOM does not exist." + exit 1 + fi + + if [ -z "$SEC" ] # NOTE: This conditional currently always false + then + echo "" + echo "You must specify the security level:" + eval "echo get security info ... $VERB" + get_security + echo "" + fi + + if check_host "$HOST"; + then + exit 1 + fi + + # ...check permission + check_perm $PAR_DOM + if [ $? -ne 0 ] + then + echo "**ERROR: no permission to create directory $DOM" + exit 1 + fi + + echo "This script sets up a non-root NIS+ master server for domain" + echo "$DOM" + if [ -z "$FORCE" ] + then + confirm + else + echo "" + print_info + fi + + echo "" + if [ -z "$HOST" ] + then + HOSTDEF="" + echo "This script will set up an NIS+ non-root master for domain" + echo "$DOM $WITHOUT NIS compatibility, using the same servers for" + echo "domain $PAR_DOM." + nis_server $PAR_DOM + echo "servers: $ALLSERVERS" + else + echo "This script sets up machine \"$HOST\" as an NIS+" + HOSTDEF="yes" + echo "non-root master server for domain $DOM $WITHOUT NIS compatibility." + MASTER_SERVER=$HOST + ALLSERVERS=$HOST + eval "echo $VERB" + eval "echo checking rpc.nisd process on $HOST ... $VERB" + rpcinfo -u $HOST 100300 3 > /dev/null + if [ $? -ne 0 ] + then + echo "**ERROR: NIS+ server is not running on $HOST." + echo " You must do the following before becoming an NIS+ server:" + echo " 1. become an NIS+ client of the parent domain or any domain" + echo " above the domain which you plan to serve. (nisclient)" + echo " 2. start the NIS+ server. (rpc.nisd)" + exit 1 + fi + fi + echo "" + + if [ -z "$FORCE" ] + then + get_yesno $CONTINUE + echo "" + fi + + # ...check domain + if nistest -t D $DOM; + then + echo "**WARNING: domain $DOM already exists." + if [ ! -z "$HOST" ] + then + nis_server $DOM + if [ "`tolower $HOSTPRINC`" = "`tolower $MASTER_SERVER`" ] + then + echo "$HOSTPRINC is already a master server for thisdomain." + echo "If you choose to continue with this script, it will" + echo "try to create the groups_dir and org_dir directories" + echo "for this domain." + IGNORE="yes" + else + is_server $HOSTPRINC + if [ $? -eq 0 ] + then + echo "$HOSTPRINC is already a replica server for this domain." + fi + echo "If you choose to continue with this script, it will" + echo "promote $HOSTPRINC to be the new master for $DOM" + PROMOTE="promote new master for" + fi + else + echo "If you choose to continue with this script, it will" + echo "try to create the groups_dir and org_dir directories" + echo "for this domain." + IGNORE="yes" + fi + + echo "" + if [ -z "$FORCE" ] + then + get_yesno $CONTINUE + echo "" + fi + fi + + + eval "echo setup NIS_GROUP environment variable ... $VERB" + GROUP=${GROUP:-admin.$DOM} + $ECHO "NIS_GROUP=$GROUP; export NIS_GROUP" + eval "echo '' $VERB" + + eval "echo running nismkdir ... $VERB" + DEF_PERM="-D access=g=rmcd,n=r" + if [ -z "$HOST" ] + then + $ECHO nismkdir $DEF_PERM $DOM + else + $ECHO nismkdir $DEF_PERM -m $HOSTPRINC $DOM + fi + + if [ -z "$IGNORE" -a $? -ne 0 ] + then + echo "**ERROR: it failed to $PROMOTE the $DOM directory." + exit 1 + fi + + $ECHO "nisupdkeys $DOM > /dev/null" + if [ $? -ne 0 ] + then + echo "**WARNING: nisupdkeys failed on directory $DOM" + echo " This script will not be able to continue." + echo " Please remove the $DOM directory using 'nisrmdir'." + exit 1 + fi + + $ECHO "nisping $PAR_DOM > /dev/null" + sleep 4 + + if [ $OS -ne 5 -o $OSVER -lt 3 ] + then + DEF_PERM="-D access=g=rmcd,n=r" + else + DEF_PERM="" + fi + if [ "$PROMOTE" != "make" ] + then + $ECHO nismkdir $DEF_PERM -m $HOSTPRINC org_dir.$DOM + if [ $? -ne 0 ] + then + echo "**ERROR: it failed to $PROMOTE the org_dir.$DOM directory." + exit 1 + fi + $ECHO nismkdir $DEF_PERM -m $HOSTPRINC groups_dir.$DOM + if [ $? -ne 0 ] + then + echo "**ERROR: it failed to $PROMOTE the groups_dir.$DOM directory." + exit 1 + fi + fi + + echo "running nissetup ..." + $ECHO "nissetup $YP $DOM" + if [ -z "$IGNORE" -a $? -ne 0 ] + then + echo "**ERROR: it failed to create the tables." + exit 1 + fi + if [ $OS -ne 5 -o $OSVER -lt 3 ] + then + $ECHO "nischmod n+r cred.org_dir.$DOM" + fi + echo "" + + if [ `echo $GROUP | cut -d. -f2-` = $DOM ] + then + echo "setting NIS+ group to ${GROUP} ..." + $ECHO "nisgrpadm -c $GROUP > /dev/null" + if [ $? -ne 0 ] + then + echo "**WARNING: failed to create the $GROUP group." + echo " You will need to create this group manually:" + echo " 1. /usr/bin/nisgrpadm -c $GROUP" + echo " 2. /usr/bin/nisgrpadm -a $GROUP $ALLSERVERS" + else + $ECHO "nisgrpadm -a $GROUP $ALLSERVERS > /dev/null" + if [ $? -ne 0 ] + then + echo "**WARNING: failed to add the following members into" + echo "the $GROUP group:" + echo $ALLSERVERS + echo "" + echo " You will need to add this member manually:" + echo " 1. /usr/bin/nisgrpadm -a $GROUP $ALLSERVERS" + else + $ECHO "nisctl -f g $DOM > /dev/null" + fi + fi + fi + echo "" + + eval "echo updating the keys for directories ... $VERB" + $ECHO "nisupdkeys org_dir.$DOM > /dev/null" + if [ $? -ne 0 ] + then + echo "WARNING: nisupdkeys failed on directory org_dir.$DOM" + echo " You will need to run nisupdkeys manually:" + echo " 1. /usr/lib/nis/nisupdkeys org_dir.$DOM" + echo "" + fi + $ECHO "nisupdkeys groups_dir.$DOM > /dev/null" + if [ $? -ne 0 ] + then + echo "WARNING: nisupdkeys failed on directory groups_dir.$DOM" + echo " You will need to run nisupdkeys manually:" + echo " 1. /usr/lib/nis/nisupdkeys groups_dir.$DOM" + echo "" + fi + eval "echo $VERB" + + if [ ! -z "$HOST" -a "`tolower $HOST`" != "$LOCALHOST" ] + then + eval "echo changing the owner on the directory ... $VERB" + nis_chown $MASTER_SERVER $DOM + eval "echo $VERB" + fi + + if [ ! -z "$HOSTDEF" ] + then + setup_properties + echo "" + fi + + # start rpc.nispasswdd if setting up on localhost + if [ ! -z "$HOST" -a "`tolower $HOST`" = "$LOCALHOST" ] + then + # check to see if already running... + zone=`/usr/bin/zonename` + PROC=`pgrep -z $zone rpc.nispasswdd` + if [ -z "$PROC" ] + then + # We are displaying this message for + # compatibility; at present, rpc.nispasswdd(1M) + # was started by our enabling of + # network/rpc/nisplus. It may or may not have + # exited by this point. + eval "echo starting NIS+ password daemon ... $VERB" + else + eval "echo NIS+ password daemon already running ... $VERB" + fi + else + # else need to print message saying start it up + echo "" + echo "**IMPORTANT:" + echo " Be sure to start the NIS+ password daemon (rpc.nispasswdd) on the" + if [ -z "$HOST" ] + then + echo " new NIS+ non-root (subdomain) master server IF NOT ALREADY." + else + echo " new NIS+ non-root (subdomain) master server $HOST IF NOT ALREADY." + fi + echo "" + fi + + echo "" + echo "The server(s) for the non-root domain $DOM is(are) now" + echo "configured. You can now populate the standard NIS+ tables by" + echo "using the nispopulate or /usr/lib/nis/nisaddent commands." +} + + + +# +# replica setup Routine: +# ---------------------- +# +# This section contains the routine to setup a replica server. +# NOTE: If the -h <hostname> is specified, then it will configure the +# specified host as a replica of the domain. Otherwise, it will +# configure the local machine as a replica of the domain. +# replica() - sets up replica server. +# +# +# replica(): sets up a replica server. +# +replica() +{ + # ...check domain + if nistest -t D $DOM; + then + : + else + echo "**ERROR: $DOM does not exist." + exit 1 + fi + + if check_host "$HOST"; + then + exit 1 + fi + + # ...check permission + check_perm $DOM + if [ $? -ne 0 ] + then + echo "**ERROR: no permission to replicate directory $DOM" + exit 1 + fi + + echo "This script sets up an NIS+ replica server for domain" + echo "$DOM" + + if [ -z "$HOST" ] + then + HOST=$LOCALHOST + HOSTPRINC=$HOST.`nisdefaults -d` + fi + + if [ -z "$FORCE" ] + then + confirm + else + echo "" + print_info + fi + + echo "" + nis_server $DOM + if [ "`tolower $HOSTPRINC`" = "`tolower $MASTER_SERVER`" ] + then + echo "ERROR: $HOST is a master server for this domain." + echo "You cannot demote a master server to replica." + echo "If you really want to demote this master, you should" + echo "promote a replica server to master using nisserver" + echo "with the -M option." + exit 1 + fi + + is_server $HOSTPRINC + if [ $? -eq 0 ] + then + echo "WARNING: $HOST is already a server for this domain." + echo "If you choose to continue with this script, it will" + echo "try to replicate the groups_dir and org_dir directories" + echo "for this domain." + IGNORE="yes" + else + echo "This script will set up machine \"$HOST\" as an NIS+" + echo "replica server for domain $DOM $WITHOUT NIS compatibility." + echo "The NIS+ server daemon, rpc.nisd, must be running on $HOST" + echo "with the proper options to serve this domain." + fi + echo "" + + if [ -z "$FORCE" ] + then + get_yesno $CONTINUE + echo "" + fi + + $ECHO "rpcinfo -u $HOST 100300 3 > /dev/null" + if [ $? -ne 0 ] + then + echo "**ERROR: NIS+ server is not running on $HOST." + echo " You must do the following before becoming an NIS+ server:" + echo " 1. become an NIS+ client of the parent domain or any domain" + echo " above the domain which you plan to serve. (nisclient)" + echo " 2. start the NIS+ server. (rpc.nisd)" + exit 1 + fi + + eval "echo running nismkdir ... $VERB" + $ECHO nismkdir -s $HOSTPRINC $DOM + if [ -z "$IGNORE" -a $? -ne 0 ] + then + echo "**ERROR: it failed to replicate the directory." + exit 1 + fi + sleep 3 + $ECHO "nisupdkeys $DOM > /dev/null" + if [ $? -ne 0 ] + then + echo "**WARNING: nisupdkeys failed on directory $DOM" + echo " This script will not be able to continue." + echo " Please remove the $DOM directory using 'nisrmdir'." + exit 1 + fi + + $ECHO nismkdir -s $HOSTPRINC org_dir.$DOM + if [ -z "$IGNORE" -a $? -ne 0 ] + then + echo "**ERROR: it failed to replicate the org_dir directory." + exit 1 + fi + sleep 3 + $ECHO nismkdir -s $HOSTPRINC groups_dir.$DOM + if [ -z "$IGNORE" -a $? -ne 0 ] + then + echo "**ERROR: it failed to replicate the groups_dir directory." + exit 1 + fi + sleep 3 + + eval "echo updating the keys for directories ... $VERB" + $ECHO "nisupdkeys org_dir.$DOM > /dev/null" + if [ $? -ne 0 ] + then + echo "WARNING: nisupdkeys failed on directory org_dir.$DOM" + echo " You will need to run nisupdkeys manually:" + echo " 1. /usr/lib/nis/nisupdkeys org_dir.$DOM" + echo "" + fi + $ECHO "nisupdkeys groups_dir.$DOM > /dev/null" + if [ $? -ne 0 ] + then + echo "WARNING: nisupdkeys failed on directory groups_dir.$DOM" + echo " You will need to run nisupdkeys manually:" + echo " 1. /usr/lib/nis/nisupdkeys groups_dir.$DOM" + echo "" + fi + eval "echo $VERB" + + if [ ! -z "$GROUPSERVER" ] + then + if nisgrpadm -s -t $GROUPSERVER $HOSTPRINC; + then + : + else + eval "echo adding replica principal into group owner ...$VERB" + $ECHO nisgrpadm -a $GROUPSERVER $HOSTPRINC + $ECHO "nisctl -f g $DOM > /dev/null" + fi + fi + + eval "echo pinging $DOM directory object on new replica ... $VERB" + $ECHO "nisping -H ${HOST} $DOM > /dev/null" + sleep 10 + eval "echo pinging $DOM groups_dir object on new replica ... $VERB" + $ECHO "nisping -H ${HOST} groups_dir.$DOM > /dev/null" + sleep 10 + eval "echo pinging $DOM org_dir object on new replica ... $VERB" + $ECHO "nisping -H ${HOST} org_dir.$DOM > /dev/null" + + echo "" + echo "The system ${HOST} is now configured as a replica server for" + echo "domain $DOM." + echo "The NIS+ server daemon, rpc.nisd, must be running on $HOST" + echo "with the proper options to serve this domain." + echo "" + echo "If you want to run this replica in NIS (YP) compatibility" + echo "mode, you must ensure that rpc.nisd on $HOST will boot in" + echo "NIS-compatibility mode. Then, restart rpc.nisd with the" + echo "-Y' option. These actions should be taken after this" + echo "script completes." +} + + + +# +# +# * * * MAIN * * * +# + +# Display the obsolescence message in all the cases +echo "" +echo "******** ******** WARNING ******** ********" +echo "NIS+ might not be supported in a future release. Tools to aid" +echo "the migration from NIS+ to LDAP are available in the Solaris 9" +echo "operating environment. For more information, visit" +echo "http://www.sun.com/directory/nisplus/transition.html" +echo "******** ******** ******* ******** ********" +echo "" + +init + +parse_arg $* +shift $? + +check_domainname "$DOM" + +check_rootgrp "$GROUP" +if [ $? -ne 0 ] +then + echo "**ERROR: invalid group name." + echo " It must be a group in the $DOM domain." + exit 1 +fi + + +case $ACTION in +"master") + ${ROOT}_master;; +"replica") + replica;; +*) + echo "**ERROR: you must specify one of these options: -r, -M or -R" + print_usage + exit 1 +esac + +# As this operation is likely configuration changing, restart the +# name-services milestone (such that configuration-sensitive services +# are in turn restarted). +/usr/sbin/svcadm restart milestone/name-services diff --git a/usr/src/cmd/rpcsvc/nis/bin/nissetup.sh b/usr/src/cmd/rpcsvc/nis/bin/nissetup.sh new file mode 100644 index 0000000000..8ba5a11359 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/bin/nissetup.sh @@ -0,0 +1,448 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 1992-2002 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# Initial setup for NIS+ domains. Note this only needs to be run on those +# domains that you plan to make into user level domains (that is domains +# with host maps and passwds etc) +# + +USAGE="usage: nissetup [-Y] [domain]"; +PASSOFF="n-r" + +if [ $# -eq 0 ] +then + A=access=og=rmcd,nw=r; + D=`nisdefaults -d`; +else + case $1 in + -Y) + if [ $# -eq 1 ] + then + D=`nisdefaults -d`; + elif [ $# -eq 2 ] + then + D=$2; + else + echo $USAGE; + exit 1; + fi; + A=access=og=rmcd,nw=r; + PASSOFF="";; # allow "nobody" read access in YP mode + -*) + echo $USAGE; + exit 1;; + *) + if [ $# -ne 1 ] + then + echo $USAGE; + exit 1; + fi; + A=access=og=rmcd,nw=r; + D=$1;; + esac; +fi; + +# column access for most tables (passwd excluded, see below) +CA=nogw= + +if nistest org_dir.$D; +then + if nistest -t D org_dir.$D; + then + echo org_dir.$D already exists; + else + echo org_dir.$D exists and is not a directory + exit 1; + fi; +else + if nismkdir -D $A org_dir.$D; + then + echo org_dir.$D created; + else + echo couldn\'t create org_dir.$D + exit 1; + fi; +fi; + +if nistest groups_dir.$D; +then + if nistest -t D groups_dir.$D; + then + echo groups_dir.$D already exists; + else + echo groups_dir.$D exists and is not a directory + exit 1; + fi; +else + if nismkdir -D $A groups_dir.$D; + then + echo groups_dir.$D created; + else + echo couldn\'t create groups_dir.$D + exit 1; + fi; +fi; + + +# Table creation order is arbitrary. We do passwd, group, then +# other standard tables in alphabetical order. +# +# Default secure setup passwd table perms is og=rmcd,w=r: ----rmcdrmcdr--- +# +# Column Perm Update Final Perms Notes +# ====== =========== =========== ===== +# (login) name: n+r,og-mcd r---r---r---r--- +# passwd: og-mcd ----r---r---r--- 2.5+; see (5) +# o-cd,g-mcd ----rm--r---r--- [def] 2.0-2.4; see (6) +# uid, gid: n+r,og-mcd r---r---r---r--- +# gcos, home, shell: n+r r---rmcdrmcdr--- +# shadow: og-rmcd ---------------- see (7) +# +# Notes: +# (1) The entire table is readable to authenticated users. +# (2) Unauthenticated users can still see the "informational" columns, +# but not the passwd or shadow columns. Unauthenticated clients +# must have read access to the informational columns in order for +# programs looking up this info while running as "daemon" to work. +# (3) Entry owner can modify gcos, home, and shell; passwd for 2.0-2.4. +# (4) In the YP compat service setup, the table-level permissions get +# n+r added. This is required for YP client access, but overrides +# the hiding of the passwd column from unauthenticated clients. +# (5) The passwd column for 2.5 clients only: rpc.nispasswdd (NPD) +# modifies entries... must manually set o-m permission +# (6) The passwd column for 2.0-2.4: to support old clients +# (i.e. no NPD). This is the DEFAULT setup. +# (7) The shadow column contains password aging info; +# no added perms above those on table. + +if nistest passwd.org_dir.$D; +then + echo passwd.org_dir.$D already exists; +else + if nistbladm -D $A,$PASSOFF -c -s : passwd_tbl name=S,nogw=r passwd=C,n=,ogw=r,o+m uid=S,nogw=r gid=,nogw=r gcos=,n+r,o+m home=,n+r shell=,n+r shadow=,nogw= passwd.org_dir.$D; + + then + echo passwd.org_dir.$D created; + else + echo couldn\'t create passwd.org_dir.$D; + fi; +fi; + + +#group table permissions for default secure setup +# table ----rmcdrmcdr--- +# passwd column o+m +# members column o+m,n+r +# all other columns n+r +# +#Explanation is same as for passwd table above. +# + +if nistest group.org_dir.$D; +then + echo group.org_dir.$D already exists; +else + if nistbladm -D $A,$PASSOFF -c -s : group_tbl name=S,n+r passwd=C,o+m gid=S,n+r members=,o+m,n+r group.org_dir.$D; + then + echo group.org_dir.$D created; + else + echo couldn\'t create group.org_dir.$D; + fi; +fi; + +# permissions on all other tables for default secure setup is: +# A=access=og=rmcd,w=r,n=r; +# this is to allow all processes run as 'daemon' to have access to +# the information stored in the NIS+ tables. + +if nistest auto_master.org_dir.$D; +then + echo auto_master.org_dir.$D already exists; +else + if nistbladm -D $A -c automount_map key=S,$CA value=,$CA auto_master.org_dir.$D; + then + echo auto_master.org_dir.$D created; + else + echo couldn\'t create auto_master.org_dir.$D; + fi; +fi; + +if nistest auto_home.org_dir.$D; +then + echo auto_home.org_dir.$D already exists; +else + if nistbladm -D $A -c automount_map key=S,$CA value=,$CA auto_home.org_dir.$D; + then + echo auto_home.org_dir.$D created; + else + echo couldn\'t create auto_home.org_dir.$D; + fi; +fi; + +if nistest bootparams.org_dir.$D; +then + echo bootparams.org_dir.$D already exists; +else + if nistbladm -D $A -c bootparams_tbl key=SI,$CA value=,$CA bootparams.org_dir.$D; + then + echo bootparams.org_dir.$D created; + else + echo couldn\'t create bootparams.org_dir.$D; + fi; +fi; + +if nistest cred.org_dir.$D; +then + echo cred.org_dir.$D already exists; +else + if nistbladm -D $A -c -s : cred_tbl cname=SI,$CA auth_type=SI,$CA auth_name=SI,$CA public_data=,$CA,o+m private_data=C,$CA,o+m cred.org_dir.$D; + then + echo cred.org_dir.$D created; + else + echo couldn\'t create cred.org_dir.$D; + fi; +fi; + +if nistest ethers.org_dir.$D; +then + echo ethers.org_dir.$D already exists; +else + if nistbladm -D $A -c ethers_tbl addr=SI,$CA name=SI,$CA comment=,$CA ethers.org_dir.$D; + then + echo ethers.org_dir.$D created; + else + echo couldn\'t create ethers.org_dir.$D; + fi; +fi; + +if nistest hosts.org_dir.$D; +then + echo hosts.org_dir.$D already exists; +else + if nistbladm -D $A -c hosts_tbl cname=SI,$CA name=SI,$CA addr=SI,$CA comment=,$CA hosts.org_dir.$D; + then + echo hosts.org_dir.$D created; + else + echo couldn\'t create hosts.org_dir.$D; + fi; +fi; + +if nistest ipnodes.org_dir.$D; +then + echo ipnodes.org_dir.$D already exists; +else + if nistbladm -D $A -c ipnodes_tbl cname=SI,$CA name=SI,$CA addr=SI,$CA comment=,$CA ipnodes.org_dir.$D; + then + echo ipnodes.org_dir.$D created; + else + echo couldn\'t create ipnodes.org_dir.$D; + fi; +fi; + +if nistest mail_aliases.org_dir.$D; +then + echo mail_aliases.org_dir.$D already exists; +else + if nistbladm -D $A -c mail_aliases alias=SI,$CA expansion=SI,$CA comments=,$CA options=,$CA mail_aliases.org_dir.$D; + then + echo mail_aliases.org_dir.$D created; + else + echo couldn\'t create mail_aliases.org_dir.$D; + fi; +fi; + +if nistest netmasks.org_dir.$D; +then + echo netmasks.org_dir.$D already exists; +else + if nistbladm -D $A -c netmasks_tbl addr=SI,$CA mask=SI,$CA comment=,$CA netmasks.org_dir.$D; + then + echo netmasks.org_dir.$D created; + else + echo couldn\'t create netmasks.org_dir.$D; + fi; +fi; + +if nistest netgroup.org_dir.$D; +then + echo netgroup.org_dir.$D already exists; +else + if nistbladm -D $A -c netgroup_tbl name=S,$CA group=S,$CA host=SI,$CA user=S,$CA domain=SI,$CA comment=,$CA netgroup.org_dir.$D; + then + echo netgroup.org_dir.$D created; + else + echo couldn\'t create netgroup.org_dir.$D; + fi; +fi; + +if nistest networks.org_dir.$D; +then + echo networks.org_dir.$D already exists; +else + if nistbladm -D $A -c networks_tbl cname=SI,$CA name=SI,$CA addr=SI,$CA comment=,$CA networks.org_dir.$D; + then + echo networks.org_dir.$D created; + else + echo couldn\'t create networks.org_dir.$D; + fi; +fi; + + +if nistest protocols.org_dir.$D; +then + echo protocols.org_dir.$D already exists; +else + if nistbladm -D $A -c protocols_tbl cname=SI,$CA name=SI,$CA number=SI,$CA comment=,$CA protocols.org_dir.$D; + then + echo protocols.org_dir.$D created; + else + echo couldn\'t create protocols.org_dir.$D; + fi; +fi; + +if nistest rpc.org_dir.$D; +then + echo rpc.org_dir.$D already exists; +else + if nistbladm -D $A -c rpc_tbl cname=SI,$CA name=SI,$CA number=SI,$CA comment=,$CA rpc.org_dir.$D; + then + echo rpc.org_dir.$D created; + else + echo couldn\'t create rpc.org_dir.$D; + fi; +fi; + +if nistest services.org_dir.$D; +then + echo services.org_dir.$D already exists; +else + if nistbladm -D $A -c services_tbl cname=SI,$CA name=SI,$CA proto=SI,$CA port=SI,$CA comment=,$CA services.org_dir.$D; + then + echo services.org_dir.$D created; + else + echo couldn\'t create services.org_dir.$D; + fi; +fi; + +if nistest timezone.org_dir.$D; +then + echo timezone.org_dir.$D already exists; +else + if nistbladm -D $A -c timezone_tbl name=SI,$CA tzone=,$CA comment=,$CA timezone.org_dir.$D; + then + echo timezone.org_dir.$D created; + else + echo couldn\'t create timezone.org_dir.$D; + fi; +fi; + +# client_info table: this table is used to store client's server discovery +# information such as preferred servers and preferred option. +# +if nistest client_info.org_dir.$D; +then + echo client_info.org_dir.$D already exists; +else + if nistbladm -D group=admin.$D:$A -c client_info_tbl client=SI,$CA attr=SI,$CA info=,$CA flags=,$CA client_info.org_dir.$D + then + echo client_info.org_dir.$D created; + else + echo couldn\'t create client_info.org_dir.$D; + fi; +fi; + +# auth_attr table: This table is used to store authorization attributes +# +if nistest auth_attr.org_dir.$D; +then + echo auth_attr.org_dir.$D already exists; +else + if nistbladm -D group=admin.$D:$A -c auth_attr_tbl name=S,$CA res1=,$CA res2=,$CA short_desc=,$CA long_desc=,$CA attr=,$CA auth_attr.org_dir.$D + then + echo auth_attr.org_dir.$D created; + else + echo couldn\'t create auth_attr.org_dir.$D; + fi; +fi; + +# exec_attr table: This table is used to store execution attributes +# +if nistest exec_attr.org_dir.$D; +then + echo exec_attr.org_dir.$D already exists; +else + if nistbladm -D group=admin.$D:$A -c exec_attr_tbl name=S,$CA policy=S,$CA type=,$CA res1=,$CA res2=,$CA id=S,$CA attr=,$CA exec_attr.org_dir.$D + then + echo exec_attr.org_dir.$D created; + else + echo couldn\'t create exec_attr.org_dir.$D; + fi; +fi; + +# prof_attr table: This table is used to store profile attributes +# +if nistest prof_attr.org_dir.$D; +then + echo prof_attr.org_dir.$D already exists; +else + if nistbladm -D group=admin.$D:$A -c prof_attr_tbl name=S,$CA res1=,$CA res2=,$CA desc=,$CA attr=,$CA prof_attr.org_dir.$D + then + echo prof_attr.org_dir.$D created; + else + echo couldn\'t create prof_attr.org_dir.$D; + fi; +fi; + +# user_attr table: This table is used to store extended user attributes +# +if nistest user_attr.org_dir.$D; +then + echo user_attr.org_dir.$D already exists; +else + if nistbladm -D group=admin.$D:$A -c user_attr_tbl name=S,$CA qualifier=S,$CA res1=,$CA res2=,$CA attr=,$CA user_attr.org_dir.$D + then + echo user_attr.org_dir.$D created; + else + echo couldn\'t create user_attr.org_dir.$D; + fi; +fi; + +# audit_user table: This table is used to store audit attributes for users +# +if nistest audit_user.org_dir.$D; +then + echo audit_user.org_dir.$D already exists; +else + if nistbladm -D group=admin.$D:access=og=rmcd,nw= -c audit_user_tbl name=S,$CA always=,$CA never=,$CA audit_user.org_dir.$D + then + echo audit_user.org_dir.$D created; + else + echo couldn\'t create audit_user.org_dir.$D; + fi; +fi; +exit 0; diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisshowcache.c b/usr/src/cmd/rpcsvc/nis/bin/nisshowcache.c new file mode 100644 index 0000000000..f73c65ef9e --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/bin/nisshowcache.c @@ -0,0 +1,176 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisshowcache.c + * + * Copyright (c) 1988-1995 Sun Microsystems, Inc. + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Ported from + * "@(#)dump_cache.c 1.5 90/11/13 Copyr 1988 Sun Micro"; + * + * This program dumps the contents of the NIS+ location cache + * to the standard output. + */ + +#include <stdio.h> +#include <sys/types.h> +#include <rpcsvc/nis.h> +#include <sys/ipc.h> +#include <sys/sem.h> + +#ifndef _sys_sem_h +/* + * This guard is defined only in 4.1 systems and is to + * be included only in 5.0 as it is not defined there. + */ +union semun { + int val; + struct semid_ds *buf; + ushort *array; +}; +#endif /* _sys_sem_h */ + +static void print_semaphores(); +static void __nis_print_sems(int, int); + + +void +usage(char *name) +{ + fprintf(stderr, "usage: %s [-v]\n", name); + exit(1); +} + + +/* dump the context cache on the system */ +void +main(int argc, char *argv[]) +{ + extern int __nis_debuglevel; + int c; + nis_error status; + int new__nis_debug_level = 0; + char *cptr; /* passed to __nis_CacheInit(); we don't use it */ + + __nis_debuglevel = 0; + + /* get command line options */ + while ((c = getopt(argc, argv, "vsd")) != EOF) { + switch (c) { + case 'v': /* verbose mode */ + new__nis_debug_level = 3; + break; + + case 's': + /* + * special format - prints information on one line. + * undocumented feature. see cache_entry.cc + */ + new__nis_debug_level = 6; + break; + + case 'd': + /* + * another undocumented feature. + * prints the semaphores + */ + print_semaphores(); + break; + + case '?': /* error */ + usage(argv[0]); + break; + } + } + if ((status = __nis_CacheInit(&cptr)) != NIS_SUCCESS) { + fprintf(stderr, "NIS+ initialization error: %s\n", + nis_sperrno(status)); + exit((int)status); + } + if (new__nis_debug_level) + __nis_debuglevel = new__nis_debug_level; + else + __nis_debuglevel = 1; + + __nis_CachePrint(); + + exit(0); +} + + +#define NIS_SEM_R_KEY 100302 +#define NIS_SEM_W_KEY 100303 +#define NIS_W_NSEMS 2 +#define NIS_R_NSEMS 1 + +static void +print_semaphores() +{ + int sem_writer, sem_reader; + int semflg = 0; + + sem_reader = semget(NIS_SEM_R_KEY, NIS_R_NSEMS, semflg); + sem_writer = semget(NIS_SEM_W_KEY, NIS_W_NSEMS, semflg); + + if (sem_reader == -1 || sem_writer == -1) { + printf("Could not open semaphores!\n"); + return; + } + __nis_print_sems(sem_writer, sem_reader); +} + + +static void +__nis_print_sems(int sem_writer, int sem_reader) +{ + int i; + ushort w_array[NIS_W_NSEMS]; + ushort r_array[NIS_R_NSEMS]; + union semun semarg; + + + /* cache_manager (writer) semaphores */ + if (sem_writer != -1) { + semarg.array = w_array; + if (semctl(sem_writer, 0, GETALL, semarg) == -1) { + printf("nis_print_sems: semctl GETALL failed"); + return; + } + for (i = 0; i < NIS_W_NSEMS; i++) + printf("sem_writer[%d] = %d\n", i, w_array[i]); + } + /* reader */ + if (sem_reader != -1) { + semarg.array = r_array; + if (semctl(sem_reader, 0, GETALL, semarg) == -1) { + printf("nis_print_sems: semctl GETALL failed"); + return; + } + for (i = 0; i < NIS_R_NSEMS; i++) + printf("sem_reader[%d] = %d\n", i, r_array[i]); + } +} diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisstat.c b/usr/src/cmd/rpcsvc/nis/bin/nisstat.c new file mode 100644 index 0000000000..87b352b69b --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/bin/nisstat.c @@ -0,0 +1,258 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisstat.c + * + * Copyright (c) 1988-1992 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisstat.c + * + * This program will print out the various statistics being maintained by + * the NIS+ server. + */ + +#include <stdio.h> +#include <rpcsvc/nis.h> +#include <string.h> + +static struct statlist { + int stat_type; + char *stat_name; + char stat_data[80]; +} statlist[] = { + { TAG_ROOTSERVER, "root server", }, + { TAG_NISCOMPAT, "NIS compat mode", }, + { TAG_DNSFORWARDING, "DNS forwarding in NIS mode", }, + { TAG_SECURITY_LEVEL, "security level", }, + { TAG_DIRLIST, "serves directories", }, + { TAG_OPSTATS, "Operation Statistics", }, + { TAG_S_DCACHE, "directory cache", }, + { TAG_S_GCACHE, "group cache", }, + { TAG_S_STORAGE, "static storage", }, + { TAG_HEAP, "dynamic storage", }, + { TAG_UPTIME, "up since", }, + { 0, NULL, }}; + +extern int optind; +extern char *optarg; + +static void +usage(s) + char *s; +{ + fprintf(stderr, "usage: %s [-H host] [directory]\n", s); + exit(1); +} + +/* + * Print all the names of the directories served by this server + * This list comes in a space separated buffer. + */ +static void +print_list_dir(header, dirlist) + char *header; + char *dirlist; +{ + char *t, *tmp; + + tmp = strdup(dirlist); + if (tmp == NULL) { + fprintf(stderr, "No memory!\n"); + exit(1); + } + + printf("Stat '%s':\n", header); + /* we will print the list in the reverse order */ + while (t = strrchr(tmp, ' ')) { + *t = NULL; + printf("\t%s\n", t + sizeof (char)); + } + printf("\t%s\n", tmp); +} + +/* + * Print all statistical information on this server. + * This list comes in a "\n" separated buffer. + */ +static void +print_stats(header, statlist) + char *header; + char *statlist; +{ + char *t, *tmp; + bool_t first = TRUE; + + tmp = strdup(statlist); + if (tmp == NULL) { + fprintf(stderr, "No memory!\n"); + exit(1); + } + + printf("Stat '%s':\n", header); + while (t = strchr(tmp, '\n')) { + *t = NULL; + if (first) { + first = FALSE; + if ((tmp[0] == NULL) || (strcmp(tmp, "stats:") == 0)) { + /* dont print this */ + tmp = t + sizeof (char); + continue; + } + } + printf("\t%s\n", tmp); + tmp = t + sizeof (char); + } + if (tmp && tmp[0]) + printf("\t%s\n", tmp); +} + +static +int +match_host(char *host, char *target) +{ + int len = strlen(host); + + if (strncasecmp(host, target, len) == 0 && + (target[len] == '.' || target[len] == '\0')) + return (1); + + return (0); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + nis_server *servers; + nis_error status; + nis_tag tags[50], *res; + int j, i, maxtag, ns, c; + nis_name domain; + char dname[1024], *host = NULL; + nis_result *lres; + nis_object *obj; + bool_t print_once = FALSE; + bool_t nis_compat; + + while ((c = getopt(argc, argv, "H:")) != -1) { + switch (c) { + case 'H' : + host = optarg; + break; + default : + usage(argv[0]); + break; + } + } + if (argc - optind) + domain = argv[optind]; + else + domain = nis_local_directory(); + + /* expand the name if not fully qualified */ + if (domain[strlen(domain) - 1] != '.') + lres = nis_lookup(domain, EXPAND_NAME); + else + lres = nis_lookup(domain, 0); + + if (lres->status != NIS_SUCCESS) { + fprintf(stderr, "\"%s\": %s\n", + domain, nis_sperrno(lres->status)); + exit(1); + } + obj = lres->objects.objects_val; + sprintf(dname, "%s.%s", obj->zo_name, obj->zo_domain); + if (__type_of(obj) != NIS_DIRECTORY_OBJ) { + fprintf(stderr, "\"%s\": Not a directory.\n", dname); + exit(1); + } + ns = obj->DI_data.do_servers.do_servers_len; + servers = obj->DI_data.do_servers.do_servers_val; + + printf("Statistics for domain \"%s\" :\n\n", dname); + memset(tags, 0, sizeof (nis_tag) * 50); + for (i = 0; i < 50 && statlist[i].stat_type; i++) { + tags[i].tag_type = statlist[i].stat_type; + tags[i].tag_val = statlist[i].stat_data; + if (tags[i].tag_type == TAG_OPSTATS) + strcpy(tags[i].tag_val, "all"); + } + maxtag = i; + + for (i = 0; i < ns; i++) { + if (host && !match_host(host, servers[i].name)) + continue; + if (print_once) + printf("\n"); + else + print_once = TRUE; + status = nis_stats(&servers[i], tags, maxtag, &res); + if (status != NIS_SUCCESS) { + fprintf(stderr, + "nisstat: Error talking to host \"%s\": %s\n", + servers[i].name, nis_sperrno(status)); + continue; + } + printf("Statistics from server : \"%s\"\n", servers[i].name); + nis_compat = FALSE; + for (j = 0; j < maxtag; j++) { + if (strcmp(res[j].tag_val, "<Unknown Statistic>") == 0) + /* Dont print this one */ + continue; + switch (statlist[j].stat_type) { + case TAG_OPSTATS: + print_stats(statlist[j].stat_name, + res[j].tag_val); + break; + case TAG_DIRLIST: + print_list_dir(statlist[j].stat_name, + res[j].tag_val); + break; + case TAG_NISCOMPAT: + if (strcmp(res[j].tag_val, "OFF") != 0) + nis_compat = TRUE; + printf("Stat '%s' = '%s'\n", + statlist[j].stat_name, res[j].tag_val); + break; + case TAG_DNSFORWARDING: + /* + * Assume that the TAG_NISCOMPAT + * answer comes first + */ + if (nis_compat == FALSE) + break; /* Dont print this answer */ + /* else fall through */ + default: + printf("Stat '%s' = '%s'\n", + statlist[j].stat_name, res[j].tag_val); + break; + } + } + } + nis_freeresult(lres); + exit(0); +} diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisupdkeys.c b/usr/src/cmd/rpcsvc/nis/bin/nisupdkeys.c new file mode 100644 index 0000000000..73543d706b --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/bin/nisupdkeys.c @@ -0,0 +1,548 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisupdkeys.c + * + * Copyright (c) 1988-1995 Sun Microsystems, Inc. + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisupdkeys.c + * + * This function will read the list of servers from a directory object, + * update them with the proper public keys and write the directory object + * back. + * + */ + +#include <stdio.h> +#include <netdb.h> +#include <netdir.h> +#include <netconfig.h> +#include <netinet/in.h> +#include <rpc/key_prot.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/nis_dhext.h> +#include <string.h> +#include <stdlib.h> + +extern int optind; +extern char *optarg; + +extern char *inet_ntoa(); +extern int gethostname(char *name, int namelen); +extern nis_server *__nis_host2nis_server_g(); +extern void __nis_netconfig2ep(struct netconfig *, endpoint *); + +static +int +match_host(char *host, char *target) +{ + int len = strlen(host); + + if (strncasecmp(host, target, len) == 0 && + (target[len] == '.' || target[len] == '\0')) + return (1); + + return (0); +} + + +void +usage(cmd) + char *cmd; +{ + fprintf(stderr, "usage: %s [-C | -a] [-H host] [directory]\n", cmd); + fprintf(stderr, "usage: %s -s [-C | -a] [-H host]\n", cmd); + exit(1); +} + +int +clearkeydata(h, ns, srvs) + char *h; /* host to act on */ + int ns; /* number of servers */ + nis_server *srvs; /* array of servers */ +{ + int i, is_modified; + + for (i = 0, is_modified = 0; i < ns; i++) { + if (h && !match_host(h, srvs[i].name)) + continue; + if (srvs[i].key_type != NIS_PK_NONE) { + printf("\tClearing server %s's key\n", srvs[i].name); + srvs[i].key_type = NIS_PK_NONE; + srvs[i].pkey.n_bytes = 0; + srvs[i].pkey.n_len = 0; + is_modified++; + } else { + printf("\tNo keys exist for the server \"%s\"\n", + srvs[i].name); + } + } + return (is_modified); +} + +void +copyserv(nis_server *dst, nis_server *src, int *mod) +{ + dst->key_type = src->key_type; + dst->pkey.n_len = src->pkey.n_len; + dst->pkey.n_bytes = src->pkey.n_bytes; + *mod = *mod + 1; +} + + +int +updkeydata(h, ns, srvs) + char *h; /* host to act on */ + int ns; /* number of servers */ + nis_server *srvs; /* array of servers */ +{ + int i, is_modified; + char netname[MAXNETNAMELEN]; + nis_server *newserver; + int errcode; + + for (i = 0, is_modified = 0; i < ns; i++) { + if (h && !match_host(h, srvs[i].name)) + continue; + if (! host2netname(netname, srvs[i].name, NULL)) { + fprintf(stderr, "\tERROR: No netname for \"%s\"\n", + srvs[i].name); + continue; + } +#ifdef DEBUG + printf("\tFetching Public key for server %s ...\n", + srvs[i].name); + printf("\tnetname = '%s'\n", netname); +#endif + + /* + * Basically let __nis_host2nis_server do the magic of + * determining what kind of nis_server should be + * storred in the directory object. All we need to do + * is preserve endpoint (address) information and + * print out the appropriate message. + * + * The new publickey should come from the master so + * that a stale key is not returned from a replica. + * However there is currently no way of doing this so + * replicas should be sync'd with the master. + */ + + if (!(newserver = + __nis_host2nis_server_g(h, TRUE, FALSE, + &errcode))) { + nis_perror(errcode, "nisupdkeys"); + fprintf(stderr, "No changes were made.\n"); + exit(1); + } + + if (strcmp(srvs[i].name, newserver->name) != 0) + continue; + + switch (srvs[i].key_type) { + case NIS_PK_NONE: + switch (newserver->key_type) { + case NIS_PK_NONE: + break; + case NIS_PK_DH: + printf( + "\tInstalling %s's public key in this object\n", + srvs[i].name); + copyserv(&srvs[i], newserver, &is_modified); + break; + case NIS_PK_DHEXT: + printf( + "\tInstalling %s's public key(s) in this object\n", + srvs[i].name); + copyserv(&srvs[i], newserver, &is_modified); + break; + default: + fprintf(stderr, + "\tERROR! Unknown server type.\n"); + continue; + } + break; + case NIS_PK_DH: + switch (newserver->key_type) { + case NIS_PK_NONE: + printf( + "\tRemoving %s's public key in this object\n", + srvs[i].name); + copyserv(&srvs[i], newserver, &is_modified); + break; + case NIS_PK_DH: + printf( + "\tUpdating %s's public key in this object\n", + srvs[i].name); + copyserv(&srvs[i], newserver, &is_modified); + break; + case NIS_PK_DHEXT: + printf( + "\tUpdating %s's public key(s) in this object\n", + srvs[i].name); + copyserv(&srvs[i], newserver, &is_modified); + break; + default: + fprintf(stderr, + "\tERROR! Unknown server type.\n"); + continue; + } + break; + case NIS_PK_DHEXT: + switch (newserver->key_type) { + case NIS_PK_NONE: + printf( + "\tRemoving %s's public key in this object\n", + srvs[i].name); + copyserv(&srvs[i], newserver, &is_modified); + break; + case NIS_PK_DH: + printf( + "\tInstalling %s's public key in this object\n", + srvs[i].name); + copyserv(&srvs[i], newserver, &is_modified); + break; + case NIS_PK_DHEXT: + printf( + "\tUpdating %s's public key(s) in this object\n", + srvs[i].name); + copyserv(&srvs[i], newserver, &is_modified); + break; + default: + fprintf(stderr, + "\tERROR! Unknown server type.\n"); + continue; + } + default: + switch (newserver->key_type) { + case NIS_PK_NONE: + break; + case NIS_PK_DH: + printf( + "\tInstalling %s's public key in this object\n", + srvs[i].name); + copyserv(&srvs[i], newserver, &is_modified); + break; + case NIS_PK_DHEXT: + printf( + "\tInstalling %s's public key(s) in this object\n", + srvs[i].name); + copyserv(&srvs[i], newserver, &is_modified); + break; + default: + fprintf(stderr, + "\tERROR! Unknown server type.\n"); + continue; + } + } + + } + return (is_modified); +} + +/* + * updaddrdata() + * + * For each server in the list, update its address information to be + * current. If h is non-null only update information for that host. + */ +int +updaddrdata(h, ns, srvs) + char *h; /* host to act on */ + int ns; /* number of servers */ + nis_server *srvs; /* array of servers */ +{ + register int i, j, k; + endpoint *eps; /* endpoints */ + int nep; /* num of eps */ + + struct netconfig *nc; /* netconfig structure */ + void *nch; /* netconfig structure handle */ + struct nd_hostserv hs; /* netconfig database hostserv */ + struct nd_addrlist *addrs; /* netconfig database addr list */ + + /* XXX only update TCP/IP addresses at present */ + for (i = 0; i < ns; i++) { + if (h && !match_host(h, srvs[i].name)) + continue; + eps = srvs[i].ep.ep_val; + nep = srvs[i].ep.ep_len; + + for (j = 0; j < nep; j++) { + free(eps[j].uaddr); + free(eps[j].family); + free(eps[j].proto); + } + + /* setup params for netdir_getbyname() */ + hs.h_host = srvs[i].name; + hs.h_serv = "rpcbind"; + + /* count how many server entries we need */ + j = 0, nch = setnetconfig(); + while (nc = getnetconfig(nch)) { + if (strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0) + continue; + if (!netdir_getbyname(nc, &hs, &addrs)) { + j += addrs->n_cnt; + netdir_free((char *)addrs, ND_ADDRLIST); + } + } + endnetconfig(nch); + + if (j == 0) { + fprintf(stderr, + "nisupdkeys: Can't get address information for " + "host \"%s\"\n", + srvs[i].name); + exit(1); + } + + /* got server count and allocate space */ + srvs[i].ep.ep_len = nep = j; + if (!(srvs[i].ep.ep_val = eps = + (endpoint*)malloc(nep*sizeof (struct endpoint)))) { + return (0); + } + + /* fill in new server address info */ + j = 0, nch = setnetconfig(); + + /* keep going if we still have more interfaces */ + while (nc = getnetconfig(nch)) { + if (strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0) + continue; + if (!netdir_getbyname(nc, &hs, &addrs)) { + for (k = 0; k < addrs->n_cnt; k++) { + eps[j].uaddr = + taddr2uaddr(nc, &(addrs->n_addrs[k])); + __nis_netconfig2ep(nc, &(eps[j])); + /* if any of these returned NULL, bail */ + if (!(eps[j].uaddr && eps[j].family && + eps[j].proto)) { + netdir_free((char *)addrs, ND_ADDRLIST); + endnetconfig(nch); + return (0); + } + j++; + } + netdir_free((char *)addrs, ND_ADDRLIST); + } + } + endnetconfig(nch); /* free(3C)'s NC data structs */ + + if (j == 0) { + fprintf(stderr, + "nisupdkeys: Can't get address information for " + "host \"%s\"\n", + srvs[i].name); + exit(1); + } + } + return (1); +} + +#define UPD_KEYS 0 +#define CLR_KEYS 1 +#define UPD_ADDR 2 + +main(argc, argv) + int argc; + char *argv[]; +{ + char dname[NIS_MAXNAMELEN]; + char *server = NULL; + nis_server *srvlist; + char *dirlist[NIS_MAXREPLICAS], **curdir; + int ns, is_modified; + nis_object *obj; + nis_result *res, *mres; + int c; + int op = UPD_KEYS; + int i = 0; + bool_t hostupdate = FALSE; + char *hostname; + + while ((c = getopt(argc, argv, "CsaH:")) != -1) { + switch (c) { + case 'C' : + op = CLR_KEYS; + break; + case 'a' : + op = UPD_ADDR; + break; + case 'H' : + server = optarg; + break; + case 's' : + hostupdate = TRUE; + break; + case '?' : + default : + fprintf(stderr, "Unrecognized option.\n"); + usage(argv[0]); + break; + } + } + + if (server) + hostname = server; + else + hostname = nis_local_host(); + + if (hostupdate == TRUE) { + /* + * get the list of directories served by this server and + * update all those directory objects. + */ + nis_server *nisserver; + nis_tag tags, *tagres; + nis_error status; + char *t, *dirname, tmpbuf[1024]; + + if (argc > optind) { + fprintf(stderr, + "No directories allowed with -s option\n"); + usage(argv[0]); + } + if (!(nisserver = + __nis_host2nis_server_g(hostname, TRUE, FALSE, NULL))) + exit(1); + + /* Get a list of directories served by this server */ + tags.tag_type = TAG_DIRLIST; + tags.tag_val = ""; + status = nis_stats(nisserver, &tags, 1, &tagres); + if (status != NIS_SUCCESS) { + fprintf(stderr, + "nisupdkeys: Error talking to host \"%s\", " + "error was %s\n", hostname, nis_sperrno(status)); + exit(1); + } + if ((strcmp(tagres->tag_val, "<Unknown Statistic>") == 0) || + (strcasecmp(tagres->tag_val, "<error>") == 0) || + (strcmp(tagres->tag_val, " ") == 0)) { + fprintf(stderr, + "Attributes for the server \"%s\" cannot be " + "updated by \"nisupdkeys -s\"\n", hostname); + fprintf(stderr, + "Instead, use the following for all directories " + "served by \"%s\"\n", + hostname); + fprintf(stderr, "\t%s [-a|-C] -H \"%s\" dir_name " + "... \n", argv[0], hostname); + exit(1); + } + + strcpy(tmpbuf, tagres->tag_val); + dirname = tmpbuf; + while (t = strchr(dirname, ' ')) { + *t++ = NULL; + dirlist[i++] = dirname; + dirname = t; + } + dirlist[i++] = dirname; + } else { + while ((argc - optind) > 0) + dirlist[i++] = argv[optind++]; + if (i == 0) { + dirlist[0] = nis_local_directory(); + i++; + } + } + dirlist[i] = NULL; + + res = NULL; + for (curdir = dirlist; *curdir; curdir++) { + is_modified = 0; + + /* if res != NULL its been used before */ + if (res) + nis_freeresult(res); + + printf("Updating directory object \"%s\" ...\n", *curdir); + res = nis_lookup(*curdir, MASTER_ONLY+EXPAND_NAME); + if (res->status != NIS_SUCCESS) { + fprintf(stderr, + "\tERROR: Unable to retrieve object.\n"); + nis_perror(res->status, *curdir); + continue; + } + obj = res->objects.objects_val; + sprintf(dname, "%s.%s", obj->zo_name, obj->zo_domain); + if (__type_of(obj) != NIS_DIRECTORY_OBJ) { + fprintf(stderr, "\tERROR: \"%s\" is not a directory.\n", + dname); + continue; + } + ns = obj->DI_data.do_servers.do_servers_len; + srvlist = obj->DI_data.do_servers.do_servers_val; + /* if a specific host has been specified */ + if (server) { + for (i = 0; i < ns; ++i) { + if (match_host(server, srvlist[i].name)) + break; + } + if (i == ns) { + fprintf(stderr, + "\tERROR: Host \"%s\" does not serve " + "directory \"%s\"\n", server, *curdir); + fprintf(stderr, + "\tDirectory \"%s\" is not being " + "modified\n", *curdir); + continue; + } + } + switch (op) { + case CLR_KEYS : + is_modified = clearkeydata(server, ns, srvlist); + break; + case UPD_KEYS : + is_modified = updkeydata(server, ns, srvlist); + break; + case UPD_ADDR : + is_modified = updaddrdata(server, ns, srvlist); + break; + default: + /* should not have happened */ + exit(1); + } + if (is_modified) { + mres = nis_modify(dname, obj); + if (mres->status != NIS_SUCCESS) { + fprintf(stderr, + "\tERROR: Unable to modify directory " + "object \"%s\"\n", *curdir); + nis_perror(mres->status, dname); + } + nis_freeresult(mres); + } + } + if (res) + nis_freeresult(res); + return (0); +} diff --git a/usr/src/cmd/rpcsvc/nis/bin/req.flg b/usr/src/cmd/rpcsvc/nis/bin/req.flg new file mode 100644 index 0000000000..b3b75227c4 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/bin/req.flg @@ -0,0 +1,28 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +#pragma ident "%Z%%M% %I% %E% SMI" +# + + +echo_file usr/src/lib/libnsl/include/rpcsvc/nis_dhext.h diff --git a/usr/src/cmd/rpcsvc/nis/cachemgr/Makefile b/usr/src/cmd/rpcsvc/nis/cachemgr/Makefile new file mode 100644 index 0000000000..ee2a2f5f73 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/cachemgr/Makefile @@ -0,0 +1,84 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# Sample makefile to build the cache manager +# +SED= sed + +PROTOCOL_DIR=$(ROOT)/usr/include/rpcsvc + +HDRS= + +SRCS=cachemgr.c + +OBJS=cachemgr.o nis_cache_svc.o nis_cache_xdr.o + +PROG=nis_cachemgr + +include $(SRC)/cmd/Makefile.cmd + +LDLIBS += -lnsl + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTUSRSBINPROG) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +nis_cache.h: $(PROTOCOL_DIR)/nis_cache.x + $(RPCGEN) -h $(PROTOCOL_DIR)/nis_cache.x |\ + $(SED) -e 's!\"$(PROTOCOL_DIR)/nis_cache.h\"!"nis_cache.h"!' > $@ + +nis_cache_svc.c: $(PROTOCOL_DIR)/nis_cache.x nis_clnt.h + $(RPCGEN) -m $(PROTOCOL_DIR)/nis_cache.x |\ + $(SED) -e 's!\"$(PROTOCOL_DIR)/nis_cache.h\"!"nis_cache.h"!' > $@ + +nis_cache_xdr.c: $(PROTOCOL_DIR)/nis_cache.x nis_clnt.h + $(RPCGEN) -c $(PROTOCOL_DIR)/nis_cache.x |\ + $(SED) -e 's!\"$(PROTOCOL_DIR)/nis_cache.h\"!"nis_cache.h"!' > $@ + +nis_clnt.h: $(PROTOCOL_DIR)/nis.x + $(RPCGEN) -C -h $(PROTOCOL_DIR)/nis.x |\ + $(SED) -n -e '/EDIT_START/,$$ p' |\ + $(SED) -e 's/_3_svc/_svc/' |\ + $(SED) -e 's/_3/_clnt/' > $@ + +nis_cachemgr: $(OBJS) nis_clnt.h + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +%.o: %.c nis_cache.h nis_clnt.h + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.c b/usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.c new file mode 100644 index 0000000000..efc5804303 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.c @@ -0,0 +1,533 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <signal.h> +#include <netdir.h> +#include <errno.h> +#include <rpcsvc/daemon_utils.h> +#include <rpcsvc/nis.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <priv.h> +#include <priv_utils.h> +#include "nis_cache.h" + +#define NIS_CACHEMGR_UID 0 +#define NIS_CACHEMGR_GID 3 + + +int mgr_verbose; +int alarm_rang = 0; +int got_sighup = 0; + +extern int __nis_debuglevel; +extern void cacheprog_2(struct svc_req *, SVCXPRT *); +extern nis_error __nis_CacheMgrInit_discard(int); + +static char *create_ti_server(); +static void my_svc_run(); +static void do_timers(); +static void do_config(); +static void set_file_ownership(char *); + +void +hangup_handler(int sig) +{ + got_sighup = 1; +} + +void +cache_purge_handler(int sig) +{ + alarm_rang = 1; +} + +/* + * Cleanup, set signal handler to default, and send ourselves + * the signal again. + */ +void +cache_cleanup_handler(int sig) +{ + __nis_CacheMgrCleanup(); + sigset(sig, SIG_DFL); + kill(getpid(), sig); +} + +static void usage(name) + char *name; +{ + printf("\tusage: %s\n", name); + printf("\t\t-d [debug_level]\n"); + printf("\t\t-i <initialize cache when starting up>\n"); + printf("\t\t-m [max size of cache file (in pages) ]\n"); + printf( + "\t\t-n <insecure mode - directory object signatures not checked>.\n"); + printf("\t\t-s [initial size of cache file (in pages)]\n"); + printf("\t\t-v <verbose - sends events to syslog>\n"); + exit(1); +} + +main(int argc, char **argv) +{ + int c; + int n; + int pid; + int console; + char *p; + int init_cache = 0; + char *options = 0; + char *uaddr; + nis_error err; + + /* + * Ensure all files are owned by nis_cachemgr. + */ + set_file_ownership(NIS_DIRECTORY); + set_file_ownership(CACHE_FILE); + set_file_ownership(PRIVATE_CACHE_FILE); + set_file_ownership(TMP_CACHE_FILE); + set_file_ownership(COLD_START_FILE); + set_file_ownership(DOT_FILE); + + console = open("/dev/console", 2); + if (console == -1) { + (void) fprintf(stderr, "can't open /dev/console. %s\n", + strerror(errno)); + exit(1); + } + + /* + * Make the process a privilege aware daemon. + * Only "basic" privileges are required. + */ + if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, + NIS_CACHEMGR_UID, NIS_CACHEMGR_GID, (char *)NULL) == -1) { + (void) fprintf(stderr, "should be run with" + " sufficient privileges\n"); + exit(1); + } + + /* + * Make sure any files we create without explicit permissions + * aren't group or world writeable. + */ + (void) umask(0022); + + openlog("nis_cachemgr", LOG_CONS, LOG_DAEMON); + + while ((c = getopt(argc, argv, "ivd:a:ns:m:o:")) != EOF) { + switch (c) { + case 'd': + __nis_debuglevel = atoi(optarg); + break; + + case 'i': + init_cache = 1; + break; + + case 'm': + case 'n': + case 's': + /* obsolete */ + break; + + case 'v': + mgr_verbose = 1; + break; + + case 'o': + options = optarg; + break; + + default: + usage(argv[0]); + break; + } + } + + + if (__nis_debuglevel && (setpflags(PRIV_DEBUG, 1) == -1)) { + (void) fprintf(stderr, + "can't turn on privilege debug flag. %s\n", + strerror(errno)); + exit(1); + } + + /* + * We pass options to the cache library through the + * environment variable NIS_OPTIONS. If options are + * passed on the command line, then we put them into + * the environment. + */ + if (options) { + n = strlen("NIS_OPTIONS=") + strlen(options) + 1; + p = (char *)malloc(n); + if (p == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + strcpy(p, "NIS_OPTIONS="); + strcat(p, options); + putenv(p); + } + + if (init_cache) { + unlink(PRIVATE_CACHE_FILE); + unlink(TMP_CACHE_FILE); + } + + err = __nis_CacheMgrInit_discard(init_cache); + if (err != NIS_SUCCESS) { + nis_perror(err, "can't initialize cache"); + exit(1); + } + + if (!__nis_debuglevel) { + pid = fork(); + if (pid == -1) { + perror("fork"); + exit(1); + } else if (pid != 0) { + exit(0); + } + close(0); + close(1); + close(2); + dup2(console, 1); + dup2(console, 2); + setsid(); + } + + + /* + * Remove unnecessary privileges - fork and exec. + * Removing permitted privileges also removes effective privileges. + */ + if (priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_PROC_FORK, + PRIV_PROC_EXEC, (char *)NULL) == -1) { + syslog(LOG_ERR, + "can't set reduced privileges. %s\n", + strerror(errno)); + } + + sigset(SIGHUP, hangup_handler); + sigset(SIGALRM, cache_purge_handler); + sigset(SIGPIPE, SIG_IGN); + sigset(SIGTERM, cache_cleanup_handler); + sigset(SIGINT, cache_cleanup_handler); + sigset(SIGKILL, cache_cleanup_handler); + sigset(SIGIOT, cache_cleanup_handler); + sigset(SIGILL, cache_cleanup_handler); + sigset(SIGQUIT, cache_cleanup_handler); + sigset(SIGSYS, cache_cleanup_handler); + sigset(SIGFPE, cache_cleanup_handler); + + uaddr = create_ti_server(); + __nis_CacheMgrUpdateUaddr(uaddr); + free(uaddr); + __nis_CacheMgrMarkUp(); + + if (mgr_verbose) + syslog(LOG_INFO, "running service, uaddr = %s", uaddr); + do_timers(); + my_svc_run(); + syslog(LOG_ERR, "svc_run() returned"); + exit(1); +} + +static void set_file_ownership(char *file) +{ + struct stat info; + if (stat(file, &info) == -1) { + if (errno == ENOENT) + return; + (void) fprintf(stderr, "can't stat file %s. %s\n", + file, strerror(errno)); + exit(1); + } + if (info.st_uid != NIS_CACHEMGR_UID || + info.st_gid != NIS_CACHEMGR_GID) { + if (chown(file, NIS_CACHEMGR_UID, NIS_CACHEMGR_GID) == -1) { + (void) fprintf(stderr, "can't chown %s to daemon. %s\n", + file, strerror(errno)); + exit(1); + } + } + +} +static +void +my_svc_run() +{ + int i; + int npollfds = 0; + pollfd_t *pollset = NULL; + + while (1) { + if (got_sighup) { + got_sighup = 0; + do_config(); + } else if (alarm_rang) { + alarm_rang = 0; + do_timers(); + } + + if (npollfds != svc_max_pollfd) { + pollset = realloc(pollset, + sizeof (pollfd_t) * svc_max_pollfd); + npollfds = svc_max_pollfd; + } + + if (npollfds == 0) + break; /* None waiting, hence return */ + + /* + * Get existing array of pollfd's, should really compress + * this but it shouldn't get very large (or sparse). + */ + (void) memcpy(pollset, svc_pollfd, + sizeof (pollfd_t) * svc_max_pollfd); + + switch (i = poll(pollset, npollfds, -1)) { + case -1: + /* + * We ignore all errors, continuing with the assumption + * that it was set by the signal handlers (or any + * other outside event) and not caused by poll(). + */ + case 0: + continue; + default: + svc_getreq_poll(pollset, i); + } + } +} + + +static +void +do_config() +{ + ulong_t secs; + + secs = __nis_CacheMgrRefreshCache(); + alarm(secs); +} + + + +static +void +do_timers() +{ + ulong_t secs; + + secs = __nis_CacheMgrTimers(); + alarm(secs); +} + +static +char * +create_ti_server() +{ + SVCXPRT *transp; + struct netconfig *nconf; + char *uaddr; + void *nc_handle; + bool_t found_loopback = FALSE; + + /* + * we want to get a loopback transport to have clients talk to the + * cachemgr + * this provides security in the sense that only local folk can access + * the cachemgr. Also, it makes the messages inherently more reliable. + * we also want a CLTS transport so search in the netconfig database + * for this type of transport. + * This is an implicit protocol between the cachemgr and the clients + * and the clients make the same selection of trasport (in + * cache_getclnt.c) and use the uaddr that the cachemgr writes into + * the cache file. This uaddr would be valid only for this transport + * that both agree on implicitly. + * If the selection scheme is changed here it should also be changed + * in the client side (cache_getclnt.c) + */ + + nc_handle = setnetconfig(); + if (nc_handle != (void *) NULL) { + while (nconf = getnetconfig(nc_handle)) + if ((strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) && + (nconf->nc_semantics == NC_TPI_CLTS)) { + found_loopback = TRUE; + break; + } + } + if (!found_loopback) { + syslog(LOG_ERR, + "Could not get loopback transport from netconfig database"); + exit(1); + + } + /* create a new transport endpoint */ + transp = svc_tp_create( + (void (*)(struct svc_req *, SVCXPRT *))cacheprog_2, + CACHEPROG, CACHE_VER_2, nconf); + if (!transp) { + syslog(LOG_ERR, + "create_ti_server: cannot create server handle on loopback transport"); + exit(1); + } + uaddr = taddr2uaddr(nconf, &transp->xp_ltaddr); + endnetconfig(nc_handle); + return (uaddr); +} + +/* + * Service routines. + */ + +void * +nis_cache_add_entry_2() +{ + if (mgr_verbose) + syslog(LOG_INFO, "nis_cache_add_entry"); +} + +void * +nis_cache_remove_entry_2() +{ + if (mgr_verbose) + syslog(LOG_INFO, "nis_cache_remove_entry"); +} + +void * +nis_cache_read_coldstart_2() +{ + if (mgr_verbose) + syslog(LOG_INFO, "nis_cache_read_coldstart"); +} + +void * +nis_cache_refresh_entry_2() +{ + if (mgr_verbose) + syslog(LOG_INFO, "nis_cache_refresh_entry"); +} + +nis_error * +nis_cache_bind_replica_2(char **argp, struct svc_req *rqstp) +{ + static nis_error err; + + if (mgr_verbose) + syslog(LOG_INFO, "nis_cache_bind_replica_2(%s)", *argp); + err = __nis_CacheMgrBindReplica(*argp); + return (&err); +} + +nis_error * +nis_cache_bind_master_2(char **argp, struct svc_req *rqstp) +{ + static nis_error err; + + if (mgr_verbose) + syslog(LOG_INFO, "nis_cache_bind_master_2(%s)", *argp); + err = __nis_CacheMgrBindMaster(*argp); + return (&err); +} + +nis_error * +nis_cache_bind_server_2(bind_server_arg *argp, struct svc_req *rqstp) +{ + static nis_error err; + + if (mgr_verbose) + syslog(LOG_INFO, + "nis_cache_bind_server_2(%s)", argp->srv->name); + err = __nis_CacheMgrBindServer(argp->srv, argp->nsrv); + return (&err); +} + +refresh_res * +nis_cache_refresh_binding_2(nis_bound_directory *binding, struct svc_req *rqstp) +{ + static refresh_res res; + + if (mgr_verbose) + syslog(LOG_INFO, "nis_cache_refresh_binding_2"); + res.changed = __nis_CacheMgrRefreshBinding(binding); + res.ep.family = NULL; + res.ep.proto = NULL; + res.ep.uaddr = NULL; + return (&res); +} + +refresh_res * +nis_cache_refresh_address_2(nis_bound_endpoint *bep, struct svc_req *rqstp) +{ + static refresh_res res; + + if (mgr_verbose) + syslog(LOG_INFO, "nis_cache_refresh_address_2"); + /* xdr_free old result */ + xdr_free(xdr_refresh_res, (char *)&res); + res.changed = __nis_CacheMgrRefreshAddress(bep); + res.ep.family = NULL; /* not used */ + res.ep.proto = NULL; /* not used */ + if (res.changed) + res.ep.uaddr = strdup(bep->uaddr); + else + res.ep.uaddr = NULL; + return (&res); +} + +refresh_res * +nis_cache_refresh_callback_2(nis_bound_endpoint *bep, struct svc_req *rqstp) +{ + static refresh_res res; + + if (mgr_verbose) + syslog(LOG_INFO, "nis_cache_refresh_callback_2"); + /* xdr_free old result */ + xdr_free(xdr_refresh_res, (char *)&res); + res.changed = __nis_CacheMgrRefreshCallback(bep); + if (res.changed) { + res.ep.family = strdup(bep->cbep.family); + res.ep.proto = strdup(bep->cbep.proto); + res.ep.uaddr = strdup(bep->cbep.uaddr); + } else { + res.ep.family = NULL; + res.ep.proto = NULL; + res.ep.uaddr = NULL; + } + return (&res); +} diff --git a/usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.h b/usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.h new file mode 100644 index 0000000000..fd58917076 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.h @@ -0,0 +1,38 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1996, by Sun Microsystems, Inc. + * All rights reserved. + */ + + +#ifndef __CACHEMGR_H +#define __CACHEMGR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +extern int xdr_fd_result(XDR *, fd_result *); +extern int xdr_directory_obj(XDR *, directory_obj *); +extern int xdr_nis_error(XDR *, int *); +extern int xdr_nis_server(XDR *, nis_server *); + +#endif /* __CACHEMGR_H */ diff --git a/usr/src/cmd/rpcsvc/nis/cachemgr/req.flg b/usr/src/cmd/rpcsvc/nis/cachemgr/req.flg new file mode 100644 index 0000000000..b3b75227c4 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/cachemgr/req.flg @@ -0,0 +1,28 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +#pragma ident "%Z%%M% %I% %E% SMI" +# + + +echo_file usr/src/lib/libnsl/include/rpcsvc/nis_dhext.h diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/Makefile b/usr/src/cmd/rpcsvc/nis/rpc.nisd/Makefile new file mode 100644 index 0000000000..ff21cc810f --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/Makefile @@ -0,0 +1,133 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# Makefile for the nis+ daemon +# + +INIT_OBJS= nisinit.o + +LOG_OBJS= nislog.o nis_log_common.o nis_mt.o nis_cleanup.o + +NISLDAPMAPTEST_OBJS= nisldapmaptest.o + +SRV_OBJS= \ + nis_main.o nis_ns_proc.o nis_ib_proc.o nis_xx_proc.o \ + nis_db.o nis_subr_proc.o nis_log_common.o yp_ns_proc.o \ + nis_log_svc.o nis_service.o resolv_common.o resolv.o \ + ypserv1.o nis_multival.o nis_opacc.o nis_mt.o \ + nis_thread_funcs.o nis_cleanup.o ldap_log.o + +SRCS= \ + nis_main.c nis_ns_proc.c nis_ib_proc.c nis_xx_proc.c nis_db.c \ + nis_subr_proc.c nis_log_common.c yp_ns_proc.c nisinit.c \ + nis_log_svc.c nis_service.c resolv_common.c resolv.c ypserv1.c \ + nis_multival.c nis_opacc.c nis_mt.c nis_thread_funcs.c \ + nisldapmaptest.c + +OBJS= $(SRV_OBJS) $(INIT_OBJS) +PROG= nisinit rpc.nisd nislog nisldapmaptest + +# XX64 rpc.nisd nislog nisldapmaptest all require C++ +__GNUC:sh= echo \\043 +$(__GNUC)PROG= nisinit + +DERIVED_FILES= nis_svc.h +CLEANFILES += $(DERIVED_FILES) + +include $(SRC)/cmd/Makefile.cmd + +PROTOCOL_DIR= $(ROOT)/usr/include/rpcsvc +CPPFLAGS += -I$(SRC)/lib/libnsl/include -I$(SRC)/lib/libnisdb + +nislog := LDLIBS += -lnisdb +nislog := CCFLAGS += -DNEED_DIROBJ +rpc.nisd := CCFLAGS += $(CCMT) -DNEED_DIROBJ +rpc.nisd := LDLIBS += -lnisdb -lldap +nisldapmaptest := LDLIBS += -lnisdb -lldap +LDLIBS += -lnsl -lc + +SUBDIR= resolv_server configs + +.KEEP_STATE: + +all := TARGET= all +install := TARGET= install +clean := TARGET= clean +clobber := TARGET= clobber +lint := TARGET= lint + +all: $(DERIVED_FILES) $(PROG) $(SUBDIR) + +install: all $(ROOTUSRSBINPROG) $(SUBDIR) + +clean: + $(RM) $(LOG_OBJS) $(OBJS) $(CLEANFILES) + +lint: lint_SRCS + +# +# Explicit link rules for the actual programs to get around the C++ +# situation. +# +nislog: $(LOG_OBJS) + $(LINK.cc) -o $@ $(LOG_OBJS) $(LDLIBS) + $(POST_PROCESS) + +nisinit: $(INIT_OBJS) + $(LINK.c) -o $@ $(INIT_OBJS) $(LDLIBS) + $(POST_PROCESS) + +rpc.nisd: $(SRV_OBJS) + $(LINK.cc) -o $@ $(SRV_OBJS) $(LDLIBS) + $(POST_PROCESS) + +nisldapmaptest: $(NISLDAPMAPTEST_OBJS) + $(LINK.cc) -o $@ $(NISLDAPMAPTEST_OBJS) $(LDLIBS) + $(POST_PROCESS) + +nplt: nisldapmaptest + +$(SUBDIR): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +SRC_DER=nis_main.o nis_service.o nis_subr_proc.o nis_xx_proc.o nisinit.o +$(SRC_DER): nis_svc.h + +FRC: + +# +# Rules for building the derived files : +# +nis_svc.h: $(PROTOCOL_DIR)/nis.x $(PROTOCOL_DIR)/nis_object.x + $(RPCGEN) -C -h $(PROTOCOL_DIR)/nis.x |\ + $(SED) -n -e '/EDIT_START/,$$ p' |\ + $(SED) -e 's/_2_svc/_svc/' |\ + $(SED) -e 's/_3_svc/_svc/' |\ + $(SED) -e 's/_3/_clnt/' |\ + $(SED) -e 's/_2/_clnt/' >nis_svc.h + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/Makefile b/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/Makefile new file mode 100644 index 0000000000..9f49a14ed9 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/Makefile @@ -0,0 +1,61 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +#pragma ident "%Z%%M% %I% %E% SMI" +# +# Copyright (c) 2001 by Sun Microsystems, Inc. +# All rights reserved. +# +# cmd/rpcsvc/nis/rpc.nisd/configs/Makefile + +ETCDEFAULTFILES= rpc.nisd +VARNISFILES= NIS+LDAPmapping.template + +include $(SRC)/cmd/Makefile.cmd + +ROOTVARNIS= $(ROOT)/var/nis +ROOTVARNISFILES= $(VARNISFILES:%=$(ROOTVARNIS)/%) +ROOTETCDEFAULTFILES= $(ETCDEFAULTFILES:%=$(ROOTETC)/default/%) + +FILEMODE= 644 +OWNER= root +GROUP= sys + +all: FRC + +install: all $(ROOTDIRS) $(ROOTETCDEFAULTFILES) $(ROOTVARNISFILES) + +$(ROOTDIRS): + $(INS.dir) + +clean: FRC + +clobber: FRC + +lint: FRC + +$(ROOTVARNIS)/NIS+LDAPmapping.template: NIS+LDAPmapping.template + $(INS.file) NIS+LDAPmapping.template + +$(ROOTETC)/default/rpc.nisd: rpc.nisd + $(INS.file) rpc.nisd + +FRC: diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/NIS+LDAPmapping.template b/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/NIS+LDAPmapping.template new file mode 100644 index 0000000000..6613ce9300 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/NIS+LDAPmapping.template @@ -0,0 +1,996 @@ +#pragma ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# Configuration file for NIS+ to LDAP mapping, used by the rpc.nisd. +# See NIS+LDAPmapping(4) for more information; see rpc.nisd(4) for +# additional attributes controlling general rpc.nisd operation, +# LDAP server(s), LDAP authentication method, etc. + + +################################################################# +# nisplusLDAPdatabaseIdMapping +# +# The nisplusLDAPdatabaseIdMapping attribute values establish a +# connection between a NIS+ object and a database id label used +# for identification in other mapping attributes. +################################################################# + +# Standard NIS+ directories +nisplusLDAPdatabaseIdMapping basedir: +nisplusLDAPdatabaseIdMapping orgdir:org_dir +nisplusLDAPdatabaseIdMapping groupsdir:groups_dir + +# Standard NIS+ groups. +nisplusLDAPdatabaseIdMapping admin:admin.groups_dir + +# Add other NIS+ directory, group, or link objects here, if any. + +# Standard NIS+ table objects (for the table objects themselves; +# the table entries are handled in the next section). +nisplusLDAPdatabaseIdMapping passwd_table:passwd.org_dir +nisplusLDAPdatabaseIdMapping group_table:group.org_dir +nisplusLDAPdatabaseIdMapping auto_master_table:auto_master.org_dir +nisplusLDAPdatabaseIdMapping auto_home_table:auto_home.org_dir +nisplusLDAPdatabaseIdMapping bootparams_table:bootparams.org_dir +nisplusLDAPdatabaseIdMapping ethers_table:ethers.org_dir +nisplusLDAPdatabaseIdMapping hosts_table:hosts.org_dir +nisplusLDAPdatabaseIdMapping ipnodes_table:ipnodes.org_dir +nisplusLDAPdatabaseIdMapping cred_table:cred.org_dir +nisplusLDAPdatabaseIdMapping aliases_table:mail_aliases.org_dir +nisplusLDAPdatabaseIdMapping netgroup_table:netgroup.org_dir +nisplusLDAPdatabaseIdMapping networks_table:networks.org_dir +nisplusLDAPdatabaseIdMapping netmasks_table:netmasks.org_dir +nisplusLDAPdatabaseIdMapping protocols_table:protocols.org_dir +nisplusLDAPdatabaseIdMapping rpc_table:rpc.org_dir +nisplusLDAPdatabaseIdMapping services_table:services.org_dir +nisplusLDAPdatabaseIdMapping auth_attr_table:auth_attr.org_dir +nisplusLDAPdatabaseIdMapping exec_attr_table:exec_attr.org_dir +nisplusLDAPdatabaseIdMapping prof_attr_table:prof_attr.org_dir +nisplusLDAPdatabaseIdMapping user_attr_table:user_attr.org_dir +nisplusLDAPdatabaseIdMapping audit_user_table:audit_user.org_dir + +# Add other NIS+ table objects here, if any + +# Standard NIS+ table entries +nisplusLDAPdatabaseIdMapping passwd:passwd.org_dir +nisplusLDAPdatabaseIdMapping group:group.org_dir +nisplusLDAPdatabaseIdMapping auto_master:auto_master.org_dir +nisplusLDAPdatabaseIdMapping auto_home:auto_home.org_dir +nisplusLDAPdatabaseIdMapping bootparams:bootparams.org_dir +nisplusLDAPdatabaseIdMapping ethers:ethers.org_dir +nisplusLDAPdatabaseIdMapping hosts:[addr="[0-9]*.[0-9]*.[0-9]*.[0-9]*"]\ + hosts.org_dir +nisplusLDAPdatabaseIdMapping ipnodes:[addr="*:*"]ipnodes.org_dir +# The 'cred' table entries map to different containers, depending on the data. +nisplusLDAPdatabaseIdMapping credlocal:[auth_type=LOCAL]cred.org_dir +nisplusLDAPdatabaseIdMapping creduser:[auth_type="D*", \ + auth_name="unix.[0-9]*"]cred.org_dir +nisplusLDAPdatabaseIdMapping crednode:[auth_type="D*", \ + auth_name="unix.[a-z]*"]cred.org_dir +nisplusLDAPdatabaseIdMapping aliases:mail_aliases.org_dir +nisplusLDAPdatabaseIdMapping netgroup:netgroup.org_dir +nisplusLDAPdatabaseIdMapping networks:networks.org_dir +nisplusLDAPdatabaseIdMapping netmasks:netmasks.org_dir +nisplusLDAPdatabaseIdMapping protocols:protocols.org_dir +nisplusLDAPdatabaseIdMapping rpc:rpc.org_dir +nisplusLDAPdatabaseIdMapping services:services.org_dir +nisplusLDAPdatabaseIdMapping auth_attr:auth_attr.org_dir +nisplusLDAPdatabaseIdMapping exec_attr:exec_attr.org_dir +nisplusLDAPdatabaseIdMapping prof_attr:prof_attr.org_dir +nisplusLDAPdatabaseIdMapping user_attr:user_attr.org_dir +nisplusLDAPdatabaseIdMapping audit_user:audit_user.org_dir + +# Add other NIS+ table entry mappings here, if any + + +################################################################# +# nisplusLDAPentryTtl +# +# Set TTLs for mapped objects, using the database id labels from +# the nisplusLDAPdatabaseIdMapping attribute values above to +# indicate the object to which the TTLs pertain. +################################################################# + +# Standard NIS+ directories +nisplusLDAPentryTtl basedir:21600:43200:43200 +nisplusLDAPentryTtl orgdir:21600:43200:43200 +nisplusLDAPentryTtl groupsdir:21600:43200:43200 +nisplusLDAPentryTtl admin:21600:43200:43200 + +# Standard NIS+ tables +nisplusLDAPentryTtl passwd_table:21600:43200:43200 +nisplusLDAPentryTtl group_table:21600:43200:43200 +nisplusLDAPentryTtl auto_master_table:21600:43200:43200 +nisplusLDAPentryTtl auto_home_table:21600:43200:43200 +nisplusLDAPentryTtl bootparams_table:21600:43200:43200 +nisplusLDAPentryTtl ethers_table:21600:43200:43200 +nisplusLDAPentryTtl hosts_table:21600:43200:43200 +nisplusLDAPentryTtl ipnodes_table:21600:43200:43200 +nisplusLDAPentryTtl cred_table:21600:43200:43200 +nisplusLDAPentryTtl aliases_table:21600:43200:43200 +nisplusLDAPentryTtl netgroup_table:21600:43200:43200 +nisplusLDAPentryTtl networks_table:21600:43200:43200 +nisplusLDAPentryTtl netmasks_table:21600:43200:43200 +nisplusLDAPentryTtl protocols_table:21600:43200:43200 +nisplusLDAPentryTtl rpc_table:21600:43200:43200 +nisplusLDAPentryTtl services_table:21600:43200:43200 +nisplusLDAPentryTtl auth_attr_table:21600:43200:43200 +nisplusLDAPentryTtl exec_attr_table:21600:43200:43200 +nisplusLDAPentryTtl prof_attr_table:21600:43200:43200 +nisplusLDAPentryTtl user_attr_table:21600:43200:43200 +nisplusLDAPentryTtl audit_user_table:21600:43200:43200 + +# Standard NIS+ table entries +#nisplusLDAPentryTtl passwd:1800:3600:3600 +#nisplusLDAPentryTtl group:1800:3600:3600 +#nisplusLDAPentryTtl auto_master:1800:3600:3600 +#nisplusLDAPentryTtl auto_home:1800:3600:3600 +#nisplusLDAPentryTtl bootparams:1800:3600:3600 +#nisplusLDAPentryTtl ethers:1800:3600:3600 +#nisplusLDAPentryTtl hosts:1800:3600:3600 +#nisplusLDAPentryTtl ipnodes:1800:3600:3600 +#nisplusLDAPentryTtl credlocal:1800:3600:3600 +#nisplusLDAPentryTtl creduser:1800:3600:3600 +#nisplusLDAPentryTtl crednode:1800:3600:3600 +#nisplusLDAPentryTtl aliases:1800:3600:3600 +#nisplusLDAPentryTtl netgroup:1800:3600:3600 +#nisplusLDAPentryTtl networks:1800:3600:3600 +#nisplusLDAPentryTtl netmasks:1800:3600:3600 +#nisplusLDAPentryTtl protocols:1800:3600:3600 +#nisplusLDAPentryTtl rpc:1800:3600:3600 +#nisplusLDAPentryTtl services:1800:3600:3600 +#nisplusLDAPentryTtl auth_attr:1800:3600:3600 +#nisplusLDAPentryTtl exec_attr:1800:3600:3600 +#nisplusLDAPentryTtl prof_attr:1800:3600:3600 +#nisplusLDAPentryTtl user_attr:1800:3600:3600 +#nisplusLDAPentryTtl audit_user:1800:3600:3600 + + +################################################################# +# nisplusLDAPobjectDN +# +# The nisplusLDAPobjectDN attribute values establish the place in +# the LDAP directory information tree where the data for mapped +# NIS+ objects reside. The NIS+ objects are identified by the +# database id labels from the nisplusLDAPdatabaseIdMapping values +# above. +# +# Before using these attribute values, make sure that the LDAP +# server(s) recognize the RFC 2307 attributes and object classes. +# Also, the nisplusObject attribute and nisplusObjectContainer +# object class must be known to the LDAP server(s) in order to +# store NIS+ objects other than NIS+ table entries in LDAP. +# See the Getting Started section on NIS+LDAPmapping(4) for +# more information. +################################################################# + +# Standard NIS+ directories +nisplusLDAPobjectDN basedir:cn=basedir,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=basedir,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN orgdir:cn=orgdir,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=orgdir,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN groupsdir:cn=groupsdir,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=groupsdir,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN admin:cn=admin,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=admin,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top + +# Standard NIS+ tables +nisplusLDAPobjectDN passwd_table:cn=passwd,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=passwd,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN group_table:cn=group,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=group,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN auto_master_table:cn=auto_master,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=auto_master,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN auto_home_table:cn=auto_home,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=auto_home,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN bootparams_table:cn=bootparams,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=bootparams,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN ethers_table:cn=ethers,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=ethers,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN hosts_table:cn=hosts,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=hosts,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN ipnodes_table:cn=ipnodes,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=ipnodes,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN cred_table:cn=cred,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=cred,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN aliases_table:cn=aliases,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=aliases,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN netgroup_table:cn=netgroup,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=netgroup,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN networks_table:cn=networks,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=networks,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN netmasks_table:cn=netmasks,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=netmasks,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN protocols_table:cn=protocols,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=protocols,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN rpc_table:cn=rpc,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=rpc,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN services_table:cn=services,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=services,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN auth_attr_table:cn=auth_attr,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=auth_attr,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN exec_attr_table:cn=exec_attr,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=exec_attr,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN prof_attr_table:cn=prof_attr,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=prof_attr,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN user_attr_table:cn=user_attr,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=user_attr,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top +nisplusLDAPobjectDN audit_user_table:cn=audit_user,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer:\ + cn=audit_user,ou=nisPlus,?base?\ + objectClass=nisplusObjectContainer,\ + objectClass=top + + +# Standard NIS+ table entries + +# If storing table entry owner, group, access, and/or TTL in LDAP, +# the write-portion of the nisplusLDAPobjectDN needs the following +# additional object class specification: +# objectClass=nisplusEntryData + +nisplusLDAPobjectDN passwd:ou=People,?one?objectClass=shadowAccount,\ + objectClass=posixAccount:\ + ou=People,?one?objectClass=shadowAccount,\ + objectClass=posixAccount,\ + objectClass=account,objectClass=top +nisplusLDAPobjectDN group:ou=Group,?one?objectClass=posixGroup:\ + ou=Group,?one?objectClass=posixGroup,\ + objectClass=top +# +# For backward compatibility with Solaris 8 LDAP Naming Services, +# use these entries instead of using automountMapName: +# +#nisplusLDAPobjectDN auto_master:nismapname=auto_master,\ +# ?one?objectClass=nisObject:\ +# nismapname=auto_master,\ +# ?one?objectClass=nisObject,\ +# objectClass=top +#nisplusLDAPobjectDN auto_home:nismapname=auto_home,\ +# ?one?objectClass=nisObject:\ +# nismapname=auto_home,\ +# ?one?objectClass=nisObject,\ +# objectClass=top +nisplusLDAPobjectDN auto_master:automountmapname=auto_master,\ + ?one?objectClass=automount:\ + automountmapname=auto_master,\ + ?one?objectClass=automount,\ + objectClass=top +nisplusLDAPobjectDN auto_home:automountmapname=auto_home,\ + ?one?objectClass=automount:\ + automountmapname=auto_home,\ + ?one?objectClass=automount,\ + objectClass=top +nisplusLDAPobjectDN bootparams:ou=Ethers,?one?objectClass=bootableDevice,\ + bootParameter=*:\ + ou=Ethers,?one?objectClass=bootableDevice,\ + objectClass=device,\ + objectClass=top:\ + dbid=bootparams_del +nisplusLDAPobjectDN ethers:ou=Ethers,?one?objectClass=ieee802Device,\ + macAddress=*:\ + ou=Ethers,?one?objectClass=ieee802Device,\ + objectClass=device,\ + objectClass=top:\ + dbid=ethers_del +nisplusLDAPobjectDN hosts:ou=Hosts,?one?objectClass=ipHost:\ + ou=Hosts,?one?objectClass=ipHost,\ + objectClass=device,objectClass=top +nisplusLDAPobjectDN ipnodes:ou=Hosts,?one?objectClass=ipHost:\ + ou=Hosts,?one?objectClass=ipHost,\ + objectClass=device,objectClass=top +# Unless you have enabled nisplusPrincipalName in the ou=People container, +# the credlocal entries will be precisely those that have the nisKeyObject +# object class, so it's unnecessary to write credlocal entries to LDAP. +# If nisplusPrincipalName is enabled, you can add a write specification +# to the credlocal nisplusLDAPobjectDN: +#nisplusLDAPobjectDN credlocal:ou=People,?one?objectClass=nisKeyObject:\ +# ou=People,?one?objectClass=nisKeyObject,\ +# objectClass=nisplusAuthName +nisplusLDAPobjectDN credlocal:ou=People,?one?objectClass=nisKeyObject + +# If nisplusPrincipalName/nisplusNetname are enabled, the write specifications +# for 'creduser' and 'crednode' should inlcude the nisplusAuthName object +# class. Replace the write-portion with: +# ou=People,?one?objectClass=nisKeyObject,\ +# objectClass=nisplusAuthName +nisplusLDAPobjectDN creduser:ou=People,?one?objectClass=nisKeyObject:\ + ou=People,?one?objectClass=nisKeyObject +nisplusLDAPobjectDN crednode:ou=Hosts,?one?objectClass=nisKeyObject:\ + ou=Hosts,?one?objectClass=nisKeyObject +nisplusLDAPobjectDN aliases:ou=Aliases,?one?objectClass=mailGroup:\ + ou=Aliases,?one?objectClass=mailGroup,\ + objectClass=top +nisplusLDAPobjectDN netgroup:ou=Netgroup,?one?objectClass=nisNetgroup:\ + ou=Netgroup,?one?objectClass=nisNetgroup,\ + objectClass=top +nisplusLDAPobjectDN networks:ou=Networks,?one?objectClass=ipNetwork:\ + ou=Networks,?one?objectClass=ipNetwork,\ + objectClass=top +nisplusLDAPobjectDN netmasks:ou=Networks,?one?objectClass=ipNetwork,\ + ipNetMaskNumber=*:\ + ou=Networks,?one?objectClass=ipNetwork:\ + dbid=netmasks_del +nisplusLDAPobjectDN protocols:ou=Protocols,?one?objectClass=ipProtocol:\ + ou=Protocols,?one?objectClass=ipProtocol,\ + objectClass=top +nisplusLDAPobjectDN rpc:ou=Rpc,?one?objectClass=oncRpc:\ + ou=Rpc,?one?objectClass=oncRpc,objectClass=top +nisplusLDAPobjectDN services:ou=Services,?one?objectClass=ipService:\ + ou=Services,?one?objectClass=ipService,\ + objectClass=top +nisplusLDAPobjectDN auth_attr:\ + ou=SolarisAuthAttr,?one?objectClass=SolarisAuthAttr:\ + ou=SolarisAuthAttr,?one?objectClass=SolarisAuthAttr,\ + objectClass=top +nisplusLDAPobjectDN exec_attr:\ + ou=SolarisProfAttr,?one?objectClass=SolarisExecAttr,\ + SolarisKernelSecurityPolicy=*:\ + ou=SolarisProfAttr,?one?objectClass=SolarisExecAttr,\ + objectClass=SolarisProfAttr,\ + objectClass=top +nisplusLDAPobjectDN prof_attr:\ + ou=SolarisProfAttr,?one?objectClass=SolarisProfAttr,\ + SolarisAttrLongDesc=*:\ + ou=SolarisProfAttr,?one?objectClass=SolarisProfAttr,\ + objectClass=SolarisExecAttr,\ + objectClass=top +nisplusLDAPobjectDN user_attr:ou=People,?one?objectClass=SolarisUserAttr,\ + solarisAttrKeyValue=*:\ + ou=People,?one?objectClass=SolarisUserAttr:\ + dbid=user_attr_del +nisplusLDAPobjectDN audit_user:ou=People,?one?objectClass=SolarisAuditUser,\ + SolarisAuditAlways=*,\ + SolarisAuditNever=*:\ + ou=People,?one?objectClass=SolarisAuditUser:\ + dbid=audit_user_del + + +################################################################# +# nisplusLDAPattributeFromColumn +# +# The nisplusLDAPattributeFromColumn values establish the mapping +# of LDAP container entries to NIS+ table columns. +# +# Before using, make sure that the LDAP server(s) understand the +# RFC 2307 attributes and object classes. +# See the Getting Started section on NIS+LDAPmapping(4) for +# more information. +################################################################# + +# Standard NIS+ table entries +nisplusLDAPattributeFromColumn \ + passwd: dn=("uid=%s,", name), \ + cn=name, \ + uid=name, \ + userPassword=("{crypt}%s", passwd), \ + uidNumber=uid, \ + gidNumber=gid, \ + gecos=gcos, \ + homeDirectory=home, \ + loginShell=shell, \ + (shadowLastChange,shadowMin,shadowMax, \ + shadowWarning, shadowInactive,shadowExpire)=\ + (shadow, ":") +# If owner/group/access information for NIS+ passwd entries should be +# stored in LDAP, make sure the LDAP server knows about the nisplusEntryData +# object class, add it to the write portion of the nisplusLDAPobjectDN for +# passwd, and enable the lines below. +# nisplusEntryOwner=zo_owner, \ +# nisplusEntryGroup=zo_group, \ +# nisplusEntryAccess=zo_access +nisplusLDAPattributeFromColumn \ + group: dn=("cn=%s,", name), \ + cn=name, \ + userPassword=("{crypt}%s", passwd), \ + gidNumber=gid, \ + (memberUid)=(members, ",") +# +# For backward compatibility with Solaris 8 LDAP Naming Services, +# use these entries instead of using automountMapName: +# +#nisplusLDAPattributeFromColumn \ +# auto_master: dn=("cn=%s,", key), \ +# cn=key, \ +# nismapentry=value, \ +# nismapname=("auto_master") +#nisplusLDAPattributeFromColumn \ +# auto_home: dn=("cn=%s,", key), \ +# cn=key, \ +# nismapentry=value, \ +# nismapname=("auto_home") +nisplusLDAPattributeFromColumn \ + auto_master: dn=("automountKey=%s,", key), \ + automountKey=key, \ + automountInformation=value +nisplusLDAPattributeFromColumn \ + auto_home: dn=("automountKey=%s,", key), \ + automountKey=key, \ + automountInformation=value +nisplusLDAPattributeFromColumn \ + bootparams: dn=("cn=%s,", key), \ + cn=key, \ + (bootParameter)=(value, " ") +nisplusLDAPattributeFromColumn \ + bootparams_del: dn=("cn=%s,", key), \ + bootParameter= +nisplusLDAPattributeFromColumn \ + ethers: dn=("cn=%s,", name), \ + macAddress=addr, \ + cn=name +nisplusLDAPattributeFromColumn \ + ethers_del: dn=("cn=%s,", name), \ + macAddress= +nisplusLDAPattributeFromColumn \ + hosts: dn=("cn=%s+ipHostNumber=%s,", cname, addr), \ + cn=cname, \ + cn=name, \ + ipHostNumber=addr, \ + description=comment +nisplusLDAPattributeFromColumn \ + ipnodes: dn=("cn=%s+ipHostNumber=%s,", cname, addr), \ + cn=cname, \ + cn=name, \ + ipHostNumber=addr, \ + description=comment + +# The cred* entries below only support principals in the local domain. In +# order to allow out-of-domain principals, read the Getting Started section +# on NIS+LDAPmapping(4) for information concerning the 'nisplusPrincipalName' +# and 'nisplusNetname' attributes. Once these have been configured for your +# LDAP server, you can replace the cred* entries with the commented ones below. +#nisplusLDAPattributeFromColumn \ +# credlocal: dn=("uid=%s,", (cname, "%s.*")), \ +# nisplusPrincipalName=cname, \ +# uidNumber=auth_name, \ +# gidNumber=public_data, \ +# nisplusEntryOwner=zo_owner, \ +# nisplusEntryGroup=zo_group, \ +# nisplusEntryAccess=zo_access +#nisplusLDAPattributeFromColumn \ +# creduser: dn=("uid=%s,", (cname, "%s.*")), \ +# nisplusPrincipalName=cname, \ +# nisplusNetname=auth_name, \ +# nisPublicKey=("{%s}%s", \ +# auth_type, public_data), \ +# nisSecretKey=("{%s}%s", \ +# auth_type, private_data), \ +# nisplusEntryOwner=zo_owner, \ +# nisplusEntryGroup=zo_group, \ +# nisplusEntryAccess=zo_access +## The 'dn' setting for 'crednode' requires that there's an ipHostNumber +## available when the credentials are stored. +#nisplusLDAPattributeFromColumn \ +# crednode: dn=("cn=%s+ipHostNumber=%s,", \ +# (cname, "%s.*"), \ +# ldap:ipHostNumber:?one?("cn=%s", (cname, "%s.*"))), \ +# nisplusPrincipalName=cname, \ +# nisplusNetname=auth_name, \ +# nisPublicKey=("{%s}%s", \ +# auth_type, public_data), \ +# nisSecretKey=("{%s}%s", \ +# auth_type, private_data), \ +# nisplusEntryOwner=zo_owner, \ +# nisplusEntryGroup=zo_group, \ +# nisplusEntryAccess=zo_access + +nisplusLDAPattributeFromColumn \ + credlocal: dn=("uid=%s,", (cname, "%s.*")), \ + uidNumber=auth_name, \ + gidNumber=public_data +# If owner/group/access information for NIS+ cred LOCAL entries should be +# stored in LDAP, make sure the LDAP server knows about the nisplusEntryData +# object class, add it to the write portion of the nisplusLDAPobjectDN for +# credlocal, and enable the lines below. +# nisplusEntryOwner=zo_owner, \ +# nisplusEntryGroup=zo_group, \ +# nisplusEntryAccess=zo_access +nisplusLDAPattributeFromColumn \ + creduser: dn=("uid=%s,", (cname, "%s.*")), \ + nisPublicKey=("{%s}%s", \ + auth_type, public_data), \ + nisSecretKey=("{%s}%s", \ + auth_type, private_data) +# If owner/group/access information for NIS+ cred user entries should be +# stored in LDAP, make sure the LDAP server knows about the nisplusEntryData +# object class, add it to the write portion of the nisplusLDAPobjectDN for +# creduser, and enable the lines below. +# nisplusEntryOwner=zo_owner, \ +# nisplusEntryGroup=zo_group, \ +# nisplusEntryAccess=zo_access + +# The 'dn' setting for 'crednode' requires that there's an ipHostNumber +# available when the credentials are stored. +nisplusLDAPattributeFromColumn \ + crednode: dn=("cn=%s+ipHostNumber=%s,", \ + (cname, "%s.*"), \ + ldap:ipHostNumber:?one?("cn=%s", (cname, "%s.*"))), \ + nisPublicKey=("{%s}%s", \ + auth_type, public_data), \ + nisSecretKey=("{%s}%s", \ + auth_type, private_data) +# If owner/group/access information for NIS+ cred node entries should be +# stored in LDAP, make sure the LDAP server knows about the nisplusEntryData +# object class, add it to the write portion of the nisplusLDAPobjectDN for +# crednode, and enable the lines below. +# nisplusEntryOwner=zo_owner, \ +# nisplusEntryGroup=zo_group, \ +# nisplusEntryAccess=zo_access + +nisplusLDAPattributeFromColumn \ + aliases: dn=("mail=%s,", alias), \ + cn=alias, \ + mail=alias, \ + (mgrprfc822mailmember)= (expansion, ",") +nisplusLDAPattributeFromColumn \ + netgroup: dn=("cn=%s,", name), \ + cn=name, \ + memberNisNetgroup=group, \ + nisNetgroupTriple=("(%s,%s,%s)", \ + host, user, domain), \ + description=comment +nisplusLDAPattributeFromColumn \ + networks: dn=("ipNetworkNumber=%s,", addr), \ + cn=cname, \ + cn=name, \ + ipNetworkNumber=addr, \ + description=comment +nisplusLDAPattributeFromColumn \ + netmasks: dn=("ipNetworkNumber=%s,", addr), \ + ipNetworkNumber=addr, \ + ipNetmaskNumber=mask, \ + description=comment +# Since netmasks share the ou=Networks container with networks, the default +# is to delete just the ipNetmaskNumber attribute. +nisplusLDAPattributeFromColumn \ + netmasks_del: dn=("ipNetworkNumber=%s,", addr), \ + ipNetmaskNumber= +nisplusLDAPattributeFromColumn \ + protocols: dn=("cn=%s,", cname), \ + cn=cname, \ + cn=name, \ + ipProtocolNumber=number, \ + description=comment +nisplusLDAPattributeFromColumn \ + rpc: dn=("cn=%s,", cname), \ + cn=cname, \ + cn=name, \ + oncRpcNumber=number, \ + description=comment +nisplusLDAPattributeFromColumn \ + services: dn=("cn=%s+ipServiceProtocol=%s,",\ + cname, proto), \ + cn=cname, \ + cn=name, \ + ipServiceProtocol=proto, \ + ipServicePort=port, \ + description=comment +nisplusLDAPattributeFromColumn \ + auth_attr: dn=("cn=%s,", name), \ + cn=name, \ + SolarisAttrReserved1=res1, \ + SolarisAttrReserved2=res2, \ + SolarisAttrShortDesc=short_desc, \ + SolarisAttrLongDesc=long_desc, \ + SolarisAttrKeyValue=attr +nisplusLDAPattributeFromColumn \ + exec_attr: dn=("cn=%s+SolarisKernelSecurityPolicy=%s+SolarisProfileType=%s+SolarisProfileId=%s,", name, policy, type, id), \ + cn=name, \ + SolarisKernelSecurityPolicy=policy, \ + SolarisProfileType=type, \ + SolarisAttrReserved1=res1, \ + SolarisAttrReserved2=res2, \ + SolarisProfileId=id, \ + SolarisAttrKeyValue=attr +nisplusLDAPattributeFromColumn \ + prof_attr: dn=("cn=%s,", name), \ + cn=name, \ + SolarisAttrReserved1=res1, \ + SolarisAttrReserved2=res2, \ + SolarisAttrLongDesc=desc, \ + SolarisAttrKeyValue=attr +nisplusLDAPattributeFromColumn \ + user_attr: dn=("uid=%s,", name), \ + SolarisUserQualifier=qualifier, \ + SolarisAttrReserved1=res1, \ + SolarisAttrReserved2=res2, \ + SolarisAttrKeyValue=attr +nisplusLDAPattributeFromColumn \ + audit_user: dn=("uid=%s,", name), \ + SolarisAuditAlways=always, \ + SolarisAuditNever=never +# Since user_attr and audit_user share the ou=People container, only delete +# the user_attr/audit_user attributes proper. +nisplusLDAPattributeFromColumn \ + user_attr_del: dn=("uid=%s,", name), \ + SolarisUserQualifier=, \ + SolarisAttrReserved1=, \ + SolarisAttrReserved2=, \ + SolarisAttrKeyValue= +nisplusLDAPattributeFromColumn \ + audit_user_del: dn=("uid=%s,", name), \ + SolarisAuditAlways=, \ + SolarisAuditNever= + + +################################################################# +# nisplusLDAPcolumnFromAttribute +# +# The nisplusLDAPcolumnFromAttribute values establish the mapping +# from NIS+ table columns to LDAP container entries. +# +# Before using, make sure that the LDAP server(s) understand the +# RFC 2307 attributes and object classes. +# See the Getting Started section on NIS+LDAPmapping(4) for +# more information. +################################################################# + +# Standard NIS+ table entries +nisplusLDAPcolumnFromAttribute \ + passwd: name=uid, \ + ("{crypt}%s", passwd)=userPassword, \ + uid=uidNumber, \ + gid=gidNumber, \ + gcos=gecos, \ + home=homeDirectory, \ + shell=loginShell, \ + shadow=("%s:%s:%s:%s:%s:%s", \ + shadowLastChange, \ + shadowMin, \ + shadowMax, \ + shadowWarning, \ + shadowInactive, \ + shadowExpire) +# If owner/group/access information for NIS+ passwd entries should be +# retrieved from LDAP, make sure the LDAP server knows about the +# nisplusEntryData object class, and enable the lines below. +# zo_owner=nisplusEntryOwner, \ +# zo_group=nisplusEntryGroup, \ +# zo_access=nisplusEntryAccess +nisplusLDAPcolumnFromAttribute \ + group: name=cn, \ + ("{crypt}%s", passwd)=userPassword, \ + gid=gidNumber, \ + members=("%s,", (memberUid), ",") +# +# For backward compatibility with Solaris 8 LDAP Naming Services, +# use these entries instead of using automountMapName: +# +#nisplusLDAPcolumnFromAttribute \ +# auto_master: key=cn, \ +# value=nismapentry +#nisplusLDAPcolumnFromAttribute \ +# auto_home: key=cn, \ +# value=nismapentry +nisplusLDAPcolumnFromAttribute \ + auto_master: key=automountKey, \ + value=automountInformation +nisplusLDAPcolumnFromAttribute \ + auto_home: key=automountKey, \ + value=automountInformation +nisplusLDAPcolumnFromAttribute \ + bootparams: key=cn, \ + value=("%s ", (bootParameter), " ") +nisplusLDAPcolumnFromAttribute \ + ethers: addr=macAddress, \ + name=cn +nisplusLDAPcolumnFromAttribute \ + hosts: cname=cn, \ + (name)=(cn), \ + addr=ipHostNumber, \ + comment=description +nisplusLDAPcolumnFromAttribute \ + ipnodes: cname=cn, \ + (name)=(cn), \ + addr=ipHostNumber, \ + comment=description + +# The cred* entries below only support principals in the local domain. In +# order to allow out-of-domain principals, read the Getting Started section +# on NIS+LDAPmapping(4) for information concerning the 'nisplusPrincipalName' +# and 'nisplusNetname' attributes. Once these have been configured for your +# LDAP server, you can replace the cred* entries with the commented ones below. +#nisplusLDAPcolumnFromAttribute \ +# credlocal: cname=nisplusPrincipalName=cname, \ +# auth_type=("LOCAL"), \ +# auth_name=uidNumber, \ +# public_data=gidNumber, \ +# zo_owner=nisplusEntryOwner, \ +# zo_group=nisplusEntryGroup, \ +# zo_access=nisplusEntryAccess +#nisplusLDAPcolumnFromAttribute \ +# creduser: cname=nisplusPrincipalName, \ +# auth_name=nisplusNetname, \ +# ("{%s}%s", auth_type, public_data)= \ +# nisPublicKey, \ +# ("{%s}%s", auth_type, private_data)= \ +# nisSecretKey, \ +# zo_owner=nisplusEntryOwner, \ +# zo_group=nisplusEntryGroup, \ +# zo_access=nisplusEntryAccess +#nisplusLDAPcolumnFromAttribute \ +# crednode: \ +# cname=nisplusPrincipalName, \ +# auth_name=nisplusNetname, \ +# ("{%s}%s", auth_type, public_data)= \ +# nisPublicKey, \ +# ("{%s}%s", auth_type, private_data)= \ +# nisSecretKey, \ +# zo_owner=nisplusEntryOwner, \ +# zo_group=nisplusEntryGroup, \ +# zo_access=nisplusEntryAccess + +nisplusLDAPcolumnFromAttribute \ + credlocal: cname=("%s.%s", uid, \ + (nis+:zo_owner[]cred.org_dir, "*.%s")), \ + auth_type=("LOCAL"), \ + auth_name=uidNumber, \ + public_data=gidNumber +# Enable if owner/group/access information is stored in LDAP, +# and the LDAP server knows about the nisplusEntryData object class. +# zo_owner=nisplusEntryOwner, \ +# zo_group=nisplusEntryGroup, \ +# zo_access=nisplusEntryAccess +nisplusLDAPcolumnFromAttribute \ + creduser: cname=("%s.%s", uid, \ + (nis+:zo_owner[]cred.org_dir, "*.%s")), \ + auth_name=("unix.%s@%s", uidNumber, \ + (nis+:zo_owner[]cred.org_dir, "*.%s.")), \ + ("{%s}%s", auth_type, public_data)= \ + nisPublicKey, \ + ("{%s}%s", auth_type, private_data)= \ + nisSecretKey +# Enable if owner/group/access information is stored in LDAP, +# and the LDAP server knows about the nisplusEntryData object class. +# zo_owner=nisplusEntryOwner, \ +# zo_group=nisplusEntryGroup, \ +# zo_access=nisplusEntryAccess + +# If your Hosts container store fully qualified host names, the 'cname' +# and 'auth_name' assignments below should be replaced with: +# cname=("%s.", cn), \ +# auth_name=("unix.%s@%s", (cn, "%s.*"), \ +# (cn, "*.%s.")) \ +# +nisplusLDAPcolumnFromAttribute \ + crednode: \ + cname=("%s.%s", cn, \ + (nis+:zo_owner[]cred.org_dir, "*.%s")), \ + auth_name=("unix.%s@%s", cn, \ + (nis+:zo_owner[]cred.org_dir, "*.%s.")), \ + ("{%s}%s", auth_type, public_data)= \ + nisPublicKey, \ + ("{%s}%s", auth_type, private_data)= \ + nisSecretKey +# Enable if owner/group/access information is stored in LDAP, +# and the LDAP server knows about the nisplusEntryData object class. +# zo_owner=nisplusEntryOwner, \ +# zo_group=nisplusEntryGroup, \ +# zo_access=nisplusEntryAccess +nisplusLDAPcolumnFromAttribute \ + aliases: alias=mail, \ + expansion= \ + ("%s,", (mgrprfc822mailmember), ",") +nisplusLDAPcolumnFromAttribute \ + netgroup: name=cn, \ + (group)=(memberNisNetgroup), \ + ("(%s,%s,%s)", host, user, domain)= \ + (nisNetgroupTriple), \ + comment=description +nisplusLDAPcolumnFromAttribute \ + networks: cname=cn, \ + (name)=(cn), \ + addr=ipNetworkNumber, \ + comment=description +nisplusLDAPcolumnFromAttribute \ + netmasks: addr=ipNetworkNumber, \ + mask=ipNetmaskNumber, \ + comment=description +nisplusLDAPcolumnFromAttribute \ + protocols: cname=cn, \ + (name)=(cn), \ + number=ipProtocolNumber, \ + comment=description +nisplusLDAPcolumnFromAttribute \ + rpc: cname=cn, \ + (name)=(cn), \ + number=oncRpcNumber, \ + comment=description +nisplusLDAPcolumnFromAttribute \ + services: cname=cn, \ + (name)=(cn), \ + proto=ipServiceProtocol, \ + port=ipServicePort, \ + comment=description +nisplusLDAPcolumnFromAttribute \ + auth_attr: name=cn, \ + res1=SolarisAttrReserved1, \ + res2=SolarisAttrReserved2, \ + short_desc=SolarisAttrShortDesc, \ + long_desc=SolarisAttrLongDesc, \ + attr=SolarisAttrKeyValue +nisplusLDAPcolumnFromAttribute \ + exec_attr: name=cn, \ + policy=SolarisKernelSecurityPolicy, \ + type=SolarisProfileType, \ + res1=SolarisAttrReserved1, \ + res2=SolarisAttrReserved2, \ + id=SolarisProfileId, \ + attr=SolarisAttrKeyValue +nisplusLDAPcolumnFromAttribute \ + prof_attr: name=cn, \ + res1=SolarisAttrReserved1, \ + res2=SolarisAttrReserved2, \ + desc=SolarisAttrLongDesc, \ + attr=SolarisAttrKeyValue +nisplusLDAPcolumnFromAttribute \ + user_attr: name=cn, \ + qualifier=SolarisUserQualifier, \ + res1=SolarisAttrReserved1, \ + res2=SolarisAttrReserved2, \ + attr=SolarisAttrKeyValue +nisplusLDAPcolumnFromAttribute \ + audit_user: name=cn, \ + always=SolarisAuditAlways, \ + never=SolarisAuditNever + + +################################################################# +# timezone and client_info +# +# The standard NIS+ "timezone" and "client_info" tables require +# definitions of object classes and attributes other than the +# RFC 2307 ones. See the Getting Started section on the +# NIS+LDAPmapping(4) man page before enabling the configuration +# below. +################################################################# + +#nisplusLDAPdatabaseIdMapping client_info_table:client_info.org_dir +#nisplusLDAPdatabaseIdMapping timezone_table:timezone.org_dir + +#nisplusLDAPdatabaseIdMapping client_info:client_info.org_dir +#nisplusLDAPdatabaseIdMapping timezone:timezone.org_dir + +#nisplusLDAPentryTtl client_info_table:21600:43200:43200 +#nisplusLDAPentryTtl timezone_table:21600:43200:43200 + +#nisplusLDAPentryTtl client_info:1800:3600:3600 +#nisplusLDAPentryTtl timezone:1800:3600:3600 + +#nisplusLDAPobjectDN client_info_table:cn=client_info,ou=nisPlus,?base?\ +# objectClass=nisplusObjectContainer:\ +# cn=client_info,ou=nisPlus,?base?\ +# objectClass=nisplusObjectContainer,\ +# objectClass=top +#nisplusLDAPobjectDN timezone_table:cn=timezone,ou=nisPlus,?base?\ +# objectClass=nisplusObjectContainer:\ +# cn=timezone,ou=nisPlus,?base?\ +# objectClass=nisplusObjectContainer,\ +# objectClass=top + +#nisplusLDAPobjectDN client_info:ou=ClientInfo,?one?\ +# objectClass=nisplusClientInfoData:\ +# ou=ClientInfo,?one?\ +# objectClass=nisplusClientInfoData,\ +# objectClass=top +#nisplusLDAPobjectDN timezone:ou=TimeZone,?one?\ +# objectClass=nisplusTimeZoneData:\ +# ou=TimeZone,?one?\ +# objectClass=nisplusTimeZoneData,\ +# objectClass=top + +#nisplusLDAPattributeFromColumn \ +# client_info: dn=("cn=%s,", client), \ +# cn=client, \ +# nisplusClientInfoAttr=attr, \ +# nisplusClientInfoInfo=info, \ +# nisplusClientInfoFlags=flags +#nisplusLDAPattributeFromColumn \ +# timezone: dn=("cn=%s,", name), \ +# cn=name, \ +# nisplusTimeZone=tzone, \ +# description=comment + +#nisplusLDAPcolumnFromAttribute \ +# client_info: client=cn, \ +# attr=nisplusClientInfoAttr, \ +# info=nisplusClientInfoInfo, \ +# flags=nisplusClientInfoFlags +#nisplusLDAPcolumnFromAttribute \ +# timezone: name=cn, \ +# tzone=nisplusTimeZone, \ +# comment=description diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/rpc.nisd b/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/rpc.nisd new file mode 100644 index 0000000000..7bd0c1f184 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/rpc.nisd @@ -0,0 +1,233 @@ +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# Configuration file for rpc.nisd(1M); see rpc.nisd(4) for more information, +# and NIS+LDAPmapping(4) for configuration of NIS+ to LDAP mapping. + +# Unless otherwise noted, commented lines show default values. + + +# The number of RPC service threads. Zero selects three plus +# the number of CPUs available when the rpc.nisd starts. +# +#nisplusNumberOfServiceThreads=0 + +# The maximum record size that RPC can use over connection oriented +# transports. Values below 9000 are invalid and will result in the +# default being used. +# +#nisplusMaxRPCRecordSize=9000 + +# The action to take when thread creation failed. Does not apply to +# RPC service threads. +# +#nisplusThreadCreationErrorAction=pass_error + +# Number of times to retry a failed thread creation. The default when +# nisplusThreadCreationErrorAttempts has no value is unlimited +# +#nisplusThreadCreationErrorAttempts= + +# The time to wait (seconds) between each attempt to create a thread. +# +#nisplusThreadCreationErrorTimeout=15 + + +# The action to take when a full dump of a NIS+ directory fails. +# +#nisplusDumpErrorAction=retry + +# Number of times to retry a failed full dump. The default when +# nisplusDumpErrorAttempts has no value is unlimited. +# +#nisplusDumpErrorAttempts= + +# The time to wait (seconds) bewteen each failed full dump. +# +#nisplusDumpErrorTimeout=120 + + +# The type of NIS+ service provided by a replica during a resync. +# +#nisplusResyncService=from_copy + + +# How updates are batched on a master server before pinging replicas +# +#nisplusUpdateBatching=accumulate + +# The minimum length of time (seconds) during which updates are batched. +# +#nisplusUpdateBatchingTimeout=120 + + +# Where to look for configuration information in LDAP. Leave empty or undefined +# to use this file, in which case the values of the other 'nisplusLdapConfig*' +# attributes are ignored. +# +#nisplusLDAPconfigDN= + + +# Server(s) for configuration information. There is no default; use the +# value on the line below for an LDAP server running on this machine, at +# port 389. +# +#nisplusLDAPconfigPreferredServerList=127.0.0.1:389 + + +# Authentication method(s) to obtain configuration information. +# +#nisplusLDAPconfigAuthenticationMethod=none + + +# Transport layer security for configuration information +# +#nisplusLDAPconfigTLS=none + + +# Certificate DB for transport layer security +# +#nisplusLDAPconfigTLSCertificateDBPath=/var/nis/cert7.db + + +# Proxy user(s) to obtain configuration information. The line below +# is an example of the format. +# +#nisplusLDAPconfigProxyUser=cn=nisplusAdmin,ou=People, + + +# Password for proxy user. Must be supplied if the authentication method +# requires a password. If a password appears in this file, it should be +# protected appropriately agains access by unauthorized users. +# +#nisplusLDAPconfigProxyPassword= + + +# Server list for mapping data to/from LDAP. There is no default; use the +# value on the line below for an LDAP server running on this machine, at +# port 389. +# +preferredServerList=127.0.0.1:389 + + +# Authentication method for mapping data to/from LDAP +# +authenticationMethod=none + + +# Starting point for LDAP data. Mapping entries can override this value. +# The default is derived from the NIS+ domain name (baseDomain). +# +#defaultSearchBase= + + +# NIS+ domain name to use for mapping data to/from LDAP. The default is +# the rpc.nisd's notion of the domain it serves. +# +#nisplusLDAPbaseDomain= + + +# Transport layer security for mapping data to/from LDAP. +# +nisplusLDAPTLS=none + + +# Certificate DB for transport layer security +# +#nisplusLDAPTLSCertificateDBPath=/var/nis/cert7.db + + +# Proxy user for rpc.nisd. Assumed to have appropriate permission to read +# and/or create or modify LDAP data. The line below is an example of the +# format. +# +#nisplusLDAPproxyUser=cn=nisplusAdmin,ou=People, + + +# Password for proxy user. Must be supplied if the authentication method +# requires a password. If a password appears in this file, it should be +# protected appropriately against unauthorized access. +# +#nisplusLDAPproxyPassword= + + +# Timeouts and time/size limits for LDAP operations. +# +#nisplusLDAPbindTimeout=15 +#nisplusLDAPsearchTimeout=15 +#nisplusLDAPmodifyTimeout=15 +#nisplusLDAPaddTimeout=15 +#nisplusLDAPdeleteTimeout=15 +#nisplusLDAPsearchTimeLimit=15 +#nisplusLDAPsearchSizeLimit=0 + + +# Should the rpc.nisd follow LDAP referrals ? +# +#nisplusLDAPfollowReferral=no + + +# Up- or down-load NIS+ data to or from LDAP when starting +# +#nisplusLDAPinitialUpdateAction=none + + +# Should the rpc.nisd exit following an initial update +# +#nisplusLDAPinitialUpdateOnly=no + + +# Action, number of attempts, and timeout following an LDAP retrieval error +# +#nisplusLDAPretrieveErrorAction=use_cached +#nisplusLDAPretrieveErrorAttempts= +#nisplusLDAPretrieveErrorTimeout=15 + + +# Action, number of attempts, and timeout following an LDAP store error +# +#nisplusLDAPstoreErrorAction=retry +#nisplusLDAPstoreErrorAttempts= +#nisplusLDAPstoreErrorTimeout=15 + + +# Action, number of attempts, and timeout following an LDAP refresh error +# +#nisplusLDAPrefreshErrorAction=continue_using +#nisplusLDAPrefreshErrorAttempts= +#nisplusLDAPrefreshErrorTimeout=15 + + +# When to fetch LDAP data for NIS+ match operations +# +#nisplusLDAPmatchFetchAction=no_match_only + + +# ENABLE_NIS_YP_EMULATION affects whether rpc.nisd is started in in NIS +# (YP) compatibility mode, see rpc.nisd(1M) for details. The default +# value is "NO". A value of "YES" (any case) results in rpc.nisd being +# started in NIS (YP) compatibility mode; any other value is ignored. +# +#ENABLE_NIS_YP_EMULATION=NO diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/ldap_log.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/ldap_log.c new file mode 100644 index 0000000000..9dabb03339 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/ldap_log.c @@ -0,0 +1,146 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <time.h> +#include <rpcsvc/nis.h> +#include "nis_proc.h" +#include <nis_servlist.h> +#include <ldap_util.h> + +int +beginTransaction(void) { + return (begin_transaction(nis_local_principal())); +} + +int +endTransaction(int xid, nis_object *dirObj) { + int ret, master; + + /* Are we the master for the directory ? */ + if (dirObj != 0 && dirObj->zo_data.zo_type == NIS_DIRECTORY_OBJ && + dirObj->DI_data.do_servers.do_servers_len > 1 && + nis_isserving(dirObj) == 1) { + master = 1; + } else { + master = 0; + } + + /* + * End the trans.log operation. If we're the master, we also + * modify the update time cache, otherwise not. + */ + ret = end_transaction_x(xid, master); + + /* + * If we're the master, and there are replicas, make a note to + * ping them. + */ + if (master) { + ulong_t ttime = last_update(dirObj->DI_data.do_name); + + add_pingitem(dirObj, ttime, &ping_list); + } + + return (ret); +} + +extern void flush_local_dircache(nis_name name); + +int +addUpdate(log_entry_t type, char *name, int numAttr, nis_attr *attr, + nis_object *obj, nis_object *oldDir, uint32_t ttime) { + log_entry le; + __nis_buffer_t b = {0, 0}; + char *myself = "addUpdate"; + + if (name == 0 || obj == 0) + return (-1); + + /* Supply current time for log entry, if the caller specified zero */ + if (ttime == 0) + ttime = time(0); + + le.le_time = ttime; + le.le_type = type; + le.le_princp = nis_local_principal(); + le.le_name = name; + le.le_attrs.le_attrs_val = attr; + le.le_attrs.le_attrs_len = numAttr; + le.le_object = *obj; + + add_update(&le); + + /* Flush the relevant caches */ + switch (obj->zo_data.zo_type) { + case NIS_DIRECTORY_OBJ: + /* + * Since flush_dircache() looks for a new version in the + * directory cache (assuming it's been flushed by the update, + * which doesn't happen in our case), we first need to flush + * the rpc.nisd dir cache. + */ + flush_local_dircache(obj->DI_data.do_name); + flush_dircache(obj->DI_data.do_name, &obj->DI_data); + /* + * If an old (pre-mod) directory object was supplied, and + * we were the master for that object, tell all replicas + * (of the old incarnation) to flush it from their caches. + */ + if (oldDir != 0 && nis_isserving(oldDir) == 1) { + nis_taglist taglist; + nis_tag tags; + + tags.tag_type = TAG_DCACHE_ONE_REFRESH; + tags.tag_val = oldDir->DI_data.do_name; + taglist.tags.tags_len = 1; + taglist.tags.tags_val = &tags; + (void) nis_mcast_tags(&oldDir->DI_data, &taglist); + } + break; + case NIS_TABLE_OBJ: + bp2buf(myself, &b, "%s.%s", obj->zo_name, obj->zo_domain); + if (b.len > 0) { + flush_tablecache(b.buf); + free(b.buf); + } + break; + case NIS_GROUP_OBJ: + bp2buf(myself, &b, "%s.%s", obj->zo_name, obj->zo_domain); + if (b.len > 0) { + flush_groupcache(b.buf); + free(b.buf); + } + default: + break; + } + + multival_invalidate(obj); + + return (0); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/log.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/log.h new file mode 100644 index 0000000000..e89e427f9f --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/log.h @@ -0,0 +1,154 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1992-2001 by Sun Microsystems, Inc. + * All rights reserved. + * + * Definitions for this server's log implementation. This is server + * dependent and may change from implementation to implementation. + */ + +#ifndef _RPC_NISD_LOG_H +#define _RPC_NISD_LOG_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LOG_FILE "/var/nis/trans.log" + +/* + * old_stamp_item is used to keep track of the timestamps for all the + * directories that have been removed from the transaction log. + */ + +struct old_stamp_item { + NIS_HASH_ITEM item; + u_long utime; + int stamped; + u_long xid; +}; +typedef struct old_stamp_item old_stamp_item; + + +struct stamp_item { + NIS_HASH_ITEM item; + u_long utime; +}; +typedef struct stamp_item stamp_item; + +/* + * The update log is mapped into memory from the file "trans.log". + * This mapping is accomplished by the nis_main function when it starts. + * When the directory checkpoints, it may truncate the log by making a + * copy of it. + */ +struct log_upd { + u_long lu_magic; /* Update magic number */ + u_long lu_xid; /* Update transaction ID */ + u_long lu_time; /* Time of this update */ + nis_name lu_dirname; /* Directory of this update */ + struct log_upd *lu_next; /* pointer to next update */ + struct log_upd *lu_prev; /* pointer to previous update */ + u_long lu_size; /* size of the data buffer */ + u_char lu_data[4]; /* pointer to XDR'd log_entry */ +}; +typedef struct log_upd log_upd; + +struct log_hdr { + u_long lh_magic; /* Log header magic number */ + u_long lh_state; /* Updating/Stable/Checkpointing */ + struct log_hdr *lh_addr; /* Current address */ + u_long lh_num; /* Number of updates in the log */ + u_long lh_xid; /* Last stable transaction */ + u_long lh_first; /* Timestamp of earliest update */ + u_long lh_last; /* Timestamp of last update */ + log_upd *lh_head; /* pointer to the "first" update */ + log_upd *lh_tail; /* pointer to the "last" update */ +}; +typedef struct log_hdr log_hdr; + +#define LOG_HDR_MAGIC 0x5551212 /* It is a directory service! */ +#define LOG_UPD_MAGIC 0x5552223 +#define LOG_UPD_MODMAG 0x5553332 /* Part of a modify operation */ + + /* Tune these */ +/* XXX these need to be tuned */ +#define MAXLOGLEN 0x10000000 /* 256 MB log file */ +#define HIWATER 0x800000 /* 8 MB high water mark */ +#define FILE_BLK_SZ 0x10000 /* grow file based log by 64K */ + +#define LOG_STABLE 1 +#define LOG_CHECKPOINT 2 +#define LOG_UPDATE 3 +#define LOG_RESYNC 4 + +#define BACKUP_LOG ".BACKUP_LOG" + +/* + * Flags used for __log_resync() routine. These flags are mainly used + * to check if msync() should be called or not. msync() is used to flush + * the current mmap area to the the disk. nislog program should never + * call msync(). + */ +#define FNISD 0 /* called from nisd */ +#define FNISLOG 1 /* called from nislog program */ +#define FCHKPT 2 /* called from checkpointing section */ + +/* + * This macro returns the next longword aligned address. + */ +#define NISRNDUP(x) (((x) & 3) ? ((x) + 4) & ~3 : (x)) + +/* + * This macro defines the linear XID size of a transaction update + * in the file. The "end address" of the log, and the log size in bytes. + */ +#define XID_SIZE(u) (NISRNDUP(sizeof (log_upd) + \ + (u)->lu_size + \ + strlen((u)->lu_dirname)+1)) + +#define LOG_END(log) NISRNDUP(((u_long)(log->lh_tail) + \ + XID_SIZE(log->lh_tail))) +#define LOG_SIZE(log) (LOG_END(log) - (u_long)(log)) + +/* Function prototypes */ +#ifdef __STDC__ +extern char *__make_name(log_entry *); +extern int __log_resync(log_hdr *, int); +extern bool_t xdr_log_entry(XDR *, log_entry *); +#else +extern char *__make_name(); +extern int __log_resync(); +extern bool_t xdr_log_entry(); +#endif + +/* global data */ +extern log_hdr *__nis_log; + +#ifdef __cplusplus +} +#endif + +#endif /* _RPC_NISD_LOG_H */ diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_cleanup.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_cleanup.c new file mode 100644 index 0000000000..4cf5f7e45c --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_cleanup.c @@ -0,0 +1,194 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * The functions in this file used to reside in nis_subr_proc.c. In order + * to simplify building nislog(1M) (which links in some but not all of the + * rpc.nisd object files), it was convenient to move the functions to their + * own file. + */ + +#include <syslog.h> +#include <rpcsvc/nis.h> +#include "nis_proc.h" + +extern FILE *cons; + +void +free_abort(p) + char *p; +{ + syslog(LOG_CRIT, "Attempting to free a free rag!"); + syslog(LOG_ERR, "Attempting to free a free rag!"); + abort(); +} + +/* + * These functions are used to clean up after ourselves when we return + * a result. The primary client are the svc functions which malloc + * their data and then put it on the cleanup list when they return. + * this allows them to be reentrant. + */ +void +do_cleanup(stuff) + cleanup *stuff; +{ + cleanup *this, *nextrag; + nis_tsd_t *tsd; + + if (stuff == 0) + return; + + tsd = __nis_get_tsd(); + + for (this = stuff; this; this = nextrag) { + nextrag = this->next; +#ifdef DEBUG + if ((this->tag) && verbose) + syslog(LOG_INFO, "do_cleanup : '%s'", this->tag); +#endif + (*(this->func))(this->data); + this->func = free_abort; + this->data = NULL; + this->tag = NULL; + this->next = tsd->free_rags; + tsd->free_rags = this; + } +} + +/* + * The non-MT code allocates space for 1024 rags. Because the auto-MT + * mode of RPC usually creates a new thread for every RPC request, + * and since each thread has its own rags list, we only need enough + * for one NIS+ operation. Testing showed that handling a nis_list() + * (without callback) used seven rags, so 16 seems a good choice, + * making it unlikely that a thread will need to allocate rags more + * than once. + */ +#define RAGCHUNK 16 + +void +add_cleanup(clean_func, clean_data, ragtag) + void (*clean_func)(); + void *clean_data; + char *ragtag; +{ + register cleanup *newrag; + int i; + nis_tsd_t *tsd; + + if ((! clean_func) || (! clean_data)) { + if (cons) + fprintf(cons, "no func or data : %s\n", ragtag); + return; + } + + tsd = __nis_get_tsd(); + + if (! tsd->free_rags) { + if (verbose) + syslog(LOG_DEBUG, + "add_cleanup: Low on rags, allocating some more."); + /* Allocate extra space to keep track of the newrag block */ +#define newragoffset (1+(sizeof (cleanupblock_t)/sizeof (cleanup))) + newrag = (cleanup *)XCALLOC((RAGCHUNK + newragoffset), + sizeof (cleanup)); + if (! newrag) { + syslog(LOG_CRIT, + "add_cleanup: Can't allocate more rags."); + return; + } + + /* + * Use the first 'newragoffset' element(s) of the 'newrag' + * array for housekeeping that enables us to free the array + * on thread exit. + */ + { + cleanupblock_t **b = &(tsd->ragblocks); + cleanupblock_t *n = (cleanupblock_t *)newrag; + + n->next = *b; + *b = n; +#ifdef NIS_MT_DEBUG + printf("%d: newrag 0x%x (%d bytes)\n", + pthread_self(), n, + (RAGCHUNK+newragoffset)*sizeof (cleanup)); +#endif /* NIS_MT_DEBUG */ + } + for (i = newragoffset; i < RAGCHUNK + newragoffset; i++) { + newrag[i].next = tsd->free_rags; + tsd->free_rags = &(newrag[i]); + } + } + + newrag = tsd->free_rags; + tsd->free_rags = tsd->free_rags->next; + + newrag->func = clean_func; + newrag->data = clean_data; + newrag->tag = ragtag; + newrag->next = tsd->looseends; + newrag->id = tsd->cleanup_tag; +#ifdef DEBUG + if (verbose) + syslog(LOG_INFO, "add_cleanup (thread # %d: tag # %d '%s'", + pthread_self(), tsd->cleanup_tag, ragtag); +#endif /* DEBUG */ + tsd->cleanup_tag++; + tsd->looseends = newrag; +} + +void +do_xdr_cleanup(data) + xdr_clean_data *data; +{ + if (! data) + return; + + xdr_free(data->xdr_func, data->xdr_data); + XFREE(data->xdr_data); + XFREE(data); +} + +void +add_xdr_cleanup(func, data, t) + bool_t (*func)(); + char *data; + char *t; +{ + xdr_clean_data *dat = NULL; + + dat = (xdr_clean_data *)XCALLOC(1, sizeof (xdr_clean_data)); + if (! dat) + return; + + dat->xdr_func = func; + dat->xdr_data = (void *)data; + add_cleanup(do_xdr_cleanup, dat, t); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_db.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_db.c new file mode 100644 index 0000000000..29bf6defab --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_db.c @@ -0,0 +1,3343 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1990-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Ported from + * "@(#)nis_db.c 1.42 91/03/21 Copyr 1990 Sun Micro"; + * + * This module contains the glue routines between the actual database + * code and the NIS+ server. Presumably they are the routines that should + * be exported in the shared library, but they may be at too high a level. + */ + +#include <stdio.h> +#include <syslog.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <malloc.h> +#include <rpcsvc/nis_db.h> +#include "nis_proc.h" +#include <lber.h> +#include <ldap.h> +#include "ldap_xdr.h" +#include "ldap_util.h" +#include "nisdb_mt.h" +#include "ldap_parse.h" + +static nis_error __create_table(char *, nis_object *); + +extern bool_t xdr_nis_object(); +extern bool_t xdr_nis_name(); +extern bool_t xdr_nis_oid(); +extern bool_t xdr_objdata(XDR *, objdata*); +extern bool_t xdr_entry_col(XDR*, entry_col *); +extern char *relative_name(); +extern db_status __db_defer(char *); +extern db_status __db_commit(char *); +extern db_status __db_rollback(char *); +extern db_result *__db_list_entries(char *, int, nis_attr *, bool_t); +extern db_status __db_configure(char *); +int retrieveDone(__nisdb_retry_t *refreshRetry, + __nisdb_retry_t *retrieveretry, + int nisPlusStat, int ldapStat, + nis_error *outStat); +int storeDone(__nisdb_retry_t *storeRetry, + int nisPlusStat, int ldapStat, + nis_error *outStat); +extern db_result *__db_add_entry_nosync(char *, int, nis_attr *, entry_obj *); +extern db_result *__db_remove_entry_nosync(char *, int, nis_attr *); +extern db_result *__db_add_entry_nolog(char *, int, nis_attr *, entry_obj *); + +typedef struct table_list_entry { + char *table; + struct table_list_entry *next; +} table_list_entry_t; + +static table_list_entry_t *table_list = 0; +DECLMUTEXLOCK(table_list); + +static int mark_for_sync(char *); + +NIS_HASH_TABLE *table_cache = NULL; + +/* + * Locking of the database is implemented here. We use a two level locking + * mechanism: + * + * (1) Directories + * + * Write lock Must have full access to the directory, + * and prevent any other access. + * + * Read lock Make sure we get to do our stuff without + * interaction with someone that may modify + * the directory. + * + * (2) Tables + * + * Write lock Any operation that modifies the table or + * data in the table. + * + * Read lock Any other operation on the table. + * + * Much of this functionality relies on the implicit locking and condition + * signaling provided by our private implementation of NIS_HASH_TABLEs. + */ + +/* + * If the database is MT safe, 'RO' should be '1' (meaning, really + * do just read-only locking); otherwise, it should be '-1' to always + * use exclusive locks. + */ +#define RO 1 +#define WR -1 + +static NIS_HASH_TABLE dirlocks = NIS_HASH_TABLE_MT_INIT; + +/* Stored in the 'dirlocks' list */ +typedef struct { + NIS_HASH_ITEM item; + NIS_HASH_TABLE tablelocks; + __nis_defer_t defer; +} dirlock_t; + +/* Stored in the 'tablelocks' list */ +typedef struct { + NIS_HASH_ITEM item; + __nis_defer_t defer; +} tablelock_t; + +#define LOCK_DIRECTORY(name, rw, status, msg) \ + { \ + int trylock = (ldapConfig.exclusiveWaitMode == block) ? \ + 0 : 1; \ + status = NIS_SUCCESS; \ + if (__nis_lock_db_directory(name, rw, &trylock, msg) == 0) { \ + if (trylock == -1) { \ + status = NIS_TRYAGAIN; \ + } else { \ + status = NIS_SYSTEMERROR; \ + } \ + } \ + } + +#define ULOCK_DIRECTORY(name, rw, msg) \ + (void) __nis_ulock_db_directory(name, rw, 0, msg) + +#define LOCK_TABLE_NORET(name, rw, status, msg) \ + { \ + int trylock = (ldapConfig.exclusiveWaitMode == block) ? \ + 0 : 1; \ + status = NIS_SUCCESS; \ + if (__nis_lock_db_table(name, rw, &trylock, msg) == 0) { \ + if (trylock == -1) { \ + status = NIS_TRYAGAIN; \ + } else { \ + status = NIS_SYSTEMERROR; \ + } \ + } \ + } + +#define LOCK_TABLE(name, rw, res, status, msg) \ + { \ + int trylock = (ldapConfig.exclusiveWaitMode == block) ? \ + 0 : 1; \ + if (__nis_lock_db_table(name, rw, &trylock, msg) == 0) { \ + if (trylock == -1) { \ + status = NIS_TRYAGAIN; \ + } else { \ + status = NIS_SYSTEMERROR; \ + } \ + return (res); \ + } \ + } + +#define ULOCK_TABLE(name, rw, msg) \ + (void) __nis_ulock_db_table(name, rw, 0, msg) + +#define ULOCK_TABLE_REMCOND(name, rw, condition, msg) \ + (void) __nis_ulock_db_table(name, rw, (condition), msg) + +static const char *rootname = "."; + +static char * +__dirname(nis_name name) { + + if (name == 0 || (name = strchr(name, '.')) == 0 || + *(++name) == '\0') { + name = (char *)rootname; + } + + return (name); +} + +void * +__nis_lock_db_directory(nis_name name, int readwrite, int *trylock, char *msg) { + + dirlock_t *dirlock; + char namebuf[NIS_MAXNAMELEN * 2]; + + name = __dirname(name); + + if ((dirlock = (dirlock_t *)__nis_find_item_mt(name, &dirlocks, + readwrite, trylock)) == 0) { + if (trylock != 0 && *trylock == -1) { + MT_LOG(1, (LOG_WARNING, + "%d: db directory %s lock deferred \"%s\"", + pthread_self(), MT_LOCK_TYPE(readwrite), name)); + return (0); + } + /* Doesn't exist; create one */ + if ((dirlock = malloc(sizeof (*dirlock))) == 0) { + return (0); + } + if ((dirlock->item.name = strdup(name)) == 0) { + free(dirlock); + return (0); + } + __nis_init_hash_table(&dirlock->tablelocks, 0); + dirlock->defer = d_none; + if (__nis_insert_item_mt((NIS_HASH_ITEM *)dirlock, &dirlocks, + readwrite) == 0) { + free(dirlock->item.name); + free(dirlock); + /* + * nis_insert_item() => 0 could mean that the item + * already exists (i.e., someone else created it), + * so we'll try to find it one last time. + */ + if ((dirlock = (dirlock_t *)__nis_find_item_mt(name, + &dirlocks, readwrite, trylock)) == 0) { + return (0); + } + } + MT_LOG(1, (LOG_NOTICE, "db directory lock created \"%s\"", + name)); + } + + MT_LOG(1, (LOG_NOTICE, "%d: acquired db directory %s lock \"%s\"", + pthread_self(), MT_LOCK_TYPE(readwrite), name)); + return (dirlock); +} + +int +__nis_ulock_db_directory(nis_name name, int readwrite, int remove, char *msg) { + + dirlock_t *dirlock; + char namebuf[NIS_MAXNAMELEN * 2]; + + name = __dirname(name); + + if ((dirlock = (dirlock_t *)__nis_find_item_mt(name, &dirlocks, + readwrite, 0)) != 0) { + /* Only remove if table list is empty and not in defer mode */ + if (remove && dirlock->tablelocks.first == 0 && + dirlock->defer == d_none) { + NIS_HASH_ITEM *item; + item = __nis_remove_item_mt(name, &dirlocks); + if (item != 0) { + free(item->name); + free(item); + } + MT_LOG(1, (LOG_NOTICE, + "%d: db dir %s lock removed \"%s\"", + pthread_self(), MT_LOCK_TYPE(readwrite), name)); + } else { + /* + * The __nis_lock_db_directory() call incremented the + * refcount on the item, and we've just incremented it + * again via __nis_find_item_mt(). Thus, we double the + * refcount to be released for __nis_release_item. + */ + if (__nis_release_item(dirlock, &dirlocks, + 2*readwrite)) { + MT_LOG(1, (LOG_NOTICE, + "%d: db dir %s lock released \"%s\"", + pthread_self(), MT_LOCK_TYPE(readwrite), name)); + } else { + MT_LOG(1, (LOG_WARNING, + "%d: error releasing dirlock item for \"%s\"", + pthread_self(), name)); + } + } + + return (1); + } + + MT_LOG(1, (LOG_ERR, "%d: db dir %s lock not found (unlock) \"%s\"", + pthread_self(), MT_LOCK_TYPE(readwrite), name)); + + return (0); +} + +/* + * Note that the 'trylock' semantics only apply to the directory in which + * the table resides, not to the table itself. + */ +int +__nis_lock_db_table(nis_name name, int readwrite, int *trylock, char *msg) { + + dirlock_t *dirlock; + tablelock_t *tablelock; + char *tablename = name; + + if ((dirlock = (dirlock_t *)__nis_lock_db_directory(name, 1, trylock, + name)) == 0) + return (0); + + if ((tablelock = (tablelock_t *)__nis_find_item_mt(tablename, + &dirlock->tablelocks, readwrite, 0)) == 0) { + if ((tablelock = malloc(sizeof (*tablelock))) == 0) { + (void) __nis_ulock_db_directory(name, 1, 0, name); + return (0); + } + if ((tablelock->item.name = strdup(tablename)) == 0) { + free(tablelock); + (void) __nis_ulock_db_directory(name, 1, 0, name); + return (0); + } + tablelock->defer = d_none; + if (__nis_insert_item_mt((NIS_HASH_ITEM *)tablelock, + &dirlock->tablelocks, readwrite) == 0) { + free(tablelock->item.name); + free(tablelock); + /* Some other thread may have created it */ + if ((tablelock = (tablelock_t *)__nis_find_item_mt( + tablename, &dirlock->tablelocks, readwrite, 0)) == 0) { + (void) __nis_ulock_db_directory(name, 1, 0, + name); + return (0); + } + } + /* If directory is in defer mode, make the table so as well */ + if (dirlock->defer == d_defer && tablelock->defer == d_none) { + db_status stat = __db_defer(tablelock->item.name); + if (stat == DB_SUCCESS || stat == DB_NOTFOUND) { + tablelock->defer = d_defer; + } else { +#ifdef NIS_MT_DEBUG + abort(); +#endif /* NIS_MT_DEBUG */ + MT_LOG(1, (LOG_WARNING, + "%d: defer DB error %d for \"%s\"", + pthread_self(), stat, + tablelock->item.name)); + } + } + MT_LOG(1, (LOG_NOTICE, "%d: db table lock created \"%s\"", + pthread_self(), name)); + } + /* + * We've got the item, so we're done. Hang on to the read-only + * directory lock so that an exclusive access to the directory + * (probably a checkpoint) must wait for us to be done with the table. + */ + MT_LOG(1, (LOG_NOTICE, "%d: db table %s lock acquired \"%s\"", + pthread_self(), MT_LOCK_TYPE(readwrite), name)); + return (1); +} + +int +__nis_ulock_db_table(nis_name name, int readwrite, int remove, char *msg) { + + dirlock_t *dirlock; + tablelock_t *tablelock; + char *tablename = name; + + /* + * At this point, we should be holding a read-only lock on the + * directory entry, so we just obtain it without any additional + * locking. + */ + dirlock = (dirlock_t *)__nis_lock_db_directory(name, 0, 0, name); + if (dirlock == 0) { + return (0); + } + + if ((tablelock = (tablelock_t *)__nis_find_item_mt(tablename, + &dirlock->tablelocks, readwrite, 0)) != 0) { + if (remove && readwrite == WR && tablelock->defer == d_none) { + NIS_HASH_ITEM *item; + item = __nis_remove_item_mt(tablename, + &dirlock->tablelocks); + if (item != 0) { + free(item->name); + free(item); + } + MT_LOG(1, (LOG_NOTICE, + "%d: removed db table lock \"%s\"", + pthread_self(), tablename)); + } else { + /* + * Normally, if the directory is in defer mode, the + * table would have been put in defer mode during the + * table lock. However, if the operation that acquired + * the lock also created the table, it couldn't be + * deferred back then, so we do it now instead. + */ + if (dirlock->defer == d_defer && + tablelock->defer == d_none) { + db_status s; + s = __db_defer(tablelock->item.name); + if (s == DB_SUCCESS) { + tablelock->defer = d_defer; + } else if (s != DB_NOTFOUND) { + MT_LOG(1, (LOG_WARNING, + "%d: defer DB error %d for \"%s\"", + pthread_self(), s, + tablelock->item.name)); + } + } + (void) __nis_release_item((NIS_HASH_ITEM *)tablelock, + &dirlock->tablelocks, 2*readwrite); + MT_LOG(1, (LOG_NOTICE, + "%d: released db %s table lock \"%s\"", + pthread_self(), MT_LOCK_TYPE(readwrite), tablename)); + } + } + + MT_LOG(dirlock == 0, (LOG_ERR, + "%d: could not find %s lock (unlock) \"%s\"", + pthread_self(), MT_LOCK_TYPE(readwrite), tablename)); + + return (__nis_ulock_db_directory(name, 1, 0, name)); +} + +/* + * Set the directory (and any known tables) to be deferred. + * If there's a failure attempting to defer a table, we try + * to rollback those deferred so far, and return the failure. + */ +db_status +db_defer(nis_name name) { + + dirlock_t *dirlock; + db_status stat = DB_SUCCESS; + + if ((dirlock = __nis_lock_db_directory(name, WR, NULL, "db_defer")) == + 0) + return (DB_NOTFOUND); + + /* + * If the directory isn't already in defer mode, make it so. + */ + if (dirlock->defer == d_none) { + tablelock_t *t; + db_status s; + + dirlock->defer = d_defer; + + /* Traverse table locks and set defer mode */ + for (t = (tablelock_t *)dirlock->tablelocks.first; + t != 0 && stat == DB_SUCCESS; + t = (tablelock_t *)t->item.nxt_item) { + if (t->defer == d_none) { + s = __db_defer(t->item.name); + /* + * DB_NOTFOUND is OK; the tablelocks list is + * out of date. Don't mark the table deferred, + * though, because it doesn't exist in the DB. + */ + if (s == DB_SUCCESS) { + t->defer = d_defer; + } else if (s != DB_NOTFOUND) { + /* Remember the first failure */ + if (stat == DB_SUCCESS) + stat = s; +#ifdef NIS_MT_DEBUG + abort(); +#endif /* NIS_MT_DEBUG */ + MT_LOG(1, (LOG_WARNING, + "%d: defer DB error %d for table \"%s\"", + pthread_self(), s, + t->item.name)); + } + } + } + + if (stat != DB_SUCCESS) { + /* Try to rollback deferred tables */ + for (t = (tablelock_t *)dirlock->tablelocks.first; + t != 0; + t = (tablelock_t *)t->item.nxt_item) { + if (t->defer == d_defer) { + s = __db_rollback(t->item.name); + if (s == DB_SUCCESS) { + t->defer = d_none; + } else { +#ifdef NIS_MT_DEBUG + abort(); +#endif /* NIS_MT_DEBUG */ + MT_LOG(1, (LOG_WARNING, + "%d: rollback DB error %d for table \"%s\"", + pthread_self(), s, + t->item.name)); + } + } + } + dirlock->defer = d_none; + } + } + + if (__nis_ulock_db_directory(name, WR, 0, "db_defer") == 0) { + MT_LOG(1, (LOG_WARNING, + "%d: db_defer: error unlocking \"%s\"", + pthread_self(), name)); + } + + return (stat); +} + +static db_status +__db_undefer(nis_name name, __nis_defer_t mode) { + + dirlock_t *dirlock; + tablelock_t *t; + db_status stat = DB_SUCCESS; + + if (mode != d_commit && mode != d_rollback) + return (DB_BADQUERY); + + if ((dirlock = __nis_lock_db_directory(name, WR, 0, "db_undefer")) == 0) + return (DB_NOTFOUND); + + /* Commit or rollback deferred table changes */ + for (t = (tablelock_t *)dirlock->tablelocks.first; + t != 0; + t = (tablelock_t *)t->item.nxt_item) { + db_status s; + + if (t->defer == d_defer) { + if (mode == d_commit) + s = __db_commit(t->item.name); + else + s = __db_rollback(t->item.name); + /* + * DB_NOTFOUND is OK; just means that the tablelocks + * list is out-of-date. + */ + if (s == DB_SUCCESS || s == DB_NOTFOUND) { + t->defer = d_none; + } else { + /* Keep first err */ + if (stat == DB_SUCCESS) + stat = s; + MT_LOG(1, (LOG_WARNING, + "%d: %s DB error %d for table \"%s\"", + pthread_self(), + (mode == d_commit) ? "commit" : "rollback", + s, t->item.name)); + } + } + } + /* + * Even if there were errors un-deferring one or more of the tables, + * we still want to set the directory undeferred. Otherwise, we'd + * continue to defer any new (meaning a tablelocks entry added) + * tables. + */ + dirlock->defer = d_none; + + if (__nis_ulock_db_directory(name, WR, 0, "db_undefer") == 0) { + MT_LOG(1, (LOG_WARNING, + "%d: db_undefer: error unlocking \"%s\"", + pthread_self(), name)); + } + + return (stat); +} + +db_status +db_commit(nis_name name) { + return (__db_undefer(name, d_commit)); +} + +db_status +db_rollback(nis_name name) { + return (__db_undefer(name, d_rollback)); +} + +/* + * Free resources associated with a db_result structure + */ +static void +free_db_result(db_result *dr) +{ + int i; + + if (dr == 0) + return; + + if (dr->status != DB_SUCCESS) { + /* Can't have valid objects */ + free(dr); + return; + } + + for (i = 0; i < dr->objects.objects_len; i++) + free_entry(dr->objects.objects_val[i]); + free(dr->objects.objects_val); + free(dr); +} + +static void +free_nis_db_result(nis_db_result *res) +{ + /* We do not free obj here because it is cached in table_cache */ + XFREE(res); +} + +static nis_error +map_db_status_to_nis_status(db_status dstatus) +{ + switch (dstatus) { + case DB_SUCCESS: + return (NIS_SUCCESS); + case DB_NOTFOUND: + return (NIS_NOTFOUND); + case DB_BADTABLE: + return (NIS_NOSUCHTABLE); + case DB_MEMORY_LIMIT: + return (NIS_NOMEMORY); + case DB_STORAGE_LIMIT: + return (NIS_NOFILESPACE); + case DB_NOTUNIQUE: + return (NIS_NAMEEXISTS); + case DB_BADQUERY: + return (NIS_BADREQUEST); + case DB_BADOBJECT: + return (NIS_BADOBJECT); + case DB_INTERNAL_ERROR: + default: + return (NIS_SYSTEMERROR); + } +} + +/* + * This function converts the internal format entries of the DB into + * a list of nis_objects that the server understands. The object returned may + * be destroyed with nis_destroy_object(); + * + * Notes : When listing directories the list function expects to see entry + * objects and this function will mangle regular objects into entry + * objects. The entry has one column which contains the binary + * equivalent of what the type would have been if it hadn't been + * mangled. + * + * When dumping directories we need the objects intact so we set mangle + * to false and return the real objects. + */ +static obj_list * +cvt2object( + nis_name tablename, /* table which has these entries */ + entry_obj *ep[], + uint_t num, + int *got, + int mangle) /* Make non-entry objects into psuedo entries */ +{ + register obj_list *oa; /* object array */ + nis_object *tmp; /* Temporary object(s) */ + XDR xdrs; /* temporary xdr stream */ + int status, /* XDR op status */ + curr_obj, /* Current Object */ + i, j, mc; + entry_obj *eo; /* tmp, makes life easier */ + entry_col *ec; + unsigned long etype; /* for fake entries */ + struct table_item *te; /* Table cache entry */ + struct nis_object *tobj; /* Table nis_object */ + + *got = 0; /* Number of objects decoded */ + te = __nis_find_item_mt(tablename, table_cache, RO, 0); + if (te == NULL) { + /* Do a db_lookup() so that cache is populated */ + nis_db_result *dbres; + + if (((dbres = db_lookup(tablename)) == NULL) || + (dbres->status != NIS_SUCCESS)) + tobj = NULL; + else + tobj = dbres->obj; + /* dbres is freed automatically during cleanup */ + } else { + tobj = te->ibobj; + } + oa = (obj_list *)XCALLOC(num, sizeof (obj_list)); + if (oa == NULL) { + if (te != 0) + __nis_release_item(te, table_cache, RO); + return (NULL); + } + + curr_obj = 0; + for (i = 0; i < num; i++) { + if (! ep[i]) { + syslog(LOG_ERR, + "cvt2object: NULL Object in database, ignored."); + continue; + } + ec = ep[i]->en_cols.en_cols_val; + mc = ep[i]->en_cols.en_cols_len; + /* + * Set up a memory stream pointing at the first column value + * which contains the XDR encoded NIS+ object. The second + * column contains the name of the NIS+ object. + */ + xdrmem_create(&xdrs, ec->ENVAL, ec->ENLEN, XDR_DECODE); + tmp = (nis_object *)XCALLOC(1, sizeof (nis_object)); + if (tmp == NULL) { + /* I'll return with the current list of objects */ + if (te != 0) + __nis_release_item(te, table_cache, RO); + return (oa); + } + /* + * Decode it into the object structure. If some fields + * are NULL, fill in appropriate values from the table + * nis_object structure. + * + * If the entry object has type "IN_DIRECTORY" then it + * is a NIS+ directory we are listing else it is + * an ENTRY Object. For ENTRY objects, we call our + * special xdr_nis_fetus_object() which knows how to + * reconstruct the entry object from the given info. + * XXX: _any_ other value we are hosed. If it is 0 or a + * NULL string, it denotes that it is an entry object. + */ + if (((ep[i]->en_type == 0) || (ep[i]->en_type[0] == 0)) && tobj) + status = xdr_nis_fetus_object(&xdrs, tmp, tobj); + else + status = xdr_nis_object(&xdrs, tmp); + /* + * POLICY : What to do about undecodeable objects ? + * ANSWER : Leave it blank and continue. (soft failure) + */ + if (! status) { + syslog(LOG_ERR, + "cvt2object: Corrupted object ('%s') in database %s", + ec[1].ENVAL, tablename); + XFREE(tmp); + continue; + } + + /* + * If the entry object has type 0 or "IN_TABLE" then it + * is an entry object. If it has type "IN_DIRECTORY" then it + * is a NIS+ directory that we are listing. + * XXX: _any_ other value we are hosed. + */ + if ((ep[i]->en_type == 0) || (ep[i]->en_type[0] == 0) || + strcmp(ep[i]->en_type, "IN_TABLE") == 0) { + if (__type_of(tmp) != NIS_ENTRY_OBJ) { + syslog(LOG_ERR, + "cvt2object: Corrupt database, entry expected for %s", ec[1].ENVAL); + xdr_free(xdr_nis_object, (char *)tmp); + XFREE(tmp); + continue; + } + /* + * Set the column fields appropriately. Copy all the + * col entry pointers to the new entry object. We are + * mucking around with the list returned by + * db_list_entries - UNCLEAN, UNCLEAN! + */ + eo = &(tmp->EN_data); + eo->en_cols.en_cols_len = mc - 1; + eo->en_cols.en_cols_val = XMALLOC((mc - 1) * + (sizeof (entry_col))); + if (eo->en_cols.en_cols_val == NULL) { + xdr_free(xdr_nis_object, (char *)tmp); + XFREE(tmp); + if (te != 0) + __nis_release_item(te, table_cache, + RO); + return (oa); + } + for (j = 1; j < mc; j++) { + eo->en_cols.en_cols_val[j-1] = + ep[i]->en_cols.en_cols_val[j]; + } + /* We set len to 1, so that other cols are not freed */ + ep[i]->en_cols.en_cols_len = 1; + } else if (mangle) { + /* Convert this dir object into a "fake" entry object */ + etype = htonl(__type_of(tmp)); /* save the old type */ + /* first free the object specific data */ + xdr_free(xdr_objdata, (char *)&tmp->zo_data); + memset(&tmp->zo_data, 0, sizeof (objdata)); + /* Now fake a entry object */ + __type_of(tmp) = NIS_ENTRY_OBJ; + eo = &(tmp->EN_data); + eo->en_type = strdup("NIS object"); /* special type */ + if (eo->en_type == NULL) { + xdr_free(xdr_nis_object, (char *)tmp); + if (te != 0) + __nis_release_item(te, table_cache, + RO); + return (oa); + } + ec = (entry_col *) XMALLOC(sizeof (entry_col)); + if (ec == NULL) { + xdr_free(xdr_nis_object, (char *)tmp); + if (te != 0) + __nis_release_item(te, table_cache, + RO); + return (oa); + } + eo->en_cols.en_cols_len = 1; + eo->en_cols.en_cols_val = ec; + ec[0].ec_flags = EN_BINARY + EN_XDR; + ec[0].ENVAL = XMALLOC(4); + if (ec[0].ENVAL == NULL) { + xdr_free(xdr_nis_object, (char *)tmp); + if (te != 0) + __nis_release_item(te, table_cache, + RO); + return (oa); + } + ec[0].ENLEN = 4; + memcpy(ec[0].ENVAL, (char *)&etype, 4); + } + oa[curr_obj++].o = tmp; + (*got)++; /* Add one to the total */ + } + + if (te != 0) + __nis_release_item(te, table_cache, RO); + return (oa); +} + +/* + * Release hash item. Intended for use by the cleanup code, executed + * when the thread is about to return to the RPC dispatch. + */ +void +tableCacheReleaseRO(void *ptr) { + struct table_item *te = ptr; + + if (te != 0) + (void) __nis_release_item(te, table_cache, RO); +} + +/* + * Destroy a table cache item. + */ +void +destroyTableCacheItem(void *item) { + struct table_item *ti = item; + + if (ti != 0) { + free(ti->ti_item.name); + nis_destroy_object(ti->ibobj); + free(ti); + } +} + +/* + * Return value if allocation of nis_db_result return value fails. Must + * not be freed, of course, but that's OK since callers already expect + * that to happen through the cleanup code. + */ +static nis_db_result __no_mem_nis_db_result = {NIS_NOMEMORY, 0, 0}; + +/* + * The following routines implement the database operations on the namespace. + * The database only understands tables, so it is up these routines to + * "fake out" the database and to build a table that defines the namespace + * that we are serving. The format of this table is fixed. It consists of + * a table with two columns, the first being the object (XDR encoded) + * and the second being the "name" of the object. + * + * Note : All of these routines assume they are passed "full" NIS+ names + * Note : The fake entry structure is the way it is to be compatible with + * the cvt2object() function above. + * + * Lookup NIS+ objects in the namespace that are stored as entries in a + * table by the same name as the directory being served. These entries + * have two columns, column 0 is the object data and column 1 is the + * object name. + */ +nis_db_result * +db_lookup_deferred(nis_name name, bool_t useDeferred) +{ + int status; + nis_attr attr; + db_result *dbres; + register entry_col *ec; + XDR xdrs; + nis_db_result *res; + char *table; /* The table path name */ + char tblbuf[NIS_MAXNAMELEN * 2]; /* table path buf */ + struct table_item *te; /* Table cache entry */ + int triedLDAP; + char *myself = "db_lookup_deferred"; + + if (verbose) + syslog(LOG_INFO, "db_lookup: Looking for %s%s", name, + useDeferred ? " in deferred DB" : ""); + + if ((res = (nis_db_result *) XCALLOC(1, sizeof (nis_db_result))) == 0) + return (&__no_mem_nis_db_result); + add_cleanup(free_nis_db_result, (void *)(res), "db_lookup result"); + + if (!table_cache) { + table_cache = (NIS_HASH_TABLE *) + XCALLOC(1, sizeof (NIS_HASH_TABLE)); + if (!table_cache) { + syslog(LOG_ERR, "db_lookup: out of memory"); + res->status = NIS_NOMEMORY; + return (res); + } + __nis_init_hash_table(table_cache, destroyTableCacheItem); + } + + LOCK_TABLE(name, RO, res, res->status, name); + + /* + * Now we check the cache to see if we have it cached locally. + */ + te = __nis_find_item_mt(name, table_cache, RO, 0); + if (te) { + if (verbose) + syslog(LOG_INFO, "db_lookup: table cache hit"); + res->status = NIS_SUCCESS; + /* Defer release of item */ + add_cleanup(tableCacheReleaseRO, te, "table obj from cache"); + ULOCK_TABLE(name, RO, name); + res->obj = te->ibobj; + return (res); + } + + table = internal_table_name(nis_domain_of(name), tblbuf); + /* This happens when we're looking for directory objects. */ + if (! table) { + res->status = NIS_NOSUCHTABLE; + ULOCK_TABLE(name, RO, name); + return (res); + } + attr.zattr_ndx = "name"; + attr.ZAVAL = nis_leaf_of(name); + attr.ZALEN = strlen(attr.ZAVAL)+1; + + if (verbose) + syslog(LOG_INFO, "db_lookup: looking up %s in table %s", + attr.ZAVAL, table); + __start_clock(1); + + { + nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd(); + __nisdb_retry_t refreshRetry = ldapDBTableMapping. + refreshErrorRetry; + __nisdb_retry_t retrieveRetry = ldapDBTableMapping. + retrieveErrorRetry; + + if (tsd == 0) { + logmsg(MSG_NOMEM, LOG_ERR, + "%s: No memory for TSD; unable to get full status for DB operation", + myself); + tsd = &fallback; + } + + triedLDAP = 0; + while (1) { + /* Establish default status (success) */ + tsd->nisPlusStat = NIS_SUCCESS; + tsd->ldapStat = LDAP_SUCCESS; + dbres = __db_list_entries(table, 1, &attr, + useDeferred); + if (dbres == 0) { + res->status = NIS_NOMEMORY; + break; + } else { + int rd; + + res->status = + map_db_status_to_nis_status(dbres->status); + if (res->status == NIS_SUCCESS && + tsd->ldapStat == LDAP_SUCCESS && + tsd->nisPlusStat == NIS_SUCCESS) + break; + /* Unlock while we (possibly) sleep */ + ULOCK_TABLE(name, RO, name); + rd = retrieveDone(&refreshRetry, + &retrieveRetry, + tsd->nisPlusStat, + tsd->ldapStat, &res->status); + LOCK_TABLE(name, RO, res, res->status, name); + if (rd) { + /* + * If we failed because there was + * no such entry in the directory, + * try to get the entry from LDAP. + * If successful, look in the dir + * again. + */ + if (!triedLDAP && res->status == + NIS_NOTFOUND) { + db_status dstat; + int lstat; + char *intName; + + triedLDAP = 1; + intName = internalTableName( + name); + if (intName == 0) { + res->status = + NIS_NOMEMORY; + break; + } + refreshRetry = + ldapDBTableMapping.refreshErrorRetry; + retrieveRetry = + ldapDBTableMapping.retrieveErrorRetry; + do { + tsd->nisPlusStat = + NIS_SUCCESS; + tsd->ldapStat = + LDAP_SUCCESS; + dstat = + dbCreateFromLDAP( + intName, &lstat); + + res->status = + map_db_status_to_nis_status(dstat); + + rd = retrieveDone( + &refreshRetry, + &retrieveRetry, + NIS_SUCCESS, + lstat, + &res->status); + } while (!rd); + free(intName); + if (dstat == DB_SUCCESS) + continue; + else if (lstat == + LDAP_NO_SUCH_OBJECT) { + res->status = + NIS_NOTFOUND; + logmsg(MSG_NOTIMECHECK, + LOG_INFO, + "%s: no LDAP data for \"%s\"", + name); + } else if (lstat != + LDAP_SUCCESS) { + logmsg(MSG_NOTIMECHECK, + LOG_WARNING, + "%s: LDAP error for \"%s\": %s", + myself, name, + ldap_err2string(lstat)); + } + } + break; + } + } + free_db_result(dbres); + } + res->ticks = __stop_clock(1); + } + + if (res->status == NIS_SUCCESS || res->status == NIS_CACHEEXPIRED) { + /* ASSERT(dbres->objects.objects_len == 1); */ + + /* + * convert from XDR format that the DB returns to + * the nis_object format + */ + ec = dbres->objects.objects_val[0]->en_cols.en_cols_val; + + xdrmem_create(&xdrs, ec->ENVAL, ec->ENLEN, XDR_DECODE); + res->obj = (nis_object *)XCALLOC(1, sizeof (nis_object)); + if (!(res->obj)) + res->status = NIS_NOMEMORY; + else { + status = xdr_nis_object(&xdrs, res->obj); + if (! status) { + syslog(LOG_ERR, "db_lookup: Corrupt object %s", + name); + XFREE(res->obj); + res->obj = NULL; + res->status = NIS_SYSTEMERROR; + } + } + } + + if (verbose && dbres) + syslog(LOG_INFO, "db_lookup: exit status is %d", dbres->status); + + if (res->obj) { + int doRead; + __nis_buffer_t b = {0, 0}; + + /* + * If the object is read-mapped from LDAP, we don't want + * to cache it in the table_cache. + */ + bp2buf("db_lookup_deferred", &b, "%s.%s", + res->obj->zo_name, res->obj->zo_domain); + if ((__type_of(res->obj) == NIS_TABLE_OBJ) && + useDeferred && + (getObjMapping(b.buf, 0, 1, &doRead, 0) == 0 || + !doRead) && + strstr(name, "org_dir")) { + /* + * Cache the table objects in the "org_dir" + * dir. We want to cache only the "org_dir" tables + * instead of caching all zillions of tables. + */ + te = (struct table_item *)XCALLOC(1, sizeof (*te)); + if (te) { + te->ti_item.name = (nis_name) strdup(name); + if (te->ti_item.name == NULL) { + add_cleanup(nis_destroy_object, + (void *)(res->obj), + "db_lookup result"); + free(te); + } else { + te->ibobj = res->obj; + (void) __nis_insert_item_mt(te, + table_cache, 0); + if (verbose) + syslog(LOG_INFO, + "Added %s to the table cache", + name); + } + } else { + add_cleanup(nis_destroy_object, + (void *)(res->obj), "db_lookup result"); + } + } else { + add_cleanup(nis_destroy_object, + (void *)(res->obj), "db_lookup result"); + } + if (b.buf != 0) + free(b.buf); + } + + free_db_result(dbres); + ULOCK_TABLE(name, RO, name); + return (res); +} + +nis_db_result * +db_lookup(nis_name name) { + return (db_lookup_deferred(name, TRUE)); +} + +/* + * __db_add() + * + * The internal version of db_add, this one doesn't add an entry into + * the log. This function converts the nis_object into a DB style object + * and then adds it to the database. + */ +nis_error +__db_add( + nis_name name, + nis_object *obj, + int modop) /* Modify operation flag */ +{ + nis_attr attr; + int i; + db_result *dbres; + entry_col ecols[2]; + entry_obj entry; + char *table; + uchar_t *buf; + nis_error res; + XDR xdrs; + char tblbuf[NIS_MAXNAMELEN * 2]; + char *myself = "__db_add"; + + if (verbose) + syslog(LOG_INFO, "__db_add: attempting to %s %s", + modop? "modify" : "add", name); + + LOCK_TABLE(name, WR, res, res, name); + + if ((__type_of(obj) == NIS_TABLE_OBJ) && (!modop)) { + if ((res = __create_table(name, obj)) != NIS_SUCCESS) { + ULOCK_TABLE(name, WR, name); + syslog((res != NIS_NAMEEXISTS) ? LOG_ERR : LOG_INFO, + "__db_add: Unable to create database for NIS+ table %s: %s.", + name, nis_sperrno(res)); + return (res); + } + } + + table = internal_table_name(nis_domain_of(name), tblbuf); + if (! table) { + ULOCK_TABLE(name, WR, name); + return (NIS_BADNAME); + } + + i = xdr_sizeof(xdr_nis_object, obj); + buf = __get_xdr_buf(i); + xdrmem_create(&xdrs, (char *)buf, i, XDR_ENCODE); + i = xdr_nis_object(&xdrs, obj); + if (! i) { + ULOCK_TABLE(name, WR, name); + syslog(LOG_ERR, "__db_add: cannot encode object %s", name); + return (NIS_SYSTEMERROR); + } + + /* Now we cons up an entry for this object in the name space */ + entry.en_type = "IN_DIRECTORY"; + entry.en_cols.en_cols_len = 2; + entry.en_cols.en_cols_val = ecols; + ecols[0].ec_flags = EN_XDR + EN_MODIFIED; + ecols[0].ENVAL = (char *)buf; + ecols[0].ENLEN = xdr_getpos(&xdrs); + ecols[1].ec_flags = EN_MODIFIED; + ecols[1].ENVAL = nis_leaf_of(name); + ecols[1].ENLEN = strlen(ecols[1].ENVAL)+1; + attr.zattr_ndx = "name"; + attr.ZAVAL = ecols[1].ENVAL; + attr.ZALEN = ecols[1].ENLEN; + + { + nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd(); + __nisdb_retry_t storeRetry = ldapDBTableMapping. + storeErrorRetry; + + if (tsd == 0) { + logmsg(MSG_NOMEM, LOG_ERR, + "%s: No memory for TSD; unable to get full status for DB operation", + myself); + tsd = &fallback; + } + + while (1) { + /* Establish default status (success) */ + tsd->nisPlusStat = NIS_SUCCESS; + tsd->ldapStat = LDAP_SUCCESS; + dbres = db_add_entry(table, 1, &attr, &entry); + if (dbres == 0) { + res = NIS_NOMEMORY; + break; + } else { + int rd; + + res = map_db_status_to_nis_status(dbres-> + status); + if (res == NIS_SUCCESS && + tsd->ldapStat == LDAP_SUCCESS && + tsd->nisPlusStat == NIS_SUCCESS) + break; + /* Unlock while we (possibly) sleep */ + ULOCK_TABLE(name, WR, name); + rd = storeDone(&storeRetry, tsd->nisPlusStat, + tsd->ldapStat, &res); + LOCK_TABLE(name, WR, res, res, name); + if (rd) + break; + } + free_db_result(dbres); + } + } + + switch (res) { + case NIS_NOMEMORY: + case NIS_NOFILESPACE: + case NIS_SYSTEMERROR: + case NIS_UNAVAIL: + break; /* these are expected */ + case NIS_SUCCESS: + if (modop) { + /* Flush various cached objects */ + switch __type_of(obj) { + case NIS_TABLE_OBJ: + flush_tablecache(name); + break; + case NIS_DIRECTORY_OBJ: + flush_dircache(name, (directory_obj *)NULL); + break; + case NIS_GROUP_OBJ: + flush_groupcache(name); + break; + default: + break; + } + } + (void) mark_for_sync(table); + break; + default: + syslog(LOG_ERR, "__db_add: unexpected database result %d", + dbres->status); + break; + } + + free_db_result(dbres); + if (res == NIS_SUCCESS) { + multival_invalidate(obj); + } + ULOCK_TABLE(name, WR, name); + return (res); +} + +/* + * db_add() + * + * External wrapper for the real db_add function. This one creates a + * transaction log entry. The internal one skips the log transaction. + */ +nis_db_result * +db_add( + nis_name name, + nis_object *obj, + int mod_op) +{ +#define res __nis_get_tsd()->db_add_res + log_entry le; /* A log entry */ + + memset((char *)&res, 0, sizeof (res)); + memset((char *)&le, 0, sizeof (le)); + if (verbose) { + if (mod_op == 0) + syslog(LOG_INFO, "db_add: Adding object %s", name); + else + syslog(LOG_INFO, "db_add: Modifying object %s", name); + } + + LOCK_TABLE(name, WR, &res, res.status, name); + + le.le_time = obj->zo_oid.mtime; + if (mod_op) + le.le_type = MOD_NAME_NEW; + else + le.le_type = ADD_NAME; + le.le_name = name; + le.le_object = *obj; + add_update(&le); + __start_clock(1); + res.status = __db_add(name, obj, mod_op); + res.ticks = __stop_clock(1); + if (verbose || (res.status != NIS_SUCCESS)) + syslog(LOG_INFO, "db_add: returning %s", + nis_sperrno(res.status)); + ULOCK_TABLE(name, WR, name); + return (&res); +} + +#undef res + +nis_error +__db_remove( + nis_name name, /* Name of object to remove */ + nis_object *obj) /* Its type (for deleting tables) */ +{ + nis_attr attr; + char *table; + db_result *dbres; + nis_error res; + char tblbuf[NIS_MAXNAMELEN * 2]; + int table_deleted = 0; + char *myself = "__db_remove"; + + LOCK_TABLE(name, WR, res, res, name); + if (__type_of(obj) == NIS_TABLE_OBJ) { + table = internal_table_name(name, tblbuf); + if (! table) { + ULOCK_TABLE(name, WR, name); + return (NIS_BADNAME); + } + + /* First make sure the table is empty */ + + { + nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd(); + __nisdb_retry_t refreshRetry = ldapDBTableMapping. + refreshErrorRetry; + __nisdb_retry_t retrieveRetry = ldapDBTableMapping. + retrieveErrorRetry; + + if (tsd == 0) { + logmsg(MSG_NOMEM, LOG_ERR, + "%s: No memory for TSD; unable to get full status for DB operation", + myself); + tsd = &fallback; + } + + while (1) { + /* Establish default status (success) */ + tsd->nisPlusStat = NIS_SUCCESS; + tsd->ldapStat = LDAP_SUCCESS; + dbres = db_first_entry(table, 0, NULL); + if (dbres == 0) { + break; + } else { + nis_error err; + int rd; + + err = map_db_status_to_nis_status( + dbres->status); + if (err == NIS_SUCCESS && + tsd->ldapStat == LDAP_SUCCESS && + tsd->nisPlusStat == NIS_SUCCESS) + break; + ULOCK_TABLE(name, WR, name); + rd = retrieveDone(&refreshRetry, + &retrieveRetry, + tsd->nisPlusStat, + tsd->ldapStat, &err); + LOCK_TABLE(name, WR, err, err, name); + if (rd) + break; + } + free_db_result(dbres); + } + } + + if (dbres == 0 || dbres->status == DB_MEMORY_LIMIT) { + free_db_result(dbres); + ULOCK_TABLE(name, WR, name); + return (NIS_NOMEMORY); + } else if (dbres->status == DB_SUCCESS) { + free(dbres->nextinfo.db_next_desc_val); /* cookie */ + free_db_result(dbres); + ULOCK_TABLE(name, WR, name); + return (NIS_NOTEMPTY); + } else if (dbres->status == DB_NOTFOUND) { + if ((res = db_destroy(name)) != NIS_SUCCESS) + syslog(LOG_WARNING, + "__db_remove: Unable to destroy table %s: %s.", + table, nis_sperrno(res)); + else + table_deleted = 1; + } else { + /* + * POLICY : What should we do, remove the object? + * or abort() because the database is + * inconsistent. + * ANSWER : Notify the system operator, and continue + * to remove the table object. + */ + syslog(LOG_ERR, + "__db_remove: table %s not in dictionary (err=%d)", + table, dbres->status); + if ((res = db_destroy(name)) != NIS_SUCCESS) + syslog(LOG_WARNING, + "__db_remove: Unable to destroy table %s: %s.", + table, nis_sperrno(res)); + else + table_deleted = 1; + } + free_db_result(dbres); + } + + table = internal_table_name(nis_domain_of(name), tblbuf); + if (! table) { + ULOCK_TABLE(name, WR, name); + return (NIS_BADNAME); + } + + attr.zattr_ndx = "name"; + attr.ZAVAL = nis_leaf_of(name); + attr.ZALEN = strlen(attr.ZAVAL)+1; + + { + nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd(); + __nisdb_retry_t storeRetry = ldapDBTableMapping. + storeErrorRetry; + + if (tsd == 0) { + logmsg(MSG_NOMEM, LOG_ERR, + "%s: No memory for TSD; unable to get full status for DB operation", + myself); + tsd = &fallback; + } + + while (1) { + /* Establish default status (success) */ + tsd->nisPlusStat = NIS_SUCCESS; + tsd->ldapStat = LDAP_SUCCESS; + dbres = db_remove_entry(table, 1, &attr); + if (dbres == 0) { + res = NIS_NOMEMORY; + break; + } else { + int rd; + + res = map_db_status_to_nis_status(dbres-> + status); + if (res == NIS_SUCCESS && + tsd->ldapStat == LDAP_SUCCESS && + tsd->nisPlusStat == NIS_SUCCESS) + break; + ULOCK_TABLE(name, WR, name); + rd = storeDone(&storeRetry, tsd->nisPlusStat, + tsd->ldapStat, &res); + LOCK_TABLE(name, WR, res, res, name); + if (rd) + break; + } + free_db_result(dbres); + } + } + + switch (res) { + case NIS_NOMEMORY: + case NIS_NOFILESPACE: + case NIS_SYSTEMERROR: + case NIS_UNAVAIL: + break; /* these are expected */ + case NIS_SUCCESS: + /* Flush various cached objects */ + switch __type_of(obj) { + case NIS_TABLE_OBJ: + flush_tablecache(name); + break; + case NIS_DIRECTORY_OBJ: + flush_dircache(name, (directory_obj *)NULL); + break; + case NIS_GROUP_OBJ: + flush_groupcache(name); + break; + default: + break; + } + (void) mark_for_sync(table); + break; + default: + syslog(LOG_ERR, + "__db_remove: unexpected result from database %d", + dbres->status); + break; + } + + free_db_result(dbres); + if (res == NIS_SUCCESS) { + multival_invalidate(obj); + } + ULOCK_TABLE_REMCOND(name, WR, table_deleted && res == NIS_SUCCESS, + name); + return (res); +} + +nis_db_result * +db_remove( + nis_name name, /* Name of object to remove */ + nis_object *obj, /* Its type (for deleting tables) */ + ulong_t xid_time) /* Time of "this" transaction */ +{ +#define res __nis_get_tsd()->db_remove_res + log_entry le; /* A log entry */ + + memset((char *)&res, 0, sizeof (res)); + memset((char *)&le, 0, sizeof (le)); + if (verbose) + syslog(LOG_INFO, "db_remove: removing %s from the namespace", + name); + LOCK_TABLE(name, WR, &res, res.status, name); + + le.le_time = xid_time; + le.le_type = REM_NAME; + le.le_name = name; + le.le_object = *obj; + add_update(&le); + __start_clock(1); + res.status = __db_remove(name, obj); + res.ticks = __stop_clock(1); + if (verbose || (res.status != NIS_SUCCESS)) + syslog(LOG_INFO, "db_remove: returning %s", + nis_sperrno(res.status)); + ULOCK_TABLE(name, WR, name); + return (&res); +} + +#undef res + +static nis_db_list_result __no_memory_db_list_result = { + NIS_NOMEMORY, + 0, 0, 0 +}; + +/* + * db_list(table, numattrs, attrs) + * + * function to call the database list function. When called we know that + * object is "readable" by the principal. + * + */ + +static nis_db_list_result * +db_list_x( + nis_name name, /* Table we are listing */ + int na, /* Number of attributes */ + nis_attr *a, /* An array of attributes */ + ulong_t flags) +{ + int i; + nis_error err; + char *table; /* internal table name */ + char tblbuf[NIS_MAXNAMELEN * 2]; /* table path buf */ + db_result *dbres; + nis_db_list_result *res; + int got; + int nm = 0; + nis_object *o; + nis_object *tobj; + nis_db_result *tres; + char *myself = "db_list_x"; + + res = (nis_db_list_result *)XCALLOC(1, sizeof (nis_db_list_result)); + if ((flags & FN_NORAGS) == 0) { + if (res == 0) + return (&__no_memory_db_list_result); + add_cleanup(free_db_list, (void *)res, "db_list result"); + } + got = 0; + table = internal_table_name(name, tblbuf); + if (! table) { + res->status = NIS_BADNAME; + return (res); + } + + if (verbose) + syslog(LOG_INFO, "db_list: listing %s", table); + + LOCK_TABLE(name, RO, res, res->status, name); + + __start_clock(1); + tres = db_lookup(name); + if (tres == NULL) { + res->ticks = __stop_clock(1); + ULOCK_TABLE(name, RO, name); + res->status = NIS_NOMEMORY; + res->numo = 0; + res->objs = NULL; + return (res); + } + if (tres->status == NIS_SUCCESS) { + tobj = tres->obj; + } else if (tres->status == NIS_NOSUCHTABLE) { + /* this happens on top directory in server's database */ + tobj = NULL; + } else { + res->ticks = __stop_clock(1); + ULOCK_TABLE(name, RO, name); + res->status = tres->status; + res->numo = 0; + res->objs = NULL; + return (res); + } + + /* check for multival searches */ + if (tobj) { + err = multival_attr(tobj, a, &na, &nm); + if (err != NIS_SUCCESS) { + res->ticks = __stop_clock(1); + ULOCK_TABLE(name, RO, name); + res->status = err; + return (res); + } + } + + /* + * If we have regular attributes or if we have no attributes + * at all (na == 0 && nm == 0), search the normal way. + */ + if (na || nm == 0) { + { + nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd(); + __nisdb_retry_t refreshRetry = ldapDBTableMapping. + refreshErrorRetry; + __nisdb_retry_t retrieveRetry = ldapDBTableMapping. + retrieveErrorRetry; + + if (tsd == 0) { + logmsg(MSG_NOMEM, LOG_ERR, + "%s: No memory for TSD; unable to get full status for DB operation", + myself); + tsd = &fallback; + } + + while (1) { + /* Establish default status (success) */ + tsd->nisPlusStat = NIS_SUCCESS; + tsd->ldapStat = LDAP_SUCCESS; + dbres = db_list_entries(table, na, a); + if (dbres == 0) { + res->status = NIS_NOMEMORY; + break; + } else { + int rd; + + res->status = + map_db_status_to_nis_status(dbres->status); + if (res->status == NIS_SUCCESS && + tsd->ldapStat == + LDAP_SUCCESS && + tsd->nisPlusStat == + NIS_SUCCESS) + break; + ULOCK_TABLE(name, RO, name); + rd = retrieveDone(&refreshRetry, + &retrieveRetry, + tsd->nisPlusStat, + tsd->ldapStat, &res->status); + LOCK_TABLE(name, RO, res, res->status, + name); + if (rd) + break; + } + free_db_result(dbres); + } + } + + res->ticks = __stop_clock(1); + /* Process the entries into "objects" */ + switch (res->status) { + case NIS_NOMEMORY: + break; + case NIS_SUCCESS: + case NIS_CACHEEXPIRED: + /* Convert from database format to NIS+ object format */ + res->objs = cvt2object(name, dbres->objects.objects_val, + dbres->objects.objects_len, &got, 1); + if (got > 0) { + res->numo = got; + res->status = NIS_SUCCESS; + } else { + res->numo = 0; + res->status = NIS_NOTFOUND; + res->objs = NULL; + } + break; + case NIS_NOTFOUND: + case NIS_TRYAGAIN: + case NIS_UNAVAIL: + case NIS_NOSUCHNAME: + res->numo = 0; + res->objs = NULL; + break; + default: + strcpy(tblbuf, "["); + for (i = 0; i < na; i++) { + if (i != 0) + strcat(tblbuf, ","); + strcat(tblbuf, a[i].zattr_ndx); + strcat(tblbuf, "="); + strncat(tblbuf, + a[i].zattr_val.zattr_val_val, + a[i].zattr_val.zattr_val_len); + } + strcat(tblbuf, "],"); + strcat(tblbuf, name); + syslog(LOG_ERR, + "Database search failed on %s, status = %d", + tblbuf, dbres->status); + break; + } + if (verbose) + syslog(LOG_INFO, + "db_list: returning status = %d, entries = %d", + res->status, got); + free_db_result(dbres); + } else { + res->ticks = __stop_clock(1); + } + + /* + * If we have multival attributes and regular attributes, + * filter out the objects gotten above that don't match + * on the multival columns. If there were no regular + * attributes, then list based only on the multival columns. + */ + if (nm) { + if (na) { + for (i = 0; i < res->numo; i++) { + o = res->objs[i].o; + if (multival_filter(tobj, nm, a + na, o)) { + nis_destroy_object(o); + res->objs[i].o = NULL; + } + } + } else { + /* na = 0 */ + multival_list(tobj, nm, a, res); + } + } + + ULOCK_TABLE(name, RO, name); + return (res); +} + +nis_db_list_result * +db_list( + nis_name name, /* Table we are listing */ + int na, /* Number of attributes */ + nis_attr *a) /* An array of attributes */ +{ + return (db_list_x(name, na, a, 0)); +} + +nis_db_list_result * +db_list_flags( + nis_name name, /* Table we are listing */ + int na, /* Number of attributes */ + nis_attr *a, /* An array of attributes */ + ulong_t flags) +{ + return (db_list_x(name, na, a, flags)); +} + +/* + * This function has to release all of the components that were allocated + * by db_list above. + */ +void +free_db_list(nis_db_list_result *list) +{ + int i; + + if (list == NULL) + return; + for (i = 0; i < list->numo; i++) { + if (list->objs[i].o) + nis_destroy_object(list->objs[i].o); + } + if ((list->objs) && (list->numo)) + XFREE(list->objs); /* Free the entire array */ + free(list); +} + +static nis_fn_result __no_memory_fn_result = { + NIS_NOMEMORY, + 0, {0}, 0 +}; + +/* + * nis_fn_result *db_firstib(name) + * + * Get the "first" entry from a table. Note this function returns an opaque + * "cookie" that has what ever state the underlying database needs to + * find the next entry in a chain of entries. Since it is specific to the + * underlying database it is opaque to this interface. + */ +nis_fn_result * +db_firstib( + nis_name name, /* Table name */ + int na, /* Number of attributes */ + nis_attr *a, /* Attribute list */ + int flags, /* Mangle objects into entries */ + char *table) +{ + db_result *dbres; + obj_list *olist; + int got; + char tblbuf[NIS_MAXNAMELEN * 2]; + nis_fn_result *res; + char *myself = "db_firstib"; + + res = (nis_fn_result *)XCALLOC(1, sizeof (nis_fn_result)); + if ((flags & FN_NORAGS) == 0) { + if (res == 0) + return (&__no_memory_fn_result); + add_cleanup((void (*)())XFREE, (char *)res, "fn (first) res"); + } + if (! table) + table = internal_table_name(name, tblbuf); + if (! table) { + res->status = NIS_BADNAME; + return (res); + } + + if (verbose) + syslog(LOG_INFO, "db_firstib: Fetching first entry from %s", + table); + LOCK_TABLE(name, RO, res, res->status, name); + __start_clock(1); + + { + nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd(); + __nisdb_retry_t refreshRetry = ldapDBTableMapping. + refreshErrorRetry; + __nisdb_retry_t retrieveRetry = ldapDBTableMapping. + retrieveErrorRetry; + + if (tsd == 0) { + logmsg(MSG_NOMEM, LOG_ERR, + "%s: No memory for TSD; unable to get full status for DB operation", + myself); + tsd = &fallback; + } + + while (1) { + /* Establish default status (success) */ + tsd->nisPlusStat = NIS_SUCCESS; + tsd->ldapStat = LDAP_SUCCESS; + dbres = db_first_entry(table, na, a); + if (dbres == 0) { + break; + } else { + int rd; + + res->status = map_db_status_to_nis_status( + dbres->status); + if (res->status == NIS_SUCCESS && + tsd->ldapStat == LDAP_SUCCESS && + tsd->nisPlusStat == NIS_SUCCESS) + break; + ULOCK_TABLE(name, RO, name); + rd = retrieveDone(&refreshRetry, + &retrieveRetry, + tsd->nisPlusStat, + tsd->ldapStat, &res->status); + LOCK_TABLE(name, RO, res, res->status, name); + if (rd) + break; + } + free_db_result(dbres); + } + } + + switch (res->status) { + case NIS_NOMEMORY: + case NIS_NOTFOUND: + case NIS_TRYAGAIN: + case NIS_UNAVAIL: + case NIS_NOSUCHNAME: + break; /* expected */ + case NIS_BADREQUEST: + if ((flags & FN_NOERROR) == 0) + syslog(LOG_ERR, + "db_firstib: Table: '%s', Bad Attribute", + name); + break; + case NIS_NOSUCHTABLE: + if ((flags & FN_NOERROR) == 0) + syslog(LOG_ERR, + "db_firstib: Missing table '%s'", name); + break; + case NIS_SUCCESS: + case NIS_CACHEEXPIRED: + /* ASSERT(dbres->objects.objects_len == 1); */ + /* Convert the entry into a NIS+ object */ + olist = cvt2object(name, dbres->objects.objects_val, 1, &got, + (FN_MANGLE & flags)); + if ((olist == NULL) || (got > 1)) { + syslog(LOG_ERR, "db_firstib: Database is corrupt."); + res->status = NIS_SYSTEMERROR; + } else { + res->obj = olist->o; /* Now that's nice and clear! */ + if ((flags & FN_NORAGS) == 0) + add_cleanup(nis_destroy_object, + (char *)res->obj, "firstib object."); + XFREE(olist); /* free list struct but not obj in it */ + } + /* Now clone the nextinfo cookie */ + res->cookie.n_len = dbres->nextinfo.db_next_desc_len; + res->cookie.n_bytes = (char *)XMALLOC(res->cookie.n_len); + memcpy(res->cookie.n_bytes, dbres->nextinfo.db_next_desc_val, + dbres->nextinfo.db_next_desc_len); + free(dbres->nextinfo.db_next_desc_val); + dbres->nextinfo.db_next_desc_val = 0; + if (res->obj == NULL) { + syslog(LOG_ERR, "db_firstib: Object not found."); + res->status = NIS_SYSTEMERROR; + } + break; + + default: + if ((flags & FN_NOERROR) == 0) + syslog(LOG_ERR, + "db_firstib: Unexpected database result %d.", + dbres->status); + break; + } + /* Don't need this anymore */ + free_db_result(dbres); + if (verbose) + syslog(LOG_INFO, "db_firstib: returning status of %s", + nis_sperrno(res->status)); + ULOCK_TABLE(name, RO, name); + return (res); /* Return it finally */ +} + +/* + * nis_fn_result *db_nextib(name, cookie) + * + * Get the "next" entry from a table. + */ +nis_fn_result * +db_nextib( + nis_name name, + netobj *cookie, + int flags, + char *table) +{ + nis_fn_result *res; + db_result *dbres; + obj_list *olist; + int got; + char tblbuf[NIS_MAXNAMELEN * 2]; + char *myself = "db_nextib"; + + res = (nis_fn_result *)XCALLOC(1, sizeof (nis_fn_result)); + if ((flags & FN_NORAGS) == 0) { + if (res == 0) + return (&__no_memory_fn_result); + add_cleanup((void (*)())XFREE, (char *)res, "fn (next) res"); + } + if (! table) + table = internal_table_name(name, tblbuf); + if (! table) { + res->status = NIS_BADNAME; + return (res); + } + if (verbose) + syslog(LOG_INFO, "db_nextib: Fetching entry from %s", table); + __start_clock(1); + LOCK_TABLE(name, RO, res, res->status, name); + + { + nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd(); + __nisdb_retry_t refreshRetry = ldapDBTableMapping. + refreshErrorRetry; + __nisdb_retry_t retrieveRetry = ldapDBTableMapping. + retrieveErrorRetry; + + if (tsd == 0) { + logmsg(MSG_NOMEM, LOG_ERR, + "%s: No memory for TSD; unable to get full status for DB operation", + myself); + tsd = &fallback; + } + + while (1) { + /* Establish default status (success) */ + tsd->nisPlusStat = NIS_SUCCESS; + tsd->ldapStat = LDAP_SUCCESS; + dbres = db_next_entry(table, (db_next_desc *)cookie); + if (dbres == 0) { + break; + } else { + int rd; + + res->status = map_db_status_to_nis_status( + dbres->status); + if (res->status == NIS_SUCCESS && + tsd->ldapStat == LDAP_SUCCESS && + tsd->nisPlusStat == NIS_SUCCESS) + break; + ULOCK_TABLE(name, RO, name); + rd = retrieveDone(&refreshRetry, + &retrieveRetry, + tsd->nisPlusStat, + tsd->ldapStat, &res->status); + LOCK_TABLE(name, RO, res, res->status, name); + if (rd) + break; + } + free_db_result(dbres); + } + } + + switch (res->status) { + case NIS_NOMEMORY: + case NIS_NOTFOUND: + case NIS_TRYAGAIN: + case NIS_UNAVAIL: + case NIS_NOSUCHNAME: + break; + case NIS_BADREQUEST: + if ((flags & FN_NOERROR) == 0) + syslog(LOG_ERR, + "db_nextib: Bad Attribute in Table: '%s'", + name); + break; + case NIS_NOSUCHTABLE: + if ((flags & FN_NOERROR) == 0) + syslog(LOG_ERR, + "db_nextib: Missing table object '%s'", + name); + break; + case NIS_SUCCESS: + case NIS_CACHEEXPIRED: + /* ASSERT(dbres->objects.objects_len == 1); */ + olist = cvt2object(name, dbres->objects.objects_val, 1, &got, + (flags & FN_MANGLE)); + if ((olist == NULL) || (got > 1)) { + syslog(LOG_ERR, "db_nextib: Database is corrupt."); + res->status = NIS_SYSTEMERROR; + } else { + res->obj = olist->o; + if ((flags & FN_NORAGS) == 0) + add_cleanup(nis_destroy_object, + (char *)res->obj, "nextib object"); + XFREE(olist); + } + /* Now clone the nextinfo cookie */ + res->cookie.n_len = dbres->nextinfo.db_next_desc_len; + res->cookie.n_bytes = (char *)XMALLOC(res->cookie.n_len); + memcpy(res->cookie.n_bytes, dbres->nextinfo.db_next_desc_val, + dbres->nextinfo.db_next_desc_len); + free(dbres->nextinfo.db_next_desc_val); + if (res->obj == NULL) + res->status = NIS_SYSTEMERROR; + break; + default: + if ((flags & FN_NOERROR) == 0) + syslog(LOG_ERR, + "db_nextib: Unexpected database result %d", + dbres->status); + break; + } + /* Don't need this anymore */ + free_db_result(dbres); + if (verbose) + syslog(LOG_INFO, "db_nextib: returning status of %s", + nis_sperrno(res->status)); + ULOCK_TABLE(name, RO, name); + return (res); /* Return it finally */ +} + +/* + * db_flush() + * + * Flush any pending "next" entries from a list returned by firstib. + */ +void +db_flush( + nis_name name, /* table name */ + netobj *cookie) /* The next_desc */ +{ + char *table; + db_result *dbres; + char tblbuf[NIS_MAXNAMELEN * 2]; + + table = internal_table_name(name, tblbuf); + if (! table) + return; + if (verbose) + syslog(LOG_INFO, "db_flush: Flushing queued entries from %s", + table); + { + int trylock = 1; + if (__nis_lock_db_table(name, RO, &trylock, name) == 0) { + return; + } + } + + dbres = db_reset_next_entry(table, (db_next_desc *)cookie); + if (dbres && dbres->status != DB_SUCCESS) { + syslog(LOG_ERR, "Unable to flush '%s'", table); + } + free_db_result(dbres); + ULOCK_TABLE(name, RO, name); +} + +/* + * Add an entry to a table, we assume we've already sanitized the + * data. + */ +static nis_error +__db_addib_x( + nis_name name, /* Name of the table. */ + int numattr, /* Number of attributes */ + nis_attr *attrs, /* array of attributes */ + nis_object *obj, /* Entry to add to the table */ + int skiplog, /* if true, don't log action */ + int nosync) +{ + entry_obj eo; /* our "fake" entry */ + entry_col *oec, /* our objects columns */ + *ec; /* our objects columns */ + int i, mc, bufsize; /* counters */ + db_result *dbres; + XDR xdrs; /* XDR stream */ + uchar_t *buf; + char *table; + nis_error res; + struct table_item *te; /* Table cache entry */ + struct nis_object *tobj; /* Table nis_object */ + char tblbuf[NIS_MAXNAMELEN * 2]; /* table path buf */ + char *myself = "__db_addib_x"; + + /* Get the number of columns and the pointer to their data */ + mc = obj->EN_data.en_cols.en_cols_len; + oec = obj->EN_data.en_cols.en_cols_val; + + ec = __get_entry_col(mc+1); /* get some temp storage */ + + if (ec == NULL) { + return (NIS_NOMEMORY); + } + + table = internal_table_name(name, tblbuf); + if (! table) + return (NIS_BADNAME); + if (verbose) + syslog(LOG_INFO, "__db_addib: Adding an entry to table %s", + table); + + LOCK_TABLE(name, WR, res, res, name); + te = __nis_find_item_mt(name, table_cache, RO, 0); + if (te == NULL) { + /* Do a db_lookup() so that cache is populated */ + nis_db_result *dbres; + + if (((dbres = db_lookup_deferred(name, FALSE)) == NULL) || + (dbres->status != NIS_SUCCESS)) { + ULOCK_TABLE(name, WR, name); + syslog(LOG_ERR, + "__db_addib: could not find table object for %s", + name); + return (NIS_NOSUCHTABLE); /* XXX: Error number */ + } + tobj = dbres->obj; + /* dbres is freed automatically during cleanup */ + } else + tobj = te->ibobj; + + /* Build up our temporary entry object */ + eo.en_type = NULL; /* used by cvt2obj - denotes non IN_DIRECTORY */ + eo.en_cols.en_cols_len = mc + 1; + eo.en_cols.en_cols_val = ec; + + /* Copy the entry value pointers, offset by 1 */ + for (i = 0; i < mc; i++) + ec[i+1] = oec[i]; + + /* + * To prevent the XDR function from making a copy of + * the entry columns, we set the columns structure to + * 0 (ie no column data) + */ + obj->EN_data.en_cols.en_cols_len = 0; /* Neuter it */ + obj->EN_data.en_cols.en_cols_val = NULL; /* Neuter it */ + + /* Make a fetus object from a FULL object */ + bufsize = xdr_sizeof(xdr_nis_object, obj); + buf = __get_xdr_buf(bufsize); + if (buf == NULL) { + if (te != 0) + __nis_release_item(te, table_cache, RO); + ULOCK_TABLE(name, WR, name); + syslog(LOG_ERR, "__db_addib: out of memory!"); + return (NIS_NOMEMORY); + } + + xdrmem_create(&xdrs, (char *)buf, bufsize, XDR_ENCODE); + if (! xdr_nis_fetus_object(&xdrs, obj, tobj)) { + if (te != 0) + __nis_release_item(te, table_cache, RO); + ULOCK_TABLE(name, WR, name); + syslog(LOG_ERR, "__db_addib: Failure to encode entry."); + return (NIS_SYSTEMERROR); + } + + /* Un-neuter it so that it can be properly freed */ + obj->EN_data.en_cols.en_cols_len = mc; + obj->EN_data.en_cols.en_cols_val = oec; + ec[0].ENVAL = (char *)buf; /* Point to encoded one */ + ec[0].ENLEN = xdr_getpos(&xdrs); + ec[0].ec_flags = EN_BINARY+EN_XDR; + + { + nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd(); + __nisdb_retry_t storeRetry = ldapDBTableMapping. + storeErrorRetry; + + if (tsd == 0) { + logmsg(MSG_NOMEM, LOG_ERR, + "%s: No memory for TSD; unable to get full status for DB operation", + myself); + tsd = &fallback; + } + + while (1) { + /* Establish default status (success) */ + tsd->nisPlusStat = NIS_SUCCESS; + tsd->ldapStat = LDAP_SUCCESS; + + if (skiplog) + dbres = __db_add_entry_nolog(table, numattr, + attrs, &eo); + else if (nosync) + dbres = __db_add_entry_nosync(table, numattr, + attrs, &eo); + else + dbres = db_add_entry(table, numattr, + attrs, &eo); + if (dbres == 0) { + res = NIS_NOMEMORY; + break; + } else { + int rd; + + res = map_db_status_to_nis_status(dbres-> + status); + if (res == NIS_SUCCESS && + tsd->ldapStat == LDAP_SUCCESS && + tsd->nisPlusStat == NIS_SUCCESS) + break; + /* Unlock while we (possibly) sleep */ + ULOCK_TABLE(name, WR, name); + rd = storeDone(&storeRetry, tsd->nisPlusStat, + tsd->ldapStat, &res); + LOCK_TABLE(name, WR, res, res, name); + if (rd) + break; + } + free_db_result(dbres); + } + } + + switch (res) { + case NIS_NOMEMORY: + case NIS_NOFILESPACE: + case NIS_SYSTEMERROR: + case NIS_UNAVAIL: + break; + case NIS_SUCCESS: + (void) mark_for_sync(table); + break; + default: + syslog(LOG_ERR, "__db_addib: Unexpected database result %d.", + dbres->status); + break; + } + if (verbose) + syslog(LOG_INFO, "__db_addib: done. (%d)", res); + free_db_result(dbres); + if (res == NIS_SUCCESS) { + multival_invalidate(obj); + } + if (te != 0) + __nis_release_item(te, table_cache, RO); + ULOCK_TABLE(name, WR, name); + return (res); +} + +nis_error +__db_addib( + nis_name name, /* Name of the table. */ + int numattr, /* Number of attributes */ + nis_attr *attrs, /* array of attributes */ + nis_object *obj) /* Entry to add to the table */ +{ + return (__db_addib_x(name, numattr, attrs, obj, 0, 0)); +} + +nis_error +__db_addib_nolog( + nis_name name, /* Name of the table. */ + int numattr, /* Number of attributes */ + nis_attr *attrs, /* array of attributes */ + nis_object *obj) /* Entry to add to the table */ +{ + return (__db_addib_x(name, numattr, attrs, obj, 1, 0)); +} + +nis_error +__db_addib_nosync( + nis_name name, /* Name of the table. */ + int numattr, /* Number of attributes */ + nis_attr *attrs, /* array of attributes */ + nis_object *obj) /* Entry to add to the table */ +{ + return (__db_addib_x(name, numattr, attrs, obj, 0, 1)); +} + +static nis_db_result __no_memory_db_result = {NIS_NOMEMORY, 0, 0}; + +/* + * Add an entry to a table, we assume we've already sanitized the + * data. + */ +nis_db_result * +db_addib( + nis_name name, /* Name of the table. */ + int numattr, /* Number of attributes */ + nis_attr *attrs, /* array of attributes */ + nis_object *obj, /* Entry to add to the table */ + nis_object *tobj) /* Table to add it too. */ +{ + nis_attr *attr_list; + int i, mc, na; + table_col *tc; + entry_col *ec; + nis_db_result *res; + log_entry le; + + /* + * First we create a fully specified entry list, with a + * set of attribute/values to go with it. + */ + res = (nis_db_result *)XCALLOC(1, sizeof (nis_db_result)); + if (res == 0) + return (&__no_memory_db_result); + add_cleanup((void (*)())XFREE, (char *)res, "db_addib result"); + memset((char *)&le, 0, sizeof (le)); + LOCK_TABLE(name, WR, res, res->status, name); + mc = tobj->TA_data.ta_cols.ta_cols_len; + tc = tobj->TA_data.ta_cols.ta_cols_val; + ec = obj->EN_data.en_cols.en_cols_val; + attr_list = __get_attrs(mc); + if (attr_list == NULL) { + ULOCK_TABLE(name, WR, name); + res->status = NIS_NOMEMORY; + return (res); + } + for (i = 0, na = 0; i < mc; i++) { + if ((tc[i].tc_flags & TA_SEARCHABLE) != 0) { + attr_list[na].zattr_ndx = tc[i].tc_name; + attr_list[na].ZAVAL = ec[i].ENVAL; + attr_list[na].ZALEN = ec[i].ENLEN; + na++; + } + } + le.le_name = name; + le.le_object = *obj; + le.le_attrs.le_attrs_len = na; + le.le_attrs.le_attrs_val = attr_list; + le.le_type = ADD_IBASE; + le.le_time = obj->zo_oid.mtime; + add_update(&le); + __start_clock(1); + res->status = __db_addib(name, na, attr_list, obj); + res->ticks = __stop_clock(1); + if (res->status != NIS_SUCCESS) + syslog(LOG_ERR, "db_addib: unable to add entry to %s", name); + ULOCK_TABLE(name, WR, name); + return (res); +} + +nis_error +__db_remib_x( + nis_name name, + int nattr, + nis_attr *attrs, + int nosync) +{ + nis_error res; + db_result *dbres; + char *table; + char tblbuf[NIS_MAXNAMELEN * 2]; + char *myself = "__db_remib_x"; + + table = internal_table_name(name, tblbuf); + if (! table) + return (NIS_BADNAME); + + if (verbose) + syslog(LOG_INFO, + "__db_remib: Removing an entry from table %s", table); + + LOCK_TABLE(name, WR, res, res, name); + + { + nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd(); + __nisdb_retry_t storeRetry = ldapDBTableMapping. + storeErrorRetry; + + if (tsd == 0) { + logmsg(MSG_NOMEM, LOG_ERR, + "%s: No memory for TSD; unable to get full status for DB operation", + myself); + tsd = &fallback; + } + + while (1) { + /* Establish default status (success) */ + tsd->nisPlusStat = NIS_SUCCESS; + tsd->ldapStat = LDAP_SUCCESS; + + if (nosync) + dbres = __db_remove_entry_nosync(table, nattr, + attrs); + else + dbres = db_remove_entry(table, nattr, attrs); + if (dbres == 0) { + res = NIS_NOMEMORY; + break; + } else { + int rd; + + res = map_db_status_to_nis_status(dbres-> + status); + if (res == NIS_SUCCESS && + tsd->ldapStat == LDAP_SUCCESS && + tsd->nisPlusStat == NIS_SUCCESS) + break; + /* Unlock while we (possibly) sleep */ + ULOCK_TABLE(name, WR, name); + rd = storeDone(&storeRetry, tsd->nisPlusStat, + tsd->ldapStat, &res); + LOCK_TABLE(name, WR, res, res, name); + if (rd) + break; + } + free_db_result(dbres); + } + } + + switch (res) { + case NIS_NOMEMORY: + case NIS_NOFILESPACE: + case NIS_SYSTEMERROR: + case NIS_UNAVAIL: + break; + case NIS_SUCCESS: + (void) mark_for_sync(table); + break; + case NIS_NOTFOUND: + /* this occurs when applying log updates */ + res = NIS_SUCCESS; + break; + default: + syslog(LOG_ERR, "__db_remib: Unexpected database result %d.", + dbres->status); + break; + } + if (verbose) + syslog(LOG_INFO, "__db_remib: done. (%d)", res); + free_db_result(dbres); + if (res == NIS_SUCCESS) { + nis_db_result *tres; + + tres = db_lookup_deferred(name, FALSE); + multival_invalidate(tres->obj); + } + ULOCK_TABLE(name, WR, name); + return (res); +} + +nis_error +__db_remib( + nis_name name, + int nattr, + nis_attr *attrs) +{ + return (__db_remib_x(name, nattr, attrs, 0)); +} + +nis_error +__db_remib_nosync( + nis_name name, + int nattr, + nis_attr *attrs) +{ + return (__db_remib_x(name, nattr, attrs, 1)); +} + +/* + * The remove operation. Since it can remove multiple entries we pass it + * the list for the log entries. + */ +nis_db_result * +db_remib( + nis_name name, + int nattrs, + nis_attr *attrs, + obj_list *olist, + int numo, + nis_object *tobj, + ulong_t xid_time) +{ + nis_attr *attr_list; + int i, j, mc, na; + table_col *tc; + entry_col *ec; + nis_db_result *res; + log_entry le; + int *amap; + + /* + * First we create a fully specified entry list, with a + * set of attribute/values to go with it. + */ + res = (nis_db_result *)XCALLOC(1, sizeof (nis_db_result)); + if (res == 0) + return (&__no_memory_db_result); + add_cleanup((void (*)())XFREE, (char *)res, "db_remib result"); + LOCK_TABLE(name, WR, res, res->status, name); + memset((char *)&le, 0, sizeof (le)); + mc = tobj->TA_data.ta_cols.ta_cols_len; + tc = tobj->TA_data.ta_cols.ta_cols_val; + na = 0; /* Actual attrs */ + attr_list = __get_attrs(mc); + /* Cheat and allocate a "static" array */ + amap = (int *)__get_xdr_buf(sizeof (int) * mc); + + if (attr_list == NULL) { + ULOCK_TABLE(name, WR, name); + res->status = NIS_NOMEMORY; + return (res); + } + for (i = 0, na = 0; i < mc; i++) { + if ((tc[i].tc_flags & TA_SEARCHABLE) != 0) { + attr_list[na].zattr_ndx = tc[i].tc_name; + amap[na] = i; + na++; + } + } + + /* Add log updates for all of the entries that will be removed */ + for (i = 0; i < numo; i++) { + ec = olist[i].o->EN_data.en_cols.en_cols_val; + for (j = 0; j < na; j++) { + attr_list[j].ZAVAL = ec[amap[j]].ENVAL; + attr_list[j].ZALEN = ec[amap[j]].ENLEN; + } + le.le_name = name; + le.le_object = *(olist[i].o); + le.le_attrs.le_attrs_len = na; + le.le_attrs.le_attrs_val = attr_list; + le.le_type = REM_IBASE; + le.le_time = xid_time; + add_update(&le); + } + __start_clock(1); + res->status = __db_remib(name, nattrs, attrs); + res->ticks = __stop_clock(1); + if (res->status != NIS_SUCCESS) + syslog(LOG_ERR, "db_remib: unable to remove entry from %s", + name); + ULOCK_TABLE(name, WR, name); + return (res); +} + + +/* + * __create_table() + * + * Create the underlying database table that supports the current database. + */ +static nis_error +__create_table( + char *table, + nis_object *obj) +{ + table_obj fake, *t; /* Fake table object here. */ + table_col *tc = NULL; /* Fake columns data */ + int i; + + /* Assume locking done by caller */ + + t = &(obj->TA_data); + tc = __get_table_col(t->ta_cols.ta_cols_len+1); + if (tc == NULL) + return (NIS_NOMEMORY); + + for (i = 0; i < t->ta_cols.ta_cols_len; i++) { + if (t->ta_cols.ta_cols_val[i].tc_flags & TA_SEARCHABLE) + break; + } + if (i == t->ta_cols.ta_cols_len) { + if (verbose) + syslog(LOG_INFO, + "Cannot create table: %s: no searchable columns.", table); + return (NIS_BADOBJECT); + } + + /* Copy the important parts of the table structure over */ + fake = obj->TA_data; + /* Now shift the columns right by 1 */ + for (i = 0; i < fake.ta_cols.ta_cols_len; i++) { + tc[i+1] = fake.ta_cols.ta_cols_val[i]; + } + tc[0].tc_name = NULL; /* NO name for the entry column */ + tc[0].tc_flags = TA_XDR+TA_BINARY; + tc[0].tc_rights = 0; /* only the server sees them */ + fake.ta_cols.ta_cols_len++; /* Add one to this */ + fake.ta_cols.ta_cols_val = tc; /* fixup this */ + return (db_create(table, &fake)); +} + +static cp_result mem_err = {NIS_NOMEMORY, 0, 0}; + +/* + * do_checkpoint + * + * This function checkpoints the table named, or all tables + * that the server has if no table is named. + * + * NB: This can be time consuming so the server should fork + * a readonly child to handle requests while we're out here. + * + * This function returns NIS_SUCCESS if all tables were + * checkpointed and return NIS_PARTIAL otherwise. + */ +static cp_result * +do_checkpoint( + nis_name name) /* name of directory to checkpoint */ +{ + int status; + cp_result *res; + char *table; + char tblbuf[NIS_MAXNAMELEN * 2]; + + res = (cp_result *) XCALLOC(1, sizeof (cp_result)); + if (! res) + return (&mem_err); + add_cleanup((void (*)())XFREE, (char *)res, "do_chkpt result"); + + if (name == NULL) + table = NULL; + else + table = internal_table_name(name, tblbuf); + if (verbose) + syslog(LOG_INFO, "Checkpointing table '%s'", + table ? table : "(all)"); + if (table == 0) + name = "."; + LOCK_TABLE(name, WR, res, res->cp_status, name); + + __start_clock(1); + status = db_checkpoint(table); + res->cp_dticks = __stop_clock(1); + if (status != DB_SUCCESS) { + syslog(LOG_ERR, + "db_checkpoint: Unable to checkpoint table '%s' because %s", + (table ? table : "(all)"), + nis_sperrno(map_db_status_to_nis_status(status))); + res->cp_status = NIS_PARTIAL; + } else + res->cp_status = NIS_SUCCESS; + + ULOCK_TABLE(name, WR, name); + return (res); +} + +/* + * do_checkpoint_dir + * + * This function checkpoints all of the tables in a named directory + * and the directory itself. + * This is accomplished by iterating over the directory and then + * checkpointing anything that looks like a table. + * + * NB: This can be time consuming so the server should have forked + * a read only child to handle requests while we're out here. + * + * This function returns returns NIS_SUCCESS if all tables were + * checkpointed and returne NIS_PARTIAL if only some of them were. + */ +static cp_result * +do_checkpoint_dir( + nis_name name) /* name of directory to checkpoint */ +{ + int status, err; + cp_result *res; + char *table; + char tblname[NIS_MAXNAMELEN]; + netobj cookie; + nis_fn_result *fnr; + char tblbuf[NIS_MAXNAMELEN * 2]; + + if (! name) + return (NULL); /* name is required */ + + res = (cp_result *) XCALLOC(1, sizeof (cp_result)); + if (! res) + return (&mem_err); + + add_cleanup((void (*)())XFREE, (char *)res, "do_chkpt result"); + + if (verbose) + syslog(LOG_INFO, "Checkpointing directory '%s'", name); + + err = 0; + fnr = db_firstib(name, 0, NULL, FN_NORAGS, NULL); + cookie = fnr->cookie; + res->cp_dticks = fnr->ticks; + /* First, do directory itself. */ + if (fnr->status == NIS_NOTFOUND || fnr->status == NIS_SUCCESS) { + table = internal_table_name(name, tblbuf); + __start_clock(1); + if (verbose) + syslog(LOG_INFO, "Checkpointing directory '%s'", + table); + LOCK_DIRECTORY(name, WR, res->cp_status, name); + if (res->cp_status == NIS_SUCCESS) { + status = db_checkpoint(table); + res->cp_dticks += __stop_clock(1); + if (status != DB_SUCCESS) { + syslog(LOG_ERR, + "db_checkpoint: Unable to checkpoint '%s' because %s", + (name ? name : "(null)"), + nis_sperrno(map_db_status_to_nis_status( + status))); + err++; + } + } else { + res->cp_dticks += __stop_clock(1); + syslog(LOG_ERR, + "db_checkpoint: Unable to checkpoint '%s' because %s", + (name ? name : "(null)"), res->cp_status); + err++; + } + ULOCK_DIRECTORY(name, WR, name); + } + + /* Do each table or directory within directory. */ + while (fnr->status == NIS_SUCCESS) { + if (__type_of(fnr->obj) == NIS_TABLE_OBJ || + __type_of(fnr->obj) == NIS_DIRECTORY_OBJ) { + sprintf(tblname, "%s.%s", fnr->obj->zo_name, + fnr->obj->zo_domain); + table = internal_table_name(tblname, tblbuf); + __start_clock(1); + if (verbose) + syslog(LOG_INFO, "Checkpointing table '%s'", + table); + LOCK_TABLE_NORET(tblname, WR, res->cp_status, + tblname); + if (res->cp_status == NIS_SUCCESS) { + status = db_checkpoint(table); + res->cp_dticks += __stop_clock(1); + /* + * We ignore DB_BADTABLE errors because we might + * not serve the contents of the directory. + */ + if (status != DB_SUCCESS && + status != DB_BADTABLE) { + syslog(LOG_ERR, + "db_checkpoint: Unable to checkpoint '%s' because %s", + (tblname ? tblname : "(null)"), + nis_sperrno( + map_db_status_to_nis_status( + status))); + err++; + } + ULOCK_TABLE(tblname, WR, name); + } else { + res->cp_dticks += __stop_clock(1); + syslog(LOG_ERR, + "db_checkpoint: Unable to checkpoint '%s' because %s", + (tblname ? tblname : "(null)"), res->cp_status); + err++; + } + } + nis_destroy_object(fnr->obj); + XFREE(fnr); + /* note the call to nextib frees the old cookie! */ + fnr = db_nextib(name, &cookie, FN_NORAGS, NULL); + cookie = fnr->cookie; + } + XFREE(fnr); + if (err) + res->cp_status = NIS_PARTIAL; + else + res->cp_status = NIS_SUCCESS; + return (res); +} + +/* + * nis_checkpoint_svc maintains a list of directories to be + * checkpointed, call do_checkpoint_dir on items on that list. + * Otherwise, call do_checkpoint(NULL) to checkpoint entire database. + */ + +int +checkpoint_db() +{ + ping_item *cp, *nxt; + cp_result *res; + + LOCK_LIST(&checkpoint_list, "checkpoint_db(checkpoint_list)"); + cp = (ping_item *)(checkpoint_list.first); + + /* No directories specified; checkpoint entire database. */ + + if (cp == NULL || checkpoint_all) { + res = do_checkpoint(NULL); + clear_checkpoint_list_nolock(); + checkpoint_all = 0; + /* res is on cleanup list; no need to free. */ + ULOCK_LIST(&checkpoint_list, "checkpoint_db(checkpoint_list)"); + return (1); + } + + for (cp; cp; cp = nxt) { + nxt = (ping_item *)(cp->item.nxt_item); + res = do_checkpoint_dir(cp->item.name); + if (res->cp_status != NIS_SUCCESS) { + syslog(LOG_WARNING, + "checkpoint_db: unable to completely checkpoint %s because %s", + (cp->item.name ? cp->item.name : "(null)"), + nis_sperrno(res->cp_status)); + } + /* ignore status. Can try checkpoint later if not complete. */ + (void) nis_remove_item(cp->item.name, &checkpoint_list); + XFREE(cp->item.name); + if (cp->obj) + nis_destroy_object(cp->obj); + XFREE(cp); + } + ULOCK_LIST(&checkpoint_list, "checkpoint_db(checkpoint_list)"); + return (1); +} + +int +checkpoint_table(char *name) +{ + char *table; + db_status status; + char tblbuf[NIS_MAXNAMELEN * 2]; + int ret = 0; + + { + nis_error dummy; + LOCK_TABLE(name, WR, dummy, ret, name); + } + table = internal_table_name(name, tblbuf); + status = db_checkpoint(table); + if (status != DB_SUCCESS) { + syslog(LOG_ERR, + "checkpoint_table: Unable to checkpoint table %s because %s", + (table ? table : "(null)"), + nis_sperrno(map_db_status_to_nis_status(status))); + } else { + ret = 1; + } + ULOCK_TABLE(name, WR, name); + return (ret); +} + +/* + * Remove all items from checkpoint list. List must be locked by caller. + */ +static int +clear_checkpoint_list_nolock() +{ + ping_item *cp, *nxt; + + for (cp = (ping_item *)(checkpoint_list.first); cp; cp = nxt) { + nxt = (ping_item *)(cp->item.nxt_item); + (void) nis_remove_item(cp->item.name, &checkpoint_list); + XFREE(cp->item.name); + if (cp->obj) + nis_destroy_object(cp->obj); + XFREE(cp); + } + return (1); +} + +int +clear_checkpoint_list(void) { + int ret; + + LOCK_LIST(&checkpoint_list, "clear_checkpoint_list(checkpoint_list)"); + ret = clear_checkpoint_list_nolock(); + ULOCK_LIST(&checkpoint_list, "clear_checkpoint_list(checkpoint_list)"); + + return (ret); +} + +/* + * Returns NIS_SUCCESS if table exists; NIS_NOSUCHTABLE if it does not exist. + * Otherwise, return error returned by database. + */ +nis_error +db_find_table(nis_name name) +{ + char tblbuf[NIS_MAXNAMELEN * 2]; + char *table = internal_table_name(name, tblbuf); + db_status dstatus; + + if (!table) { + return (NIS_BADNAME); + } + + { + nis_error ret; + LOCK_TABLE(name, RO, ret, ret, name); + } + dstatus = db_table_exists(table); + ULOCK_TABLE(name, RO, name); + return (map_db_status_to_nis_status(dstatus)); +} + + +nis_error +db_create(nis_name name, table_obj *obj) +{ + char tblbuf[NIS_MAXNAMELEN * 2]; + char *table = internal_table_name(name, tblbuf); + db_status dstatus; + nis_error err; + char *myself = "db_create"; + + if (! table) { + return (NIS_BADNAME); + } + + LOCK_TABLE(name, WR, err, err, name); + + { + nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd(); + __nisdb_retry_t storeRetry = ldapDBTableMapping. + storeErrorRetry; + + if (tsd == 0) { + logmsg(MSG_NOMEM, LOG_ERR, + "%s: No memory for TSD; unable to get full status for DB operation", + myself); + tsd = &fallback; + } + + while (1) { + int rd; + + /* Establish default status (success) */ + tsd->nisPlusStat = NIS_SUCCESS; + tsd->ldapStat = LDAP_SUCCESS; + dstatus = db_create_table(table, obj); + err = map_db_status_to_nis_status(dstatus); + if (dstatus == DB_SUCCESS && + tsd->ldapStat == LDAP_SUCCESS && + tsd->nisPlusStat == NIS_SUCCESS) + break; + ULOCK_TABLE(name, WR, name); + rd = storeDone(&storeRetry, tsd->nisPlusStat, + tsd->ldapStat, &err); + LOCK_TABLE(name, WR, err, err, name); + if (rd) + break; + } + } + + if (dstatus == DB_SUCCESS) + (void) mark_for_sync(table); + err = map_db_status_to_nis_status(dstatus); + ULOCK_TABLE_REMCOND(name, WR, dstatus != DB_SUCCESS, name); + return (err); +} + +nis_error +db_destroy(nis_name name) +{ + char tblbuf[NIS_MAXNAMELEN * 2]; + char *table = internal_table_name(name, tblbuf); + db_status dstatus; + nis_error err; + char *myself = "db_destroy"; + + if (! table) { + return (NIS_BADNAME); + } + + LOCK_TABLE(name, WR, err, err, name); + + { + nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd(); + __nisdb_retry_t storeRetry = ldapDBTableMapping. + storeErrorRetry; + + if (tsd == 0) { + logmsg(MSG_NOMEM, LOG_ERR, + "%s: No memory for TSD; unable to get full status for DB operation", + myself); + tsd = &fallback; + } + + while (1) { + int rd; + + /* Establish default status (success) */ + tsd->nisPlusStat = NIS_SUCCESS; + tsd->ldapStat = LDAP_SUCCESS; + dstatus = db_destroy_table(table); + err = map_db_status_to_nis_status(dstatus); + if (dstatus == DB_SUCCESS && + tsd->ldapStat == LDAP_SUCCESS && + tsd->nisPlusStat == NIS_SUCCESS) + break; + ULOCK_TABLE(name, WR, name); + rd = storeDone(&storeRetry, tsd->nisPlusStat, + tsd->ldapStat, &err); + LOCK_TABLE(name, WR, err, err, name); + if (rd) + break; + } + } + + if (dstatus == DB_SUCCESS) + (void) mark_for_sync(table); + ULOCK_TABLE_REMCOND(name, WR, dstatus == DB_SUCCESS, name); + return (err); +} + +static int +mark_for_sync(char *table) { + + table_list_entry_t *te; + int ret = 0; + + MUTEXLOCK(table_list, "mark_for_sync(table_list)"); + + for (te = table_list; te != 0; te = te->next) { + if (strcmp(te->table, table) == 0) + break; + } + if (te == 0 && (te = malloc(sizeof (table_list_entry_t)+ + strlen(table)+1)) != 0) { + te->table = (char *)((ulong_t)te + sizeof (*te)); + (void) strcpy(te->table, table); + te->next = table_list; + table_list = te; + } else if (te == 0) { + syslog(LOG_WARNING, + "mark_for_sync: could not add sync entry for \"%s\"\n", table); + ret = ENOMEM; + } + + MUTEXUNLOCK(table_list, "mark_for_sync(table_list)"); + + return (ret); +} + +int +nis_db_sync_log(void) { + + table_list_entry_t *te; + int ret = 0; + db_status stat; + + MUTEXLOCK(table_list, "nis_db_sync_log(table_list)"); + + while ((te = table_list) != 0) { + if ((stat = db_sync_log(te->table)) != DB_SUCCESS) { + if (verbose) + syslog(LOG_INFO, + "nis_db_sync_log: error %d syncing \"%s\"", + stat, te->table); + ret++; + } + table_list = te->next; + free(te); + } + + MUTEXUNLOCK(table_list, "nis_db_sync_log(table_list)"); + + return (ret); +} + +nis_error +db_configure(char *table) { + db_status stat; + + stat = __db_configure(table); + return (map_db_status_to_nis_status(stat)); +} + +/* + * Given an input NIS+ and LDAP status, perform appropriate actions + * according to retrieveError/refreshError. May set '*outStat'. + */ +int +retrieveDone(__nisdb_retry_t *refreshRetry, __nisdb_retry_t *retrieveRetry, + int nisPlusStat, int ldapStat, nis_error *outStat) { + + if (ldapStat == LDAP_NO_SUCH_OBJECT) { + *outStat = NIS_NOTFOUND; + return (1); + } else if (ldapStat != LDAP_SUCCESS) { + /* Tried LDAP, didn't work => retrieve error */ + if (ldapDBTableMapping.retrieveError == use_cached) { + /* Use refresh error actions */ + if ((ldapDBTableMapping.refreshError == + continue_using || + ldapDBTableMapping.refreshError == + continue_using_retry) && + nisPlusStat == NIS_CACHEEXPIRED) { + *outStat = NIS_SUCCESS; + return (1); + } else if (ldapDBTableMapping.refreshError == + continue_using || + ldapDBTableMapping.refreshError == + ref_retry || + ldapDBTableMapping.refreshError == + continue_using_retry) { + return (!__nis_retry_sleep(refreshRetry, 0)); + } else if (ldapDBTableMapping.refreshError == + cache_expired) { + *outStat = NIS_CACHEEXPIRED; + return (1); + } else { + *outStat = NIS_TRYAGAIN; + return (1); + } + } else if (ldapDBTableMapping.retrieveError == ret_retry) { + return (!__nis_retry_sleep(retrieveRetry, 0)); + } else if (ldapDBTableMapping.retrieveError == try_again) { + *outStat = NIS_TRYAGAIN; + return (1); + } else if (ldapDBTableMapping.retrieveError == ret_unavail) { + *outStat = NIS_UNAVAIL; + return (1); + } else if (ldapDBTableMapping.retrieveError == no_such_name) { + *outStat = NIS_NOSUCHNAME; + return (1); + } + } else if (nisPlusStat != NIS_SUCCESS) { + if (nisPlusStat == NIS_CACHEEXPIRED && + (ldapDBTableMapping.refreshError == + continue_using || + ldapDBTableMapping.refreshError == + continue_using_retry)) + *outStat = NIS_SUCCESS; + else + *outStat = nisPlusStat; + return (1); + } + + /* Both NIS+ and LDAP status OK => done */ + return (1); +} + +/* + * Given an input NIS+ and LDAP status, perform appropriate actions + * according to storeError. May set '*outStat'. + */ +int +storeDone(__nisdb_retry_t *storeRetry, int nisPlusStat, int ldapStat, + nis_error *outStat) { + + if (ldapStat != LDAP_SUCCESS) { + if (ldapDBTableMapping.storeError == sto_retry) { + return (!__nis_retry_sleep(storeRetry, 0)); + } else if (ldapDBTableMapping.storeError == system_error) { + *outStat = NIS_SYSTEMERROR; + return (1); + } else if (ldapDBTableMapping.storeError == sto_unavail) { + *outStat = NIS_UNAVAIL; + return (1); + } + } else if (nisPlusStat != NIS_SUCCESS) { + *outStat = nisPlusStat; + return (1); + } + + /* Both NIS+ and LDAP status OK => done */ + return (1); +} + +extern int loadAllLDAP(int, void *, db_status *); + +/* + * Up-/down-loads LDAP data, depending on the value of + * ldapConfig.initialUpdate. Returns 0 if the rpc.nisd + * can start serving data, 1 if it should exit, -1 if + * there was an error during the load. + */ +int +loadLDAPdata(void) { + void *prevCookie, *cookie = 0; + int stat, rd; + db_status dstat; + nis_error nerr; + __nisdb_retry_t refresh, retrieve, store; + char *myself = "loadLDAPdata"; + + refresh = ldapDBTableMapping.refreshErrorRetry; + retrieve = ldapDBTableMapping.retrieveErrorRetry; + store = ldapDBTableMapping.storeErrorRetry; + + while (1) { + switch (ldapConfig.initialUpdate) { + case ini_none: + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "No up-/down-load to/from LDAP"); + return (0); + case from_ldap: + case from_ldap_update_only: + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "Down-loading data from LDAP"); + prevCookie = cookie; + stat = loadAllLDAP(1, &cookie, &dstat); + nerr = map_db_status_to_nis_status(dstat); + rd = retrieveDone(&refresh, &retrieve, + nerr, stat, &nerr); + break; + case to_ldap: + case to_ldap_update_only: + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "Up-loading data to LDAP"); + prevCookie = cookie; + stat = loadAllLDAP(0, &cookie, &dstat); + nerr = map_db_status_to_nis_status(dstat); + rd = storeDone(&store, nerr, stat, &nerr); + break; + default: + logmsg(MSG_NOTIMECHECK, LOG_ERR, + "%s: Unknown initial update option %d", + myself, ldapConfig.initialUpdate); + return (-1); + } + + /* + * If we're done (rd != 0), tell the caller if the rpc.nisd + * should exit or continue running. + */ + if (rd) { + if (stat != LDAP_SUCCESS || dstat != DB_SUCCESS) { + logmsg(MSG_NOTIMECHECK, LOG_ERR, + "%s: LDAP status 0x%x, DB status %d", + myself, stat, dstat); + return (-1); + } + switch (ldapConfig.initialUpdate) { + case from_ldap: + case to_ldap: + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "LDAP load done; continuing"); + return (0); + case from_ldap_update_only: + case to_ldap_update_only: + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "LDAP load done; terminating"); + return (1); + default: + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "LDAP load internal error; terminating"); + return (-1); + } + } + + /* + * If the value of the cookie has changed, we've managed + * to move on to another mapping, so we reset the retry + * structures. + */ + if (cookie != prevCookie) { + refresh = ldapDBTableMapping.refreshErrorRetry; + retrieve = ldapDBTableMapping.retrieveErrorRetry; + store = ldapDBTableMapping.storeErrorRetry; + } + } + + /* NOTREACHED */ + return (-1); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ib_proc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ib_proc.c new file mode 100644 index 0000000000..09385ba96c --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ib_proc.c @@ -0,0 +1,1799 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1990-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Ported from + * "@(#)nis_ib_proc.c 1.37 91/03/21 Copyr 1990 Sun Micro"; + * + * nis_ib_proc.c + * + * This module contains information base function of NIS Version 3 + * NB : It provides the routines that the dispatch function in nis_svc.c + * call. That file, nis_svc.c, is automatically generated and reflects the + * interface definition that is described in the nis.x file. When the + * nis.x file changes, you must make sure that any parameters that change + * get reflected in these routines. + * + */ + +#include <stdio.h> +#include <syslog.h> +#include <stdlib.h> +#include <rpc/rpc.h> +#include <rpc/clnt.h> +#include <rpc/svc.h> +#include <rpc/auth.h> +#include <rpc/auth_des.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/nis_callback.h> +#include "nis_proc.h" +#include "nisdb_mt.h" + +#include <string.h> +#include <netdir.h> +#include <netconfig.h> + +extern bool_t xdr_nis_result(); +extern bool_t xdr_nis_error(); +extern bool_t xdr_nis_object(); +extern void (*_svc_getreqset_proc)(); + +#define NO_ENTRY_OBJS 96 + +/* + * nis_censor_object[_attr] + * + * This function enforces the access rights policies for objects. + * It is called with a candidate ENTRY object and a principal name. + * The result is either NULL (object not readable) or a pointer to + * an object that has been "censored" by this code to remove sensitive + * columns or encrypt encrypted columns. + * nis_censor_object_attr adds a fix which ensures that + * users cannot infer information from the database by using special + * search criteria. + */ +nis_object * +nis_censor_object_attr(o, tc, p, zn, za) + nis_object *o; /* The object to censor */ + table_col *tc; /* Table column descriptor */ + nis_name p; /* The principal in question */ + uint_t zn; /* Number of attributes */ + nis_attr *za; /* The search attributes */ +{ + nis_object zo; /* our censored object */ + int mc; /* Number of columns */ + int i, j, + somedata; +#define buf __nis_get_tsd()->censor_object_buf + entry_col *oec, /* The old columns pointer */ + *ec; /* Some temporary buffer */ + + + /* copy the object */ + zo = *o; + + mc = o->EN_data.en_cols.en_cols_len; + /* get some temporay space. */ + ec = (entry_col *)nis_get_static_storage(&buf, sizeof (entry_col), mc); + if (! ec) + return (NULL); + + /* Use our static version of the columns */ + zo.EN_data.en_cols.en_cols_val = ec; + + /* Get a pointer to the original columns */ + oec = o->EN_data.en_cols.en_cols_val; + somedata = 0; + for (j = 0; j < mc; j++) { + ec[j] = oec[j]; /* copy the pointers */ + ec[j].ec_flags = 0; + /* Transfer the flags values */ + if (tc[j].tc_flags & TA_BINARY) + ec[j].ec_flags |= EN_BINARY; + if (tc[j].tc_flags & TA_XDR) + ec[j].ec_flags |= EN_XDR; +#ifdef NIS_CRYPT_SUPPORT + if (tc[j].tc_flags & TA_CRYPT) + ec[j].ec_flags |= EN_CRYPT; +#endif + + /* Check to see if we can read this column */ + if (! __can_do(NIS_READ_ACC, tc[j].tc_rights, o, p)) { + /* close inference holes */ + for (i = 0; i < zn; i++) + if (za[i].zattr_ndx && + (strcasecmp(tc[j].tc_name, za[i].zattr_ndx) == 0)) + return (NULL); + /* Replace with No Permission value */ + ec[j].ENLEN = 5; + ec[j].ENVAL = "*NP*"; + } else { + somedata++; +#ifdef NIS_CRYPT_SUPPORT + if (ec[j].ec_flags & EN_CRYPT) { + /* Encrypt the data using the session key */ + /* XXX */ + } +#endif + } + } + if (somedata == NULL) + return (NULL); + + return (nis_clone_object(&zo, 0)); +} + +#undef buf + +/* + * The original function (nis_censor_object) takes + * the old arguments, and passes them on with new placeholder arguments to + * the new function (nis_censor_object_attr). Only YP compat functions + * still call nis_censor_object. + */ +nis_object +*nis_censor_object(o, tc, p) + nis_object *o; /* The object to censor */ + table_col *tc; /* Table column descriptor */ + nis_name p; /* The principal in question */ +{ + return (nis_censor_object_attr(o, tc, p, 0, NULL)); +} + +/* + * This is the free function for the list results that are returned "enmasse" + * without a callback. + */ +static void +free_rlist(entries) + nis_object *entries; +{ + XFREE(entries); +} + +/* + * nis_return_list() + * + * This function converts a full list of entries returned by the database + * into a list that can be returned to the user. It has two main functions, + * #1) Remove those entries from the list which the client does not + * have read access too. + * #2) If the client has read access to the entry, censor those + * columns which the client does not have read access to. + */ + +nis_object * +nis_return_list(obj, list, num, p, got, ar, zn, za) + nis_object *obj; /* Information base object */ + obj_list *list; /* Array of objects */ + int num; /* Size of the array */ + nis_name p; /* principal making reqst */ + int *got; /* Number actually returned */ + int ar; /* All readable flag */ + uint_t zn; /* All readable flag */ + nis_attr *za; /* The search attributes */ +{ + register table_col *tc; /* Table column pointer */ + nis_object *eo; /* Temporary object */ + nis_object *entries; /* our result pointer */ + int i, /* Temporary counters */ + cur_entry; /* The current entry */ + + *got = 0; + /* Get some parameters from the table object */ + if (__type_of(obj) == NIS_TABLE_OBJ) + tc = obj->TA_data.ta_cols.ta_cols_val; + else + tc = tbl_prototype.ta_cols.ta_cols_val; + + /* This may allocate more than we need but it is faster this way */ + entries = (nis_object *)XCALLOC(num+1, sizeof (nis_object)); + if (! entries) + return (NULL); + + add_cleanup(free_rlist, (char *)entries, "nis_return_list array."); + + if (entries == NULL) + return (NULL); + + for (i = 0, cur_entry = 0; i < num; i++) { + if (! list[i].o) + continue; + if (ar || + __can_do(NIS_READ_ACC, list[i].o->zo_access, list[i].o, p)) { + entries[cur_entry] = *list[i].o; + cur_entry++; + } else { + eo = nis_censor_object_attr(list[i].o, tc, p, zn, za); + if (eo) { + *(entries+cur_entry) = *eo; + add_cleanup(nis_destroy_object, eo, + "nis_censor_object_attr result"); + cur_entry++; + } + } + } + + *got = cur_entry; + return (entries); +} + +/* + * __search_ok() + * + * This function does a sanity check on the request (which has attribute + * value pairs) and the table which defines the names of searchable columns. + * if an attribute is passed that doesn't match up to a column the request + * is rejected. + */ +static int +__search_ok(zn, za, tobj) + uint_t zn; /* Number of attributes */ + nis_attr *za; /* The search attributes */ + nis_object *tobj; /* A Table object */ +{ + register table_col *tc; + short acm[256]; /* XXX HARD LIMIT on columns XXX */ + int i, j, mc; + + if (__type_of(tobj) == NIS_TABLE_OBJ) { + mc = tobj->TA_data.ta_maxcol; + tc = tobj->TA_data.ta_cols.ta_cols_val; + } else { + /* Can't specify anything for directories */ + return (zn == 0); + } + for (i = 0; i < zn; i++) { + acm[i] = -1; /* Entry column map */ + for (j = 0; j < mc; j++) { + if ((tc[j].tc_flags & TA_SEARCHABLE) && + (strcasecmp(tc[j].tc_name, za[i].zattr_ndx) + == 0)) { + if (acm[i] == -1) + acm[i] = j; + else + acm[i] = -2; + break; + } + } + + /* + * If we didn't find a searchable column with the name, + * check to see if it is a multival column. If it is, + * then continue on to the other attributes. We don't + * update acm because multival columns can be specified + * multiple times. + */ + if (acm[i] == -1) { + if (multival_check(tobj, &za[i], tc, mc)) + continue; + } + + /* Didn't match or matched twice */ + if (acm[i] < 0) { + return (0); + } + + } + return (1); +} + +/* + * __nis_listinit() + * + * This function set's up either a list or first/next operation. + * If it is successful, it returns a pointer to the object to be + * listed (either a table or directory object), if it is unsuccessful + * it returns NULL and fills in the result structure appropriately. + */ +static nis_object * +__nis_listinit(res, argp, princp) + nis_result *res; + ib_request *argp; + nis_name princp; +{ + nis_object *d_obj, *ib_obj = NULL; + struct ticks t; + nis_db_result *dbres; + nis_error xx; + enum name_pos p; + + NIS_RES_NUMOBJ(res) = 0; + p = nis_dir_cmp(argp->ibr_name, __nis_rpc_domain()); + if ((p != SAME_NAME) && (p != LOWER_NAME)) { + res->status = NIS_BADNAME; + return (NULL); + } + + /* Make sure we serve this directory. */ + xx = __directory_object(nis_domain_of(argp->ibr_name), &t, 0, &d_obj); + res->aticks = t.aticks; + res->dticks = t.dticks; + res->cticks = t.cticks; + res->zticks = t.zticks; + + if ((xx != NIS_SUCCESS) && (xx != NIS_S_SUCCESS)) { + /* + * Hmm, we didn't find it. Maybe it _is_ our directory + * object. + */ + xx = __directory_object(argp->ibr_name, &t, 0, &d_obj); + res->aticks += t.aticks; + res->dticks += t.dticks; + res->cticks += t.cticks; + res->zticks += t.zticks; + if ((xx != NIS_SUCCESS) && (xx != NIS_S_SUCCESS)) { + /* They really blew it */ + syslog(LOG_INFO, "Can't find directory for %s", + argp->ibr_name); + res->status = xx; + return (NULL); + } else { + ib_obj = d_obj; + } + } + + /* Now get the information base object from the database */ + if (! ib_obj) { + dbres = db_lookup(argp->ibr_name); + res->dticks += dbres->ticks; + if (dbres->status == NIS_NOSUCHTABLE) { + /* This means table of directory does not exist. */ + dbres->status = recover_from_no_table(d_obj, + nis_domain_of(argp->ibr_name)); + /* for sure, argp->ibr_name will not be there */ + if (dbres->status == NIS_SUCCESS) + dbres->status = NIS_NOTFOUND; + } + if (dbres->status != NIS_SUCCESS) { + if (dbres->status == NIS_NOTFOUND) + res->status = NIS_NOSUCHTABLE; + else + res->status = dbres->status; + res->zticks += __stop_clock(0); + return (NULL); + } + ib_obj = dbres->obj; + } else { + /* + * If its a directory, make sure we've got a database + * for it. (if not create one and synchronize) + */ + nis_error status = db_find_table(argp->ibr_name); + switch (status) { + case NIS_SUCCESS: + break; + case NIS_NOSUCHTABLE: + res->status = recover_from_no_table(d_obj, + argp->ibr_name); + if (res->status != NIS_SUCCESS) { + return (NULL); + } + break; + default: + res->status = status; + return (NULL); + } + } + + switch (__type_of(ib_obj)) { + case NIS_TABLE_OBJ: + case NIS_DIRECTORY_OBJ: + break; + + case NIS_LINK_OBJ: + if (verbose) + syslog(LOG_INFO, "__nis_listinit: Object is a link."); + /* + * we return a partial result indicating that the LINK was + * found and the client can follow it if they choose to. + */ + res->status = NIS_PARTIAL; + NIS_RES_NUMOBJ(res) = 1; + NIS_RES_OBJECT(res) = ib_obj; + return (NULL); + + default: + /* + * XXX this hard codes the "kinds" of objects that can be + * information bases. This is not nice if we want to support + * user designed objects. + */ + res->status = NIS_INVALIDOBJ; + if (verbose) + syslog(LOG_INFO, + "__nis_listinit: Nonsearchable object (type = %d).", + __type_of(ib_obj)); + return (NULL); + } + + /* Check to see is we have a valid search criteria */ + if (!__search_ok(argp->ibr_srch.ibr_srch_len, + argp->ibr_srch.ibr_srch_val, ib_obj)) { + res->status = NIS_BADATTRIBUTE; + if (verbose) + syslog(LOG_INFO, + "__nis_listinit: Unparsable attribute failure."); + return (NULL); + } + + res->status = NIS_SUCCESS; + return (ib_obj); +} + +/* + * Does the callback part of iblist + */ +static nis_result * +nis_iblist_callback(argp, pname, ib_obj, all_read, res) + ib_request *argp; + char *pname; /* caller's name */ + nis_object *ib_obj; + int all_read; /* all readable flag */ + nis_result *res; +{ + nis_error err; + nis_object *d_obj; + int pid = -1; /* Process ID of child making call back */ + int i, queued; + CLIENT *cback = NULL; /* Callback client handle */ + cback_data cbarg; /* Callback arguments */ + /* An array of object pointers */ + struct timeval tv; /* Timeout for callback */ + nis_fn_result *fnr; + netobj cookie; /* Cookie storage for first/nxt */ + enum clnt_stat status = RPC_SUCCESS; + nis_error result; + table_col *tc; + ulong_t flags; + char tblbuf[NIS_MAXNAMELEN * 2]; + char *table; + nis_attr *a; + int na; /* number of regular attributes */ + int nm; /* number of multival attributes */ + + + /* + * Used to create the callback handle here. We now delay + * until we know that we actually intend to perform the callback. + */ + + table = internal_table_name(argp->ibr_name, tblbuf); + if (! table) { + res->status = NIS_BADNAME; + return (res); + } + + na = argp->ibr_srch.ibr_srch_len; + a = argp->ibr_srch.ibr_srch_val; + err = multival_attr(ib_obj, a, &na, &nm); + if (err != NIS_SUCCESS) { + res->status = err; + return (res); + } + + /* + * This will force the database to be loaded. + */ + fnr = db_firstib(argp->ibr_name, na, a, FN_MANGLE+FN_NORAGS, table); + + if (fnr->status != NIS_SUCCESS) { + if (fnr->status == NIS_NOTFOUND) { + res->status = NIS_PARTIAL; + res->objects.objects_val = ib_obj; + res->objects.objects_len = 1; + } else { + res->status = fnr->status; + res->objects.objects_val = NULL; + res->objects.objects_len = 0; + } + res->dticks += fnr->ticks; + res->zticks += __stop_clock(0); + XFREE(fnr); + fnr = NULL; + return (res); + } + + if (verbose) + syslog(LOG_INFO, "Making callback handle to : %s", + argp->ibr_cbhost.ibr_cbhost_val[0].name); + if ((strcmp(pname, "nobody") == 0) || (secure_level < 2)) + flags = ZMH_VC; + else + flags = ZMH_VC+ZMH_AUTH; + + cback = nis_make_rpchandle(argp->ibr_cbhost.ibr_cbhost_val, 1, + CB_PROG, 1, flags, 16384, 16384); + /* If we couldn't create a client handle we're hosed */ + if (! cback) { + syslog(LOG_WARNING, "Unable to create callback."); + res->status = NIS_NOCALLBACK; + res->zticks = __stop_clock(0); + XFREE(fnr); + fnr = NULL; + return (res); + } + + /* Create a new thread to perform the callback */ + { + pthread_t tid; + pthread_attr_t attr; + int stat; + callback_thread_arg_t *cbtarg; + int attrsize = na * sizeof (cbtarg->a[0]); + + if ((cbtarg = calloc(1, sizeof (*cbtarg))) == 0) { + syslog(LOG_WARNING, + "nis_iblist_callback: memory allocation failed for %d bytes", + sizeof (*cbtarg)); + auth_destroy(cback->cl_auth); + clnt_destroy(cback); + res->status = NIS_NOMEMORY; + res->zticks = __stop_clock(0); + XFREE(fnr); + return (res); + } + cbtarg->fnr = fnr; + cbtarg->ib_obj = nis_clone_object(ib_obj, 0); + if (na <= (sizeof (cbtarg->a)/sizeof (cbtarg->a[0]))) { + cbtarg->na = na; + } else { + syslog(LOG_WARNING, + "nis_iblist_callback: too many attributes; max %d expected, %d found", + (sizeof (cbtarg->a)/sizeof (cbtarg->a[0])), na); + cbtarg->na = sizeof (cbtarg->a)/sizeof (cbtarg->a[0]); +#ifdef NIS_MT_DEBUG + abort(); +#endif /* NIS_MT_DEBUG */ + } + memcpy(cbtarg->a, a, cbtarg->na * sizeof (cbtarg->a[0])); + cbtarg->nm = nm; + cbtarg->all_read = all_read; + strncpy(cbtarg->pname, pname, sizeof (cbtarg->pname)); + cbtarg->pname[sizeof (cbtarg->pname) - 1] = '\0'; + cbtarg->cbarg = cbarg; + cbtarg->cback = cback; + strncpy(cbtarg->cbhostname, + argp->ibr_cbhost.ibr_cbhost_val[0].name, + sizeof (cbtarg->cbhostname)); + cbtarg->cbhostname[sizeof (cbtarg->cbhostname) - 1] = '\0'; + strncpy(cbtarg->ibr_name, + argp->ibr_name, sizeof (cbtarg->ibr_name)); + cbtarg->ibr_name[sizeof (cbtarg->ibr_name) - 1] = '\0'; + + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, + PTHREAD_CREATE_DETACHED); + if ((stat = pthread_create(&tid, &attr, callback_thread, + cbtarg)) == 0) { + if (verbose) + syslog(LOG_INFO, + "nis_iblist_callback: created callback thread %d", tid); + res->status = NIS_CBRESULTS; + } else { + if (verbose) + syslog(LOG_WARNING, + "nis_iblist_callback: callback thread create failed: %d", + stat); + res->status = NIS_TRYAGAIN; + } + res->zticks += __stop_clock(0); + + (void) pthread_attr_destroy(&attr); + + res->cookie.n_bytes = (char *)malloc(sizeof (anonid_t)); + if (res->cookie.n_bytes == NULL) { + syslog(LOG_WARNING, + "Couldn't allocate memory for callback id"); + /* what error to return in case of no memory */ + res->status = NIS_NOMEMORY; + } else { + anonid_t anonid = tid; + add_cleanup((void (*)())XFREE, + (char *)res->cookie.n_bytes, "callback id"); + res->cookie.n_len = sizeof (anonid); + memcpy(res->cookie.n_bytes, &anonid, + sizeof (anonid)); + if (verbose) + syslog(LOG_INFO, + "returning, callback id = %d", + *((anonid_t *)res->cookie.n_bytes)); + } + return (res); + } +} + + +/* + * __nis_alt_callback_server() + * + * Return a pointer to a new chunk of memory containing a copy of the + * org_endpoint argument, with the rpc_origin address substituted (merged, + * since the port number is retained) for the first endpoint whose family + * matches an NC_TPI_COTS netconfig entry. + * + * If (*uaddr != 0), it is assumed to point to a piece of memory of length + * uaddrlen, where the caller wants a copy of the uaddr. + * + * It is the responsibility of the caller to free() the returned endpoint + * structure, as well as the uaddr if (*uaddr == 0). + */ +endpoint * +__nis_alt_callback_server(endpoint *org_endpoint, + uint_t count, + struct netbuf *rpc_origin, + char **uaddr, + int uaddrlen) +{ + endpoint *alt_endpoint; + uint_t size, i, j, ei; + struct netconfig *nc, *match = 0; + struct netbuf *taddr; + void *newaddr; + void *rpcaddr; + int addrlen; + sa_family_t af, rpcaf; + char *tmpuaddr; + + /* Sanity check arguments */ + if (org_endpoint == 0 || count == 0 || rpc_origin == 0) { + return (0); + } + + /* We only know how to deal with IP */ + rpcaf = ((struct sockaddr *)rpc_origin->buf)->sa_family; + if (rpcaf != AF_INET && rpcaf != AF_INET6) { + return (0); + } + + /* Retrieve a netconfig entry for "inet" that matches an endpoint */ + for (i = 0; i < count; i++) { + if ((nc = __nis_get_netconfig(&org_endpoint[i])) != 0 && + nc->nc_semantics == NC_TPI_COTS_ORD) { + if (strcasecmp(nc->nc_protofmly, NC_INET) == 0 && + rpcaf == AF_INET) { + af = AF_INET; + } else if (strcasecmp( + nc->nc_protofmly, NC_INET6) == 0 && + rpcaf == AF_INET6) { + af = AF_INET6; + } else { + continue; + } + match = nc; + ei = i; + break; + } + } + + /* Did we find one ? */ + if (match == 0) { + return (0); + } + + /* + * Allocate memory for the alternate endpoint array. + * Must be freed by caller, if we return successfully. + */ + size = count * sizeof (endpoint); + if ((alt_endpoint = malloc(size)) == 0) { + return (0); + } + + for (j = 0; j < count; j++) { + alt_endpoint[j] = org_endpoint[j]; + } + + /* Get the address suggested by the remote caller */ + if ((taddr = uaddr2taddr(match, alt_endpoint[ei].uaddr)) == 0) { + free(alt_endpoint); + return (0); + } + + /* + * The ugly part: merge port number and RPC source address. + * This requires knowledge about the internals of a netbuf. + */ + + if (af == AF_INET) { + struct in_addr dummy; + newaddr = + &((struct sockaddr_in *)taddr->buf)->sin_addr.s_addr; + rpcaddr = + &((struct sockaddr_in *)rpc_origin->buf)->sin_addr.s_addr; + addrlen = sizeof (dummy.s_addr); + } else { + struct in6_addr dummy; + newaddr = + &((struct sockaddr_in6 *)taddr->buf)->sin6_addr.s6_addr; + rpcaddr = + &((struct sockaddr_in6 *)rpc_origin->buf)->sin6_addr.s6_addr; + addrlen = sizeof (dummy.s6_addr); + } + + /* + * If addresses are the same, there is no need to try an alternate + * address, so just pretend that we failed. + */ + if (!memcmp(newaddr, rpcaddr, addrlen)) { + free(alt_endpoint); + netdir_free(taddr, ND_ADDR); + return (0); + } + + /* Alternate address differs; worth trying */ + memcpy(newaddr, rpcaddr, addrlen); + if ((tmpuaddr = taddr2uaddr(match, taddr)) == 0) { + free(alt_endpoint); + netdir_free(taddr, ND_ADDR); + return (0); + } + + if (*uaddr == 0) { + *uaddr = tmpuaddr; + } else if (strlen(tmpuaddr) < uaddrlen) { + (void) strcpy(*uaddr, tmpuaddr); + free(tmpuaddr); + } else { + free(alt_endpoint); + netdir_free(taddr, ND_ADDR); + free(tmpuaddr); + return (0); + } + + /* Clean up */ + netdir_free(taddr, ND_ADDR); + + alt_endpoint[ei].uaddr = *uaddr; + + return (alt_endpoint); +} + +/* + * nis_iblist, this function will take the search criteria passed, + * and pass it on to the database. If the callback structure is + * null then the results are simply returned, otherwise the server + * forks and and begins returning results one by one. In a multithreaded + * environment this is handled by a thread. The library will check + * a resource limit variable before forking. + */ +nis_result * +nis_iblist_svc(argp, reqstp) + ib_request *argp; + struct svc_req *reqstp; +{ + nis_result *res = NULL; + nis_object *ib_obj, + *list; + int got; + char *s; + int all_read; /* all readable flag */ + char pname[1024]; + nis_db_list_result *ib_list; + int zn; + nis_attr *za; + + __start_clock(0); + if (verbose) + syslog(LOG_INFO, "LIST_SVC: Listing table %s", + argp->ibr_name); + + res = (nis_result *)XCALLOC(1, sizeof (nis_result)); + add_cleanup((void (*)())XFREE, (char *)res, "iblist result"); + + /* If reqstp == NULL then we're recursing */ + if (reqstp) + nis_getprincipal(pname, reqstp); + else + pname[0] = '\0'; + + ib_obj = __nis_listinit(res, argp, pname); + + if (res->status != NIS_SUCCESS) { + res->zticks = __stop_clock(0); + return (res); + } + /* If we have read access on the table, we can read all of it */ + all_read = __can_do(NIS_READ_ACC, ib_obj->zo_access, ib_obj, pname); + + /* + * Now we actually do the list operation. If there is a callback, + * then we use the first/next operations to send the objects back + * one at a time. + */ + if (argp->ibr_cbhost.ibr_cbhost_len) { + /* Process the callback request. */ + /* + * The client supplied an address for the callback service. + * However, we may not be able to reach that address, so + * try the source address of the RPC request first. + * + * Note: The alt_endpoint array (which usually contains just + * a single element) is a copy of of org_endpoint, including + * pointers, except for the uaddr of one element. This uaddr + * contains a merged version of the RPC source address and the + * port number specified by the client in the callback data. + */ + { + struct netbuf *rpc_origin; + endpoint *org_endpoint, *alt_endpoint; + char *uaddr = 0; + + org_endpoint = + argp->ibr_cbhost.ibr_cbhost_val->ep.ep_val; + rpc_origin = svc_getrpccaller(reqstp->rq_xprt); + if ((alt_endpoint = __nis_alt_callback_server( + org_endpoint, + argp->ibr_cbhost.ibr_cbhost_val->ep.ep_len, + rpc_origin, + &uaddr, 0)) != 0) { + argp->ibr_cbhost.ibr_cbhost_val->ep.ep_val = + alt_endpoint; + res = nis_iblist_callback(argp, pname, ib_obj, + all_read, res); + argp->ibr_cbhost.ibr_cbhost_val->ep.ep_val = + org_endpoint; + free(alt_endpoint); + free(uaddr); + if (res->status != NIS_NOCALLBACK) { + return (res); + } + } + } + return (nis_iblist_callback(argp, pname, ib_obj, + all_read, res)); + } + /* + * If the client didn't ask for a callback, we process the + * entire list at once and return it. (implicit else clause) + */ + ib_list = db_list(argp->ibr_name, argp->ibr_srch.ibr_srch_len, + argp->ibr_srch.ibr_srch_val); + + res->dticks += ib_list->ticks; + if (ib_list->status != NIS_SUCCESS) { + if (ib_list->status == NIS_NOTFOUND) { + res->status = NIS_PARTIAL; + NIS_RES_OBJECT(res) = ib_obj; + NIS_RES_NUMOBJ(res) = 1; + } else { + if (__type_of(ib_obj) == NIS_TABLE_OBJ) { + s = "table"; + } else + s = "directory"; + syslog(LOG_ERR, "Unable to list %s '%s' err=%d", + s, argp->ibr_name, ib_list->status); + res->status = ib_list->status; + NIS_RES_OBJECT(res) = NULL; + NIS_RES_NUMOBJ(res) = 0; + } + res->zticks = __stop_clock(0); + if (verbose) + syslog(LOG_INFO, "nis_iblist_svc: return status %s", + nis_sperrno(res->status)); + return (res); + } + /* Now process the list of objects we've got to return */ + zn = argp->ibr_srch.ibr_srch_len; + za = argp->ibr_srch.ibr_srch_val; + list = nis_return_list(ib_obj, ib_list->objs, ib_list->numo, pname, + &got, all_read, + zn, za); + + /* Construct the result */ + if (got) { + res->status = NIS_SUCCESS; + NIS_RES_NUMOBJ(res) = got; + NIS_RES_OBJECT(res) = list; + } else { + res->status = NIS_NOTFOUND; + NIS_RES_NUMOBJ(res) = 0; + NIS_RES_OBJECT(res) = NULL; + } + res->zticks = __stop_clock(0); + return (res); +} + +/* + * This is an internal Information Base operations function. It is collected + * here for clarity. It makes the actual database calls to manipulate the + * namespace. + */ +static void +add_entry(name, zn, za, obj, tbl, flags, princp, xid, res) + nis_name name; /* Table name */ + int zn; /* Number of attributes */ + nis_attr *za; /* NIS+ attribute list. */ + nis_object *obj, /* The entry we're adding */ + *tbl; /* Table we're adding to */ + ulong_t flags; /* Semantic flags */ + nis_name princp; /* Principal making request */ + int *xid; /* XID for transaction */ + nis_result *res; /* Place for results. */ +{ + nis_db_list_result *db; + nis_db_result *ars; + int add_ok, mod_ok; + time_t currtime; + + /* + * Check to see if we can actually create an entry in this + * table. + */ + add_ok = __can_do(NIS_CREATE_ACC, tbl->zo_access, tbl, princp); + mod_ok = __can_do(NIS_MODIFY_ACC, tbl->zo_access, tbl, princp); + if (auth_verbose) { + syslog(LOG_INFO, + "add_entry: creation is %s by the table.", + (add_ok) ? "ALLOWED" : "DISALLOWED"); + + } + + db = db_list(name, zn, za); + + *xid = begin_transaction(princp); + if (*xid == 0) { + res->status = NIS_TRYAGAIN; + return; + } + + currtime = time(0); /* Make sure REMOVE/ADD has same time */ + obj->zo_oid.ctime = (ulong_t)currtime; + obj->zo_oid.mtime = obj->zo_oid.ctime; + if (db->status == NIS_SUCCESS) { + if (db->numo != 1) { + res->status = NIS_NOTUNIQUE; + } else if ((flags & ADD_OVERWRITE) == 0) { + res->status = NIS_NAMEEXISTS; + } else if (! mod_ok && + ! __can_do(NIS_MODIFY_ACC, db->objs[0].o->zo_access, + db->objs[0].o, princp)) { + res->status = NIS_PERMISSION; + } else { + nisdb_tsd_t *tsd; + + /* Act like a modify on the OID */ + obj->zo_oid.ctime = db->objs->o->zo_oid.ctime; + + /* + * Tell the DB that we're doing a modify, so that + * it can merge the remove/add to a single LDAP + * update. + */ + tsd = __nisdb_get_tsd(); + tsd->doingModify = 1; + + /* This updates the log */ + db_remib(name, zn, za, db->objs, db->numo, tbl, + (ulong_t)currtime); + /* As does this, a virtual modify operation */ + ars = db_addib(name, zn, za, obj, tbl); + + tsd->doingModify = 0; + + res->dticks += ars->ticks; + res->status = ars->status; + } + } else if (db->status == NIS_NOTFOUND) { + /* Ok, it doesn't exist so lets add it to the table */ + if (! add_ok) { + res->status = NIS_PERMISSION; + return; + } + + ars = db_addib(name, zn, za, obj, tbl); + if ((ars->status == NIS_SUCCESS) && (flags & RETURN_RESULT)) { + res->objects.objects_val = nis_clone_object(obj, NULL); + res->objects.objects_len = 1; + } + res->dticks += ars->ticks; + res->status = ars->status; + } else + res->status = db->status; +} + +/* + * This is an internal Information Base operations function that implements + * the remove operation. It is collected here for clarity. + */ +static void +remove_entry(name, zn, za, obj, tbl, flags, princp, xid, res) + nis_name name; /* Table name */ + int zn; /* Number of attributes */ + nis_attr *za; /* NIS attribute list. */ + nis_object *obj, /* The entry we're removing */ + *tbl; /* Table we're adding to */ + ulong_t flags; /* Semantic flags */ + nis_name princp; /* Principal making request */ + int *xid; /* XID for transaction */ + nis_result *res; /* Place for results. */ +{ + nis_db_list_result *db; + nis_db_result *rrs; + int i, rem_ok; + + /* + * Access control, if we have remove access granted at the + * table level then we can remove all entries. + */ + rem_ok = __can_do(NIS_DESTROY_ACC, tbl->zo_access, tbl, princp); + if (auth_verbose) { + syslog(LOG_INFO, + "remove_entry: removal is %s by the table.", + (rem_ok) ? "ALLOWED" : "DISALLOWED"); + + } + + + db = db_list(name, zn, za); + res->dticks += db->ticks; + + /* + * Check for errors, first to see if we actually found an + * object to remove. + */ + if (db->status != NIS_SUCCESS) { + res->status = db->status; + return; + } + + /* + * next to see if more than one object may be removed. + */ + if ((db->numo > 1) && ((flags & REM_MULTIPLE) == 0)) { + res->status = NIS_NOTUNIQUE; + return; + } + + /* + * Third to see if we need the same object to remove + */ + if (obj && (! same_oid(obj, db->objs->o))) { + res->status = NIS_NOTSAMEOBJ; + return; + } + + /* + * Fourth check to see if we have the right to remove + * these entrie(s) + */ + if (! rem_ok) { + for (i = 0; i < db->numo; i++) + if (! __can_do(NIS_DESTROY_ACC, + db->objs[i].o->zo_access, + db->objs[i].o, princp)) + break; + if (i < db->numo) { + res->status = NIS_PERMISSION; + return; + } + } + + *xid = begin_transaction(princp); + if (*xid == 0) { + res->status = NIS_TRYAGAIN; + return; + } + + /* + * Finally, remove the object in question. + */ + rrs = db_remib(name, zn, za, db->objs, db->numo, tbl, + (ulong_t)(time(0))); + res->dticks += rrs->ticks; + res->status = rrs->status; +} + +/* + * This is the internal Information base function that implements the modify + * operation. It is put here for clarity. + */ +static void +modify_entry(name, zn, za, obj, tbl, flags, princp, xid, res) + nis_name name; /* Table name */ + int zn; /* Number of attributes */ + nis_attr *za; /* NIS attribute list. */ + nis_object *obj, /* The entry we're removing */ + *tbl; /* Table we're adding to */ + ulong_t flags; /* Semantic flags */ + nis_name princp; /* Principal making request */ + int *xid; /* XID for transaction */ + nis_result *res; /* Place for results. */ +{ +#define buf __nis_get_tsd()->modify_entry_buf + nis_db_list_result *db; + nis_db_result *rrs; + int i, mc; + entry_col *o_ec, /* Old entry columns */ + *n_ec; /* New entry columns */ + nis_object mobj; /* Modified object ... */ + int clone; /* True if new object is a clone */ + entry_col *mec; /* ... and it's columns */ + int mod_ok; + table_col *tcol; + nis_db_list_result *o_db; + nis_attr *test_attr; + int na, mod_attrs = 0; + time_t currtime; + nisdb_tsd_t *tsd; + + /* + * Set the global modify access bit for the objects by checking + * the table's access rights. + */ + mod_ok = __can_do(NIS_MODIFY_ACC, tbl->zo_access, tbl, princp); + + if (auth_verbose) { + syslog(LOG_INFO, + "modify_entry: modification is %s by the table.", + (mod_ok) ? "ALLOWED" : "DISALLOWED"); + + } + + mc = tbl->TA_data.ta_maxcol; + tcol = tbl->TA_data.ta_cols.ta_cols_val; + mec = (entry_col *)nis_get_static_storage(&buf, sizeof (entry_col), mc); + if (! mec) { + res->status = NIS_NOMEMORY; + return; + } + + db = db_list(name, zn, za); + + /* If we didn't find the original just return the error */ + if (db->status != NIS_SUCCESS) { + res->status = db->status; + return; + } + + /* If the object isn't unique we can't modify it. */ + if (db->numo > 1) { + res->status = NIS_NOTUNIQUE; + return; + } + + clone = same_oid(obj, db->objs->o); + + if ((flags & MOD_SAMEOBJ) && (! clone)) { + res->status = NIS_NOTSAMEOBJ; + return; + } + + /* + * Now check the permissions on the object itself. + * If we don't have modify access to the table we check + * to see if we have modify access to the object itself, + * if that fails we check to see if we have modify access + * to the column we are modifying. If that fails we return + * a permission error. + */ + o_ec = db->objs->o->EN_data.en_cols.en_cols_val; + n_ec = obj->EN_data.en_cols.en_cols_val; + if (! mod_ok) + mod_ok = __can_do(NIS_MODIFY_ACC, db->objs[0].o->zo_access, + db->objs[0].o, princp); + if (! mod_ok) { + for (i = 0; i < mc; i++) + if (((n_ec[i].ec_flags & EN_MODIFIED) != 0) && + ! __can_do(NIS_MODIFY_ACC, + tcol[i].tc_rights, + db->objs[0].o, princp)) { + res->status = NIS_PERMISSION; + return; + } + } + + /* + * Check the MOD_EXCLUSIVE flag. If a searchable column has been + * modified, the key index will be changed, which may conflict with + * an existing entry. + */ + + if (flags & MOD_EXCLUSIVE) { + /* Check for a searchable column that has been modified */ + for (i = 0; i < mc; i++) { + if (mod_attrs = ((tcol[i].tc_flags & TA_SEARCHABLE) && + (n_ec[i].ec_flags & EN_MODIFIED))) { + break; + } + } + /* + * If key modified, then create new attr list and see if + * it matches an existing entry + */ + if (mod_attrs) { + test_attr = __get_attrs(mc); + if (test_attr == NULL) { + res->status = NIS_NOMEMORY; + return; + } + for (i = 0, na = 0; i < mc; i++) { + if (tcol[i].tc_flags & TA_SEARCHABLE) { + test_attr[na].zattr_ndx = + tcol[i].tc_name; + if ((n_ec[i].ec_flags & EN_MODIFIED) == + 0) { + test_attr[na].ZAVAL = o_ec[i].ENVAL; + test_attr[na].ZALEN = o_ec[i].ENLEN; + } else { + test_attr[na].ZAVAL = n_ec[i].ENVAL; + test_attr[na].ZALEN = n_ec[i].ENLEN; + } + na++; + } + } + o_db = db_list(name, na, test_attr); + if (o_db->status == NIS_SUCCESS) { + res->status = NIS_NAMEEXISTS; + return; + } + } + } + + mobj = *(db->objs->o); + currtime = time(0); /* Make sure REMOVE/ADD has same time */ + mobj.zo_oid.mtime = (ulong_t)currtime; + + /* Now check to see if the attributes need to change */ + /* + * XXX this can fail silently when attributes change + * but mod_ok isn't set, we should figure out how to + * return a permission error. XXX + */ + if (clone && mod_ok) { + mobj.zo_owner = obj->zo_owner; + mobj.zo_group = obj->zo_group; + mobj.zo_access = obj->zo_access; + mobj.zo_ttl = obj->zo_ttl; + } + mobj.EN_data.en_cols.en_cols_val = mec; + + for (i = 0; i < mc; i++) { + if ((n_ec[i].ec_flags & EN_MODIFIED) == 0) + mec[i] = o_ec[i]; + else + mec[i] = n_ec[i]; + } + + *xid = begin_transaction(princp); + if (*xid == 0) { + res->status = NIS_TRYAGAIN; + return; + } + + /* + * If changes are written through to LDAP, we want to make + * just one LDAP update, not one remove and one add, which + * causes problems when more than one NIS+ table maps to + * one and the same LDAP container. Hence, inform the DB + * that this removal is part of a modify operation. + */ + tsd = __nisdb_get_tsd(); /* Never returns NULL */ + tsd->doingModify = 1; + + /* Remove the old version of the entry */ + rrs = db_remib(name, zn, za, db->objs, db->numo, tbl, + (ulong_t)currtime); + res->dticks += rrs->ticks; + + if (res->status != NIS_SUCCESS) + syslog(LOG_ERR, "modify_entry: Unable to remove object %s", + name); + /* + * Now do the modify by overwriting the existing + * object. + */ + rrs = db_addib(name, zn, za, &mobj, tbl); + if ((rrs->status == NIS_SUCCESS) && (flags & RETURN_RESULT)) { + res->objects.objects_val = nis_clone_object(&mobj, NULL); + res->objects.objects_len = 1; + } + + /* Reset modification indication to the DB */ + tsd->doingModify = 0; + + res->dticks += rrs->ticks; + res->status = rrs->status; +} + +#undef buf + +/* + * __nis_ibops() + * This is the common information base ops routine. It implements the + * various operations that can be done on an information base. It is + * analagous to the nameops() routine above. + */ +static nis_result * +__nis_ibops(op, argp, princp) + int op; + ib_request *argp; + nis_name princp; +{ + nis_result *res; +#define buf __nis_get_tsd()->__ibops_buf + nis_object *t_obj, *n_obj, *d_obj; + nis_attr *za; + int zn; + table_col *tc; + ulong_t ttime; + int i, mc, xid; + entry_col *ec; + nis_db_result *table; + struct ticks t; + char optxt[32]; + nis_error xx; + + res = (nis_result *)XCALLOC(1, sizeof (nis_result)); + add_xdr_cleanup(xdr_nis_result, (char *)res, "ibops result"); + + if (readonly) { + res->status = NIS_TRYAGAIN; + return (res); + } + + if (auth_verbose) { + switch (op) { + case ADD_OP : + strcpy(optxt, "ADD"); + break; + case REM_OP : + strcpy(optxt, "REMOVE"); + break; + case MOD_OP : + strcpy(optxt, "MODIFY"); + break; + } + syslog(LOG_INFO, "Entry operation '%s' for principal %s", + optxt, princp); + } + + /* + * Check to see if we serve the directory this table is in. + * NOTE: this could do a NIS+ lookup to the parent. + */ + xx = __directory_object(nis_domain_of(argp->ibr_name), &t, TRUE, + &d_obj); + res->aticks = t.aticks; + res->zticks = t.zticks; + res->cticks = t.cticks; + res->dticks = t.dticks; + if (d_obj == NULL) { + syslog(LOG_ERR, "Couldn't locate directory object for %s", + argp->ibr_name); + res->status = NIS_NOT_ME; /* XXX should we use 'xx'? */ + return (res); + } + + /* + * Get the last update time. If the current time is earlier + * someone has set the clock back which is very bad as far + * as NIS+ is concerned. This will catch almost all possible + * situations except the single second when time has finally + * caught up with the last entry in the log. Fixing this + * would require more radical changes to the whole update + * mechanism which is potentially risky and may even require + * protocol changes. + */ + if (last_update(d_obj->DI_data.do_name) > time(0)) { + syslog(LOG_ERR, "Update rejected because system time is " + "earlier than most recent log entry"); + res->status = NIS_SYSTEMERROR; + return (res); + } + + /* + * POLICY : Should we have to read the directory to + * read the table ? + * ANSWER : No, if we _know_ the name of the table we + * should be able to access it. + */ + + /* quick pointer to the "new" object */ + if (argp->ibr_obj.ibr_obj_len) + n_obj = argp->ibr_obj.ibr_obj_val; + else + n_obj = NULL; + + /* Get the table that we will be manipulating. */ + table = db_lookup(argp->ibr_name); + res->dticks += table->ticks; + if (table->status != NIS_SUCCESS) { + res->status = table->status; + return (res); + } + + t_obj = table->obj; + if (__type_of(t_obj) != NIS_TABLE_OBJ) { + res->status = NIS_INVALIDOBJ; + return (res); + } + + if (! __search_ok(argp->ibr_srch.ibr_srch_len, + argp->ibr_srch.ibr_srch_val, t_obj)) { + res->status = NIS_BADATTRIBUTE; + return (res); + } + + /* For ADD/MODify Make sure the types are the same */ + if ((op == MOD_OP) || (op == ADD_OP)) { + if (! n_obj) { + res->status = NIS_BADOBJECT; + return (res); + } + } + + /* + * For all operations, if there is an object, check that + * it matches the table. + */ + if (n_obj) { + if (strcasecmp(n_obj->EN_data.en_type, + t_obj->TA_data.ta_type) != 0) { + res->status = NIS_TYPEMISMATCH; + return (res); + } + + /* Make sure the number of columns are the same */ + if (n_obj->EN_data.en_cols.en_cols_len != + t_obj->TA_data.ta_maxcol) { + res->status = NIS_TYPEMISMATCH; + return (res); + } + } + + /* + * Now we check the attribute list, doing some sanity checking + * and then go to the database. + */ + + tc = t_obj->TA_data.ta_cols.ta_cols_val; /* The Table columns */ + mc = t_obj->TA_data.ta_maxcol; /* The total columns */ + if (n_obj) /* The Entry columns */ + ec = n_obj->EN_data.en_cols.en_cols_val; + else + ec = NULL; + + if (argp->ibr_srch.ibr_srch_len) { + za = argp->ibr_srch.ibr_srch_val; /* The AVA list */ + zn = argp->ibr_srch.ibr_srch_len; /* The current AVA */ + } else if (ec) { + /* build a search criteria from the passed entry */ + za = (nis_attr *)nis_get_static_storage(&buf, sizeof (nis_attr), + mc); + for (i = 0, zn = 0; i < mc; i++) { + if ((tc[i].tc_flags & TA_SEARCHABLE) && + (ec[i].ec_value.ec_value_val)) { + za[zn].zattr_ndx = tc[i].tc_name; + za[zn].ZAVAL = ec[i].ENVAL; + za[zn].ZALEN = ec[i].ENLEN; + zn++; + } + } + } else { + za = NULL; + zn = 0; + } + + xid = 0; + + switch (op) { + + case ADD_OP : + add_entry(argp->ibr_name, zn, za, n_obj, t_obj, + argp->ibr_flags, princp, &xid, + res); + break; + case REM_OP : + remove_entry(argp->ibr_name, zn, za, n_obj, t_obj, + argp->ibr_flags, princp, &xid, + res); + break; + case MOD_OP : + modify_entry(argp->ibr_name, zn, za, n_obj, t_obj, + argp->ibr_flags, princp, &xid, + res); + break; + } + + if (verbose) + syslog(LOG_INFO, "ibops: operation completed."); + if (res->status == NIS_SUCCESS) { + end_transaction(xid); + ttime = last_update(d_obj->DI_data.do_name); + if (d_obj->DI_data.do_servers.do_servers_len > 1) { + RLOCK(ping_list); + add_pingitem(d_obj, ttime, &ping_list); + RULOCK(ping_list); + } + } else if (xid != 0) + abort_transaction(xid); + + if (verbose) + syslog(LOG_INFO, "ibops: returning..."); + return (res); +} + +#undef buf + +/* + * nis_fnops() + * This is routine is common to the first and next functions below. Quite + * a bit of code is shared so this cuts down on the chance for errors. + * The first part is identical to the ibops function above but this + * routine needs to return different data so I didn't want to overload + * the return value of ibobs() above. + */ +static nis_result * +__nis_fnops(op, argp, princp) + int op; + ib_request *argp; + nis_name princp; +{ + nis_object *r_obj, *t_obj, *ib_obj = NULL; + nis_fn_result *fnr; + table_col *tc; + int all_read = 0; + nis_result *res; + int zn; + nis_attr *za; + + res = (nis_result *)XCALLOC(1, sizeof (nis_result)); + if (! res) { + syslog(LOG_ERR, "rpc.nisd: Out of memory"); + return (NULL); + } + add_cleanup((void (*)())XFREE, (char *)res, "fnops result"); + + ib_obj = __nis_listinit(res, argp, princp); + + if (! ib_obj) { + res->zticks = __stop_clock(0); + return (res); + } + + all_read = __can_do(NIS_READ_ACC, ib_obj->zo_access, ib_obj, princp); + if (op == FIRST_OP) + fnr = db_firstib(argp->ibr_name, argp->ibr_srch.ibr_srch_len, + argp->ibr_srch.ibr_srch_val, FN_MANGLE+FN_NORAGS, NULL); + else if (op == NEXT_OP) { + res->cookie.n_len = argp->ibr_cookie.n_len; + res->cookie.n_bytes = (char *)malloc(res->cookie.n_len); + memcpy(res->cookie.n_bytes, + argp->ibr_cookie.n_bytes, res->cookie.n_len); + fnr = db_nextib(argp->ibr_name, &(res->cookie), + FN_MANGLE+FN_NORAGS, NULL); + } + + res->dticks += fnr->ticks; + if (fnr->status != NIS_SUCCESS) { + res->status = fnr->status; + XFREE(fnr); + return (res); + } + + res->cookie = fnr->cookie; + res->status = fnr->status; + /* + * If we have read access to everything or the object then we're done. + */ + if (all_read || + __can_do(NIS_READ_ACC, fnr->obj->zo_access, fnr->obj, princp)) { + res->objects.objects_val = fnr->obj; + res->objects.objects_len = 1; + add_cleanup(nis_destroy_object, (char *)fnr->obj, "fnops obj"); + add_cleanup((void (*)())XFREE, (char *)fnr->cookie.n_bytes, + "fnops descript"); + XFREE(fnr); + return (res); + } + + r_obj = fnr->obj; + XFREE(fnr); /* free the result structure */ + if (__type_of(ib_obj) == NIS_TABLE_OBJ) + tc = ib_obj->TA_data.ta_cols.ta_cols_val; + else + tc = tbl_prototype.ta_cols.ta_cols_val; + do { + if (__can_do(NIS_READ_ACC, r_obj->zo_access, r_obj, princp)) + t_obj = r_obj; + else { + zn = argp->ibr_srch.ibr_srch_len; + za = argp->ibr_srch.ibr_srch_val; + t_obj = nis_censor_object_attr(r_obj, tc, princp, + zn, za); + nis_destroy_object(r_obj); + } + if (t_obj) { + res->objects.objects_val = t_obj; + res->objects.objects_len = 1; + add_cleanup(nis_destroy_object, (char *)t_obj, + "fnops obj"); + add_cleanup((void (*)())XFREE, + (char *)res->cookie.n_bytes, + "fnops descript"); + return (res); + } + + fnr = db_nextib(argp->ibr_name, &(res->cookie), + FN_MANGLE+FN_NORAGS, NULL); + res->dticks += fnr->ticks; + res->status = fnr->status; + if (res->status == NIS_SUCCESS) + res->cookie = fnr->cookie; + r_obj = fnr->obj; + XFREE(fnr); + } while (res->status == NIS_SUCCESS); + + return (res); +} + +/* + * nis_addib, this function adds an entry into an Information Base. + * the entry is not "visible" in the namespace, only as a component + * of the information base that contains it. + */ +nis_result * +nis_ibadd_svc(argp, reqstp) + ib_request *argp; + struct svc_req *reqstp; +{ + nis_result *res; + char pname[1024]; + + __start_clock(0); + if (verbose) + syslog(LOG_INFO, "Entry ADD_SVC: to table %s", argp->ibr_name); + /* A quick and easy check... */ + if ((argp->ibr_obj.ibr_obj_len == 1) && + (__type_of(argp->ibr_obj.ibr_obj_val) != NIS_ENTRY_OBJ)) { + return (nis_make_error(NIS_INVALIDOBJ, 0, 0, 0, + __stop_clock(0))); + } + nis_getprincipal(pname, reqstp); + res = __nis_ibops(ADD_OP, argp, pname); + if (verbose) + syslog(LOG_INFO, "Done, exit status %s", + nis_sperrno(res->status)); + res->zticks += __stop_clock(0); + return (res); +} + +/* + * nis_ibmodify, this function modifys an entry in the information + * base by changing the columns that are marked as modified in the + * passed entry. Permission checking is done here as well. + */ +nis_result * +nis_ibmodify_svc(argp, reqstp) + ib_request *argp; + struct svc_req *reqstp; +{ + char pname[1024]; + nis_result *res; + + __start_clock(0); + if (verbose) + syslog(LOG_INFO, "Entry MODIFY_SVC: to table %s", + argp->ibr_name); + /* A quick and easy check... */ + if ((argp->ibr_obj.ibr_obj_len == 1) && + (__type_of(argp->ibr_obj.ibr_obj_val) != NIS_ENTRY_OBJ)) { + return (nis_make_error(NIS_INVALIDOBJ, 0, 0, 0, + __stop_clock(0))); + } + nis_getprincipal(pname, reqstp); + res = __nis_ibops(MOD_OP, argp, pname); + if (verbose) + syslog(LOG_INFO, "Done, exit status %s", + nis_sperrno(res->status)); + res->zticks += __stop_clock(0); + return (res); +} + +/* + * nis_remove, delete an entry from the indicated information base. + */ +nis_result * +nis_ibremove_svc(argp, reqstp) + ib_request *argp; + struct svc_req *reqstp; +{ + char pname[1024]; + nis_result *res; + + __start_clock(0); + if (verbose) + syslog(LOG_INFO, "Entry REMOVE_SVC: to table %s", + argp->ibr_name); + /* A quick and easy check... */ + if ((argp->ibr_obj.ibr_obj_len == 1) && + (__type_of(argp->ibr_obj.ibr_obj_val) != NIS_ENTRY_OBJ)) { + return (nis_make_error(NIS_INVALIDOBJ, 0, 0, 0, + __stop_clock(0))); + } + nis_getprincipal(pname, reqstp); + res = __nis_ibops(REM_OP, argp, pname); + if (verbose) + syslog(LOG_INFO, "Done, exit status %s", + nis_sperrno(res->status)); + res->zticks += __stop_clock(0); + return (res); +} + +/* + * nis_ibfirst() + * This function will return the first entry in an information base. + * It is provided primarily for backward compatibility with YP and its + * use is not encouraged. + */ +nis_result * +nis_ibfirst_svc(argp, reqstp) + ib_request *argp; + struct svc_req *reqstp; +{ + char pname[1024]; + nis_result *res; + + + __start_clock(0); + if (verbose) + syslog(LOG_INFO, "Entry FIRST_SVC : Fetch from table %s", + argp->ibr_name); + nis_getprincipal(pname, reqstp); + res = __nis_fnops(FIRST_OP, argp, pname); + if (verbose) + syslog(LOG_INFO, "Done, exit status %s", + nis_sperrno(res->status)); + res->zticks += __stop_clock(0); + return (res); +} + +/* + * nis_ibnext, return a subsequent entry in the information base. + */ +nis_result * +nis_ibnext_svc(argp, reqstp) + ib_request *argp; + struct svc_req *reqstp; +{ + char pname[1024]; + nis_result *res; + + __start_clock(0); + if (verbose) + syslog(LOG_INFO, "Entry NEXT_SVC : Fetch from table %s", + argp->ibr_name); + nis_getprincipal(pname, reqstp); + res = __nis_fnops(NEXT_OP, argp, pname); + if (verbose) + syslog(LOG_INFO, "Done, exit status %s", + nis_sperrno(res->status)); + res->zticks += __stop_clock(0); + return (res); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_common.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_common.c new file mode 100644 index 0000000000..001f4ec599 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_common.c @@ -0,0 +1,1318 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * nis_log_common.c + * + * This module contains logging functions that are common to the service and + * the log diagnostic utilities. + */ + +#include <syslog.h> +#include <sys/fcntl.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <errno.h> +#include <rpc/types.h> +#include <rpc/rpc.h> +#include <rpc/xdr.h> +#include <rpcsvc/nis.h> +#include <limits.h> +#include <string.h> +#include "nis_svc.h" +#include "nis_proc.h" +#include "log.h" +#include "nisdb_mt.h" +#include "ldap_util.h" + +extern void __db_disallowLDAP(); +extern void __db_allowLDAP(); + +/* string guard, it is always safe to print nilptr(char_pointer) */ +#define nilptr(s) ((s) ? (s) : "(nil)") + +#define getpagesize() sysconf(_SC_PAGESIZE) + +static int pagesize = 0; +int in_checkpoint = FALSE; +int need_checkpoint = 0; +extern NIS_HASH_TABLE old_stamp_list; +extern void add_updatetime(); +#define invalid_directory (__nis_get_tsd()->invalid_directory) + +static log_upd *last_upd_p; /* tmp ptr used during delta updates */ +static ulong_t upd_cnt; /* tmp cntr used during delta updates */ + +/* + * variable used to assure that nisping warnings both do not flood the log + * and also do get sent out periodically (moved to nis_log_common.c). + */ +long next_warntime = 0l; + +#define CHKPT_WARN_INTERVAL 3600 /* 1 hour interval in seconds */ + +extern int verbose; +int __nis_logfd = -1; +log_hdr *__nis_log = NULL; +ulong_t __nis_filesize = FILE_BLK_SZ; +ulong_t __nis_logsize; +ulong_t __maxloglen = MAXLOGLEN; +ulong_t __loghiwater = HIWATER; + +pid_t master_pid = 0; /* Master PROCESS id */ +ulong_t cur_xid; +nis_name cur_princp; + +/* data and routines for the updatetime cache begin here */ +/* + * updatetime_cache is a cache copy of the update timestamp for all + * the directories served by this server. It is kept in sync with the + * log file by adding time stamps in end_transaction(), and rebuilding + * the cache when checkpoint_log has finished. Using the cache is + * vital in readonly children, as the log file may be inconsistant. It + * is an optimization only otherwise. + */ + +NIS_HASH_TABLE *updatetime_cache = NULL; + +void init_updatetime(void); /* forward */ + +/* + * Purges the update timestamp cache + * Frees everything, and sets updatetime_cache to NULL. + */ +static void +purge_updatetime(void) +{ + LOCK_LIST(updatetime_cache, "purge_updatetime(updatetime_cache)"); + + if (updatetime_cache != NULL) { + /* clean up the timestamp cache */ + stamp_item *si; + while (si = (stamp_item *)nis_pop_item(updatetime_cache)) { + if (si->item.name) + free(si->item.name); + free(si); + } + free(updatetime_cache); + updatetime_cache = NULL; + } + + ULOCK_LIST(updatetime_cache, "purge_updatetime(updatetime_cache)"); +} + +static void +add_updatetime(nis_name name, ulong_t utime) +{ + stamp_item *si; + + if (updatetime_cache == NULL) { + init_updatetime(); + if (updatetime_cache == NULL) + return; + } + si = (stamp_item *)(nis_find_item(name, updatetime_cache)); + if (si) { + /* already cached - update the timestamp */ + si->utime = utime; + __nis_release_item((NIS_HASH_ITEM *)si, updatetime_cache, -1); + return; + } + si = (stamp_item *)(XCALLOC(1, sizeof (stamp_item))); + if (! si) { + syslog(LOG_CRIT, "add_updatetime(): out of memory."); + purge_updatetime(); + return; + } + si->item.name = (nis_name) XSTRDUP(name); + if (! si->item.name) { + syslog(LOG_CRIT, "add_updatetime(): out of memory."); + XFREE(si); + purge_updatetime(); + return; + } + si->utime = utime; + if (!__nis_insert_item_mt((NIS_HASH_ITEM *)si, updatetime_cache, 0)) { + XFREE(si->item.name); + XFREE(si); + si = (stamp_item *)(nis_find_item(name, updatetime_cache)); + if (si != 0) { + /* + * Apparently, another thread just created this item. + * Update the utime only if it increases. + */ + if (utime > si->utime) + si->utime = utime; + __nis_release_item((NIS_HASH_ITEM *)si, + updatetime_cache, -1); + } else { + syslog(LOG_ERR, + "add_updatetime(): error inserting %ld for %s", + utime, name != 0 ? name : "<NIL>"); + } + } +} + + +/* + * Routine to generate the cache copy of the update timestamp. + * This routine would be static, and we would rely on initializing the + * updatetime_cache when we tried to add_updatetime() to an empty cache, + * except that it can take some time to traverse the log, and the client + * could time out. + * + * We traverse the log from old to new, so that the newer updates will + * overwrite the older ones. + * + * Recursive calls would be possible if init_updatetime called + * add_updatetime, which ran out of memory, and freed the cache. Then + * the next call to add_updatetime from within init_updatetime would + * recursively call init_updatetime. If we run out of room building + * the cache, we just free everything and don't build it. + */ +static bool_t updating_cache = FALSE; +void +init_updatetime(void) +{ + log_upd *upd; + + if (updating_cache) + return; /* won't do recursive updates */ + + LOCK_LIST(updatetime_cache, "init_updatetime(updatetime_cache)"); + + if (updatetime_cache != NULL) { + ULOCK_LIST(updatetime_cache, + "init_updatetime(updatetime_cache)"); + purge_updatetime(); + LOCK_LIST(updatetime_cache, + "init_updatetime(updatetime_cache)"); + if (updatetime_cache != 0) { + /* OK, some other thread got in before us */ + ULOCK_LIST(updatetime_cache, + "init_updatetime(updatetime_cache)"); + return; + } + } + + updatetime_cache = (NIS_HASH_TABLE *) + calloc(1, sizeof (NIS_HASH_TABLE)); + if (! updatetime_cache) { + ULOCK_LIST(updatetime_cache, + "init_updatetime(updatetime_cache)"); + syslog(LOG_CRIT, "add_updatetime(): out of memory."); + return; + } + + ULOCK_LIST(updatetime_cache, "init_updatetime(updatetime_cache)"); + + upd = __nis_log->lh_head; + + updating_cache = TRUE; + + while (upd) { + add_updatetime(upd->lu_dirname, upd->lu_time); + upd = upd->lu_next; + } + + /* + * We need the code below only when this file is being + * compiled for rpc.nisd or nislog. The NEED_DIROBJ flag is + * declared in the CCFLAGS for rpc.nisd and nislog. + */ +#ifdef NEED_DIROBJ + /* + * In general, if we're a replica and are mapping from LDAP, + * the last update time for a directory isn't necessarily + * equal to the master's notion of same. Hence, if available, + * we prefer UPD_STAMP:s. + * + * However, if we're the master, the UPD_STAMP isn't necessarily + * up to date, so we still use the last update time. + */ + for (upd = __nis_log->lh_head; upd != 0; upd = upd->lu_next) { + XDR xdrs; + log_entry le; + nis_error err; + struct ticks ticks; + nis_object *obj = 0; + + /* + * Check if we're the master. We do this by asking + * __directory_object_msg() for the object, with the + * 'is_master' flag set. If we don't get an object, + * we aren't the master. + */ + err = __directory_object_msg(upd->lu_dirname, &ticks, 1, &obj, + 0); + if (err == NIS_SUCCESS && obj != 0) + continue; + + xdrmem_create(&xdrs, (char *)upd->lu_data, upd->lu_size, + XDR_DECODE); + memset((char *)&le, 0, sizeof (le)); + if (xdr_log_entry(&xdrs, &le)) { + if (le.le_type == UPD_STAMP) + add_updatetime(upd->lu_dirname, upd->lu_time); + xdr_free(xdr_log_entry, (char *)&le); + } + } +#endif /* NEED_DIROBJ */ + + updating_cache = FALSE; +} + +/* data and routines for the updatetime cache end here */ + +void +sync_header() +{ + if (! pagesize) /* msync misfeature */ + pagesize = getpagesize(); + + ASSERTWHELD(translog); + + if (msync((caddr_t)__nis_log, pagesize, MS_SYNC)) { + perror("msync:"); + syslog(LOG_CRIT, "Unable to msync LOG HEADER."); + abort(); + } +} + +void +sync_update(upd) + log_upd *upd; +{ + ulong_t start, end, len; + ulong_t size; + + ASSERTWHELD(translog); + + if (! pagesize) /* msync misfeature */ + pagesize = getpagesize(); + /* round start to the nearest page */ + start = ((ulong_t)upd) & (~(pagesize-1)); + /* round end to the nearest page */ + end = (((ulong_t)upd) + XID_SIZE(upd) + (pagesize-1)) & + (~(pagesize-1)); + size = end - start; + if (msync((caddr_t)start, size, MS_SYNC)) { + perror("msync:"); + syslog(LOG_CRIT, "Unable to msync LOG UPDATE."); + abort(); + } + +} + +/* + * call msync() on the entire transaction log -- used for fast delta + * updates. + */ +void sync_log() { + ulong_t start, end, len; + ulong_t size; + + ASSERTWHELD(translog); + + if (! pagesize) /* msync misfeature */ + pagesize = getpagesize(); + /* start at __nis_log */ + start = (ulong_t)__nis_log; + /* round end to the nearest page */ + end = (((ulong_t)__nis_log->lh_tail) + XID_SIZE(__nis_log->lh_tail) + + (pagesize-1)) & (~(pagesize-1)); + size = end - start; + if (msync((caddr_t)start, size, MS_SYNC)) { + perror("msync:"); + syslog(LOG_CRIT, "Unable to msync transaction log."); + abort(); + } +} + +/* + * Separate locking/unlocking of trans.log from begin_transaction et al., + * so that we can lock the trans.log without incurring the overhead of + * begin_transaction until we really have to. + * + * If 'wr' is set, a write lock is requested, and in this case, the + * 'trylock' parameter is also honored (it's ignored for read locks). + */ +int +lockTransLog(char *msg, int wr, int trylock) { + int lstat; + + if (msg == 0) + msg = "lockTransLog"; + + if (wr) { + if (trylock) + lstat = TRYWLOCK(translog); + else + lstat = WLOCK(translog); + } else { + lstat = RLOCK(translog); + } + if (lstat != 0) { + if (lstat == EBUSY && trylock) + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "%s: [%d] transaction log busy; try later", + msg, pthread_self()); + else + logmsg(MSG_NOTIMECHECK, LOG_ERR, + "%s: transaction log lock error %d", + msg, lstat); + } else { + if (verbose) + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "%s: [%d] holding transaction log %s lock", + msg, pthread_self(), wr ? "W" : "R"); + } + + return (lstat); +} + +void +unlockTransLog(char *msg, int wr) { + + if (msg == 0) + msg = "unlockTransLog"; + + if (wr) + WULOCK(translog); + else + RULOCK(translog); + + if (verbose) + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "%s: [%d] released transaction log %s lock", + msg, pthread_self(), wr ? "W" : "R"); +} + +/* + * This function starts a logged transaction. When the function returns + * the log is in the "update" state. If the function returns 0 then + * the transaction couldn't be started (NIS_TRYAGAIN) is returned to + * the client. Otherwise it returns the XID that should be used in + * calls to add update. + */ +int +begin_transaction(princp) + nis_name princp; +{ + int lstat; + + /* XXX for threads we set up a writer lock */ + + if (lockTransLog("begin_transaction", 1, 1) != 0) + return (0); + + /* + * Synchronization point. At this point we try to get + * exclusive access to the log. If this fails we return + * 0 so that the client can try again later. + */ + if (in_checkpoint) { + unlockTransLog("begin_transaction", 1); + return (0); + } + + if (__nis_log->lh_state != LOG_STABLE) { + unlockTransLog("begin_transaction", 1); + return (0); + } + + cur_xid = __nis_log->lh_xid + 1; + cur_princp = princp; + __nis_log->lh_state = LOG_UPDATE; + sync_header(); + + /* Returning with write lock held */ + + return (cur_xid); +} + +/* + * end_transaction + * + * This function does the actual commit, by setting the log header + * to be stable, and to have the latest XID present. Further it updates + * the time of the last update in the transaction to be 'ttime'. This + * is the time that is sent to the replicates so that they will know + * when they've picked up all of the updates up to this point. + * + * When the update was sourced from LDAP, we don't want to change the + * cached update time; hence the 'addUpdateTime' parameter. + */ +int +end_transaction_x(int xid, int addUpdateTime) { + + ASSERTWHELD(translog); + + /* Check to see if add_update was called at all */ + if (__nis_log->lh_tail->lu_xid != __nis_log->lh_xid) { + __nis_log->lh_xid++; + if (__nis_log->lh_xid != xid) { + syslog(LOG_CRIT, "end_transaction: Corrupted log!"); + abort(); + } + } + sync_update(__nis_log->lh_tail); + + __nis_log->lh_state = LOG_STABLE; + sync_header(); + + /* update the updatetime cache */ + if (addUpdateTime) { + add_updatetime(__nis_log->lh_tail->lu_dirname, + __nis_log->lh_tail->lu_time); + } + + unlockTransLog("end_transaction", 1); + + return (0); +} + +int +end_transaction(int xid) { + return (end_transaction_x(xid, 1)); +} + +/* + * Freeze the log tail and the log counter during fast updates. + */ +void begin_update() { + ASSERTWHELD(translog); + last_upd_p = __nis_log->lh_tail; + sync_header(); + upd_cnt = __nis_log->lh_num; +} + +/* + * Unfreeze the log tail and the log counter + */ +void end_update() { + ASSERTWHELD(translog); + __nis_log->lh_tail = last_upd_p; + last_upd_p = NULL; + __nis_log->lh_num = upd_cnt; + sync_header(); +} + +/* + * add_update() + * + * This function adds an update from the current transaction into + * the transaction log. It gets a bit tricky because it "allocates" + * all of it's memory from the file. Basically the transactions are + * laid down as follows : + * + * : prev update : + * +----------------+ + * | log_upd header | + * +----------------+ + * | XDR Encoded | + * : object from : + * | log_entry | + * +----------------+ + * | Directory name | + * +----------------+ + * : next update : + * + * The macro XID_SIZE calculates the offset to the start of the next + * update. + * returns 0 if the msync fails, otherwise it returns the log time. + * + * add_update is now a wrapper for the same code, but without + * msync's. replica_update calls add_update_nosync directly (the log tail and + * the log counter are then frozen); all others calls are to add_update, when + * the tail and the counter are not frozen. + */ + +ulong_t +add_update(log_entry *le) { + log_upd *upd; + ulong_t res; + + ASSERTWHELD(translog); + + __nis_logsize = (__nis_log->lh_tail) ? LOG_SIZE(__nis_log) : + sizeof (log_hdr); + last_upd_p = __nis_log->lh_tail; + upd_cnt = __nis_log->lh_num; + res = add_update_nosync(le, (void **)&upd); + sync_update(upd); + __nis_log->lh_tail = last_upd_p; + __nis_log->lh_num = upd_cnt; + sync_header(); + return (res); +} + +ulong_t +add_update_nosync(le, updp) + log_entry *le; + void **updp; +{ + XDR xdrs; + log_upd *upd; + ulong_t upd_size, total_size; + int ret; + + ASSERTWHELD(translog); + + if (in_checkpoint) + abort(); /* Can't happen, because begin_trans would fail */ + + __nis_logsize = (__nis_log->lh_tail) ? __nis_logsize : + sizeof (log_hdr); + + /* need both lines to find update size */ + if (le->le_princp == 0) + le->le_princp = cur_princp; + upd_size = xdr_sizeof((xdrproc_t)xdr_log_entry, le); + + /* see diagram above this function */ + total_size = __nis_logsize + NISRNDUP(sizeof (log_upd) + upd_size + + strlen(le->le_object.zo_domain) + 1); + if (total_size >= __nis_filesize) { + __nis_filesize = ((total_size / FILE_BLK_SZ) + 1) * FILE_BLK_SZ; + ret = (int)lseek(__nis_logfd, __nis_filesize, SEEK_SET); + if (ret == -1) { + syslog(LOG_ERR, + "Cannot grow transaction log, error %s", + strerror(errno)); + return (0); + } + ret = (int)write(__nis_logfd, "+", 1); + if (ret != 1) { + syslog(LOG_ERR, + "Cannot write one character to transaction log, error %s", + strerror(errno)); + return (0); + } + } + + __nis_logsize = total_size; + if (last_upd_p) + upd = (log_upd *) + (((ulong_t)last_upd_p) + XID_SIZE(last_upd_p)); + else { + upd = (log_upd *)((ulong_t)(__nis_log)+ + NISRNDUP(sizeof (log_hdr))); + } + + if ((ulong_t)(upd) > ((ulong_t)(__nis_log) + __loghiwater)) { + struct timeval curtime; + if ((gettimeofday(&curtime, 0) != -1) && + (curtime.tv_sec > next_warntime)) { + next_warntime = curtime.tv_sec + CHKPT_WARN_INTERVAL; + syslog(LOG_CRIT, + "NIS+ server needs to be checkpointed. Use \"nisping -C domainname\""); + } + } + + /* + * If this operation is a table entry addition or removal, and the + * TSD 'doingModify' flag is set, use a special magic number. + */ + if ((le->le_type == ADD_IBASE || le->le_type == REM_IBASE) && + __nisdb_get_tsd()->doingModify) { + upd->lu_magic = LOG_UPD_MODMAG; + } else { + upd->lu_magic = LOG_UPD_MAGIC; + } + + upd->lu_prev = last_upd_p; + upd->lu_next = NULL; + upd->lu_xid = cur_xid; + upd->lu_time = le->le_time; + upd->lu_size = upd_size; + xdrmem_create(&xdrs, (char *)upd->lu_data, upd->lu_size, XDR_ENCODE); + if (!xdr_log_entry(&xdrs, le)) { + syslog(LOG_ERR, "add_update: xdr_log_entry failed"); + return (0); + } + upd->lu_dirname = (char *)(NISRNDUP((((ulong_t)upd) + sizeof (log_upd) + + upd->lu_size))); + strcpy(upd->lu_dirname, le->le_object.zo_domain); + if (updp) + *updp = upd; + if (last_upd_p) + last_upd_p->lu_next = upd; + else + __nis_log->lh_head = upd; + last_upd_p = upd; + upd_cnt++; + need_checkpoint = 1; + return (1); +} + +/* + * make_stamp() - moved from nis_subr_proc.c to this file since + * nisbackup also needs this. + * + * This function adds a "null" entry into the log that indicates either + * the directory is stable or gone. When a directory is deleted, a tombstone + * (entry with a timestamp of 0) is written to the log. This prevents prior + * activity on the log from being confused with current activity. When a + * directory is resynchronized with a full dump, timestamp information is + * lost so this is used to mark the directory as being stable up to that point. + */ +void +make_stamp(name, stime) + nis_name name; + ulong_t stime; +{ + log_entry le; + ulong_t xid; + + memset((char *)&le, 0, sizeof (le)); + le.le_princp = nis_local_principal(); + le.le_time = stime; + le.le_type = UPD_STAMP; + le.le_name = name; + __type_of(&(le.le_object)) = NIS_NO_OBJ; + le.le_object.zo_name = ""; + le.le_object.zo_owner = ""; + le.le_object.zo_group = ""; + le.le_object.zo_domain = name; + if (xid = begin_transaction(le.le_princp)) { + if (! add_update(&le)) + fprintf(stderr, "make_stamp: could not add_update\n"); + end_transaction(xid); + } else { + fprintf(stderr, + "make_stamp: zero xid from begin_transaction\n"); + } +} + +/* + * nis_cptime() + * + * This function will ask the indicated replicate for the last + * update it has seen to the given directory. + */ +ulong_t +nis_cptime(replica, name) + nis_server *replica; + nis_name name; +{ + return (nis_cptime_msg(replica, name, TRUE, TRUE)); +} + +/* + * nis_cptime_msg() + * + * The guts of nis_cptime(). If "domsg" is FALSE, messages are suppressed, + * unless verbose mode is enabled. + */ +ulong_t +nis_cptime_msg(replica, name, domsg, tryhard) + nis_server *replica; + nis_name name; + bool_t domsg; + bool_t tryhard; +{ + CLIENT *clnt; + enum clnt_stat status; + struct timeval tv; + ulong_t res; + + clnt = nis_make_rpchandle(replica, 0, NIS_PROG, NIS_VERSION, + ZMH_DG|ZMH_AUTH|(tryhard ? 0 : ZMH_NOFALLBACK), 1024, 512); + /* If we can't contact it, return the safe answer */ + if (! clnt) { + if (verbose) + syslog(LOG_INFO, "nis_cptime: could not contact %s", + replica->name); + return (0); + } + + tv.tv_sec = 6; /* retry time out */ + tv.tv_usec = 0; + clnt_control(clnt, CLSET_RETRY_TIMEOUT, (void *)&tv); + tv.tv_sec = 10; + status = clnt_call(clnt, NIS_CPTIME, xdr_nis_name, (char *)&name, + xdr_u_long, (char *)&res, tv); + if (status != RPC_SUCCESS && (domsg || verbose)) { + syslog(LOG_WARNING, + "nis_cptime: RPC error srv='%s', dir='%s', err='%s'", + replica->name, name, clnt_sperrno(status)); + res = 0; + } + if (clnt->cl_auth != 0) { + auth_destroy(clnt->cl_auth); + clnt->cl_auth = 0; + } + clnt_destroy(clnt); + if (verbose) + syslog(LOG_INFO, "nis_cptime: returning %d for %s from %s", + res, name, replica->name); + return (res); +} + +/* + * __make_name() + * + * This function prints out a nice name for a search entry. + */ +char * +__make_name(le) + log_entry *le; +{ + static char namestr[2048]; + int i; + + if (le->le_attrs.le_attrs_len) + strcpy(namestr, "[ "); + else + namestr[0] = '\0'; + + for (i = 0; i < le->le_attrs.le_attrs_len; i++) { + strcat(namestr, le->le_attrs.le_attrs_val[i].zattr_ndx); + strcat(namestr, " = "); + if (le->le_attrs.le_attrs_val[i].zattr_val.zattr_val_len) + strcat(namestr, + le->le_attrs.le_attrs_val[i].zattr_val.zattr_val_val); + else + strcat(namestr, "(nil)"); + strcat(namestr, ", "); + } + + if (le->le_attrs.le_attrs_len) { + namestr[strlen(namestr) - 2] = '\0'; + strcat(namestr, " ],"); + } + strcat(namestr, le->le_name); + return (namestr); +} + +/* + * __log_resync() + * + * This function will relocate the log to its current position in + * memory. Errors returned : + * 0 success + * -1 Illegal Update + * -2 Missing Data + * -3 Not enough updates + * + * Private flag (p): + * FNISD called from nisd + * FNISLOG called from nislog + * FCHKPT called from checkpoint_log + */ +int +__log_resync(log, p) + log_hdr *log; + int p; +{ + log_upd *prev, *cur; + int i, ret; + ulong_t addr_p; + + ASSERTWHELD(translog); + + /* + * Resync the hard way. In this section of code we + * reconstruct the log pointers by calculating where + * the pieces would be placed. Our calcuation is + * verified by the presence of the appropriate MAGIC + * number at the address we've calculated. If any + * magic number isn't present, we note that the log + * is corrupt and exit the service. The user will + * have to either patch the log, or resync the slaves + * by hand. (Forced resync) + * + */ + + if (verbose) + syslog(LOG_INFO, "Resynchronizing transaction log."); + + addr_p = NISRNDUP((ulong_t)log + sizeof (log_hdr)); + prev = NULL; + + /* + * if there are any transactions, this is the first one. + */ + if (log->lh_num) + log->lh_head = (log_upd *)(addr_p); + if (verbose) { + syslog(LOG_INFO, "Log has %d transactions in it.", log->lh_num); + syslog(LOG_INFO, "Last valid transaction is %d.", log->lh_xid); + } + + for (i = 0; i < log->lh_num; i++) { + cur = (log_upd *)(addr_p); + if (cur->lu_magic != LOG_UPD_MAGIC && + cur->lu_magic != LOG_UPD_MODMAG) { + syslog(LOG_ERR, + "__log_resync: Transaction #%d bad magic number", i); + if (p != FNISLOG) { + syslog(LOG_ERR, +"__log_resync: log truncated, resync to propagate possibly lost changes"); + } else { + printf( + "__log_resync: Transaction #%d has a bad magic number\n", i); + } + break; /* major corruption */ + } else if (NISRNDUP((ulong_t)cur + + sizeof (log_upd) + cur->lu_size) >= + ((ulong_t)log + __nis_filesize) || + NISRNDUP((ulong_t)cur + + sizeof (log_upd) + cur->lu_size) <= + (ulong_t)cur) { +/* + * Verify that lu_size seems reasonable by checking that the address to be used + * for cur->lu_dirname falls inside the mapped section from the transaction log. + * Note: Test for "<=", since cur+sizeof(log_upd)+cur->lu_size could overflow a + * u_long. + */ + syslog(LOG_ERR, + "__log_resync: Transaction #%d: bad size %d", + i, cur->lu_size); + if (p != FNISLOG) { + syslog(LOG_ERR, +"__log_resync: log truncated, resync to propagate possibly lost changes"); + } else { + printf( + "__log_resync: Transaction #%d bad size %d\n", i, cur->lu_size); + } + break; /* also major corruption */ + } + + /* Fix up the link lists */ + log->lh_tail = cur; /* Track the current update */ + if (prev) + prev->lu_next = cur; + cur->lu_prev = prev; + cur->lu_next = NULL; + cur->lu_dirname = (char *)(NISRNDUP((((ulong_t)cur) + + sizeof (log_upd) + + cur->lu_size))); + prev = cur; + + /* move to next update in the list */ + if (verbose) { + syslog(LOG_INFO, "Resync'd transaction #%d.", i+1); + syslog(LOG_INFO, "Directory was '%s'.", + nilptr(cur->lu_dirname)); + } + addr_p += XID_SIZE(cur); + } + if (i < log->lh_num) { + if (verbose) + syslog(LOG_INFO, "%d valid transactions.", i); + if (prev) { + if (prev->lu_xid > log->lh_xid) { + syslog(LOG_INFO, + "__log_resync: Incomplete last update transaction, removing."); + while (log->lh_tail->lu_xid > log->lh_xid) { + log->lh_tail = log->lh_tail->lu_prev; + i--; + if (! log->lh_tail) + break; + } + } else + log->lh_tail = prev; + log->lh_num = i; + } else { + log->lh_tail = NULL; + log->lh_num = 0; + } + } + __nis_logsize = (log->lh_tail) ? LOG_SIZE(log) : sizeof (log_hdr); + if (verbose) + syslog(LOG_INFO, "Log size is %d bytes", __nis_logsize); + + if (p == FNISLOG) { + /* called from nislog, don't ftruncate() or msync() */ + return (0); + } else if (p == FCHKPT) { + /* called from checkpoint, truncate transaction log file */ + __nis_filesize = (((__nis_logsize / FILE_BLK_SZ) + 1) * + FILE_BLK_SZ); + ret = ftruncate(__nis_logfd, __nis_filesize); + if (ret == -1) { + syslog(LOG_ERR, + "__log_resync: Cannot truncate transaction log file"); + return (-1); + } + ret = (int)lseek(__nis_logfd, 0L, SEEK_CUR); + if (ret == -1) { + syslog(LOG_ERR, + "__log_resync: cannot seek to begining of transaction log file"); + return (-1); + } + ret = (int)lseek(__nis_logfd, __nis_filesize, SEEK_SET); + if (ret == -1) { + syslog(LOG_ERR, + "__log_resync: cannot increase transaction log file size"); + return (-1); + } + ret = (int)write(__nis_logfd, "+", 1); + if (ret != 1) { + syslog(LOG_ERR, + "__log_resync: cannot write one byte to transaction log file"); + return (-1); + } + } + + log->lh_addr = log; + ret = msync((caddr_t)log, __nis_logsize, MS_SYNC); + /* Could take a while */ + if (ret == -1) { + syslog(LOG_ERR, "msync() error in __log_resync()"); + abort(); + } + return (0); +} + +/* + * map_log() + * + * This function maps in the logfile into the address space of thenis + * server process. After the log is successfully mapped it is "relocated" + * so that the pointers in the file are valid for the log's position in + * memory. Once relocated, the log is ready for use. This function returns + * 0 if the log file is OK and !0 if it is corrupted. + * + * Error numbers : + * -4 File not found. + * -5 Cannot MMAP file + * -6 Corrupt file, bad magic number in header. + * -7 Unknonwn (illegal) state value + */ + +int +map_log(logname, p) + char *logname; + int p; /* Private flag: */ + /* FNISD=called from nisd */ + /* FNISLOG=from nislog */ +{ + int error, fd, ret; + log_upd *update; + log_hdr tmp_hdr; + log_entry *entry_1, *entry_2; + struct stat st; + long log_size; + + if (lockTransLog("map_log", 1, 0) != 0) + return (-9); + + sprintf(logname, "%s", LOG_FILE); + if (stat(logname, &st) == -1) { + if (p == FNISLOG) { /* called from nislog */ + unlockTransLog("map_log", 1); + return (-4); + } + + __nis_logfd = open(logname, O_RDWR+O_CREAT, 0600); + if (__nis_logfd == -1) { + syslog(LOG_ERR, "Unable to open logfile '%s'", logname); + unlockTransLog("map_log", 1); + return (-4); + } + + /* Make this file as it doesn't exist */ + ret = (int)lseek(__nis_logfd, 0L, SEEK_CUR); + if (ret == -1) { + syslog(LOG_ERR, + "map_log: cannot seek to begining of transaction log file"); + unlockTransLog("map_log", 1); + return (-1); + } + ret = (int)lseek(__nis_logfd, __nis_filesize, SEEK_SET); + if (ret == -1) { + syslog(LOG_ERR, + "map_log: cannot increase transaction log file size"); + + unlockTransLog("map_log", 1); + return (-1); + } + /* writing a / all the time seemed silly */ + ret = (int)write(__nis_logfd, "+", 1); + if (ret != 1) { + syslog(LOG_ERR, + "map_log: cannot write one byte to transaction log file"); + unlockTransLog("map_log", 1); + return (-1); + } + fstat(__nis_logfd, &st); + } else { /* transaction log file exists */ + __maxloglen = (st.st_size > __maxloglen) ? st.st_size : + __maxloglen; + __nis_logfd = open(logname, O_RDWR, 0600); + if (__nis_logfd == -1) { + syslog(LOG_ERR, "Unable to open logfile '%s'", logname); + unlockTransLog("map_log", 1); + return (-4); + } + } + __nis_filesize = st.st_size; + + + if (p == FNISLOG) { + /* called from nislog */ + __nis_log = (log_hdr *)mmap(0, __nis_filesize, + PROT_READ+PROT_WRITE, MAP_PRIVATE, __nis_logfd, 0); + } else { + /* called from nisd */ + __nis_log = (log_hdr *)mmap(0, __maxloglen, + PROT_READ+PROT_WRITE, MAP_SHARED, __nis_logfd, 0); + } + + if ((int)(__nis_log) == -1) { + syslog(LOG_ERR, + "Unable to map logfile of length %ld into address space.", + __maxloglen); + close(__nis_logfd); + unlockTransLog("map_log", 1); + return (-5); + } + + if (__nis_log->lh_magic != LOG_HDR_MAGIC) { + /* If it's NULL then we just created the file */ + if (__nis_log->lh_magic == 0) { + (void) memset(__nis_log, 0, sizeof (log_hdr)); + __nis_log->lh_state = LOG_STABLE; + __nis_log->lh_magic = LOG_HDR_MAGIC; + __nis_log->lh_addr = __nis_log; + if (p == FNISD) + msync((caddr_t)__nis_log, sizeof (log_hdr), + MS_SYNC); + } else { + syslog(LOG_ERR, "Illegal log file, remove and restart"); + unlockTransLog("map_log", 1); + return (-6); + } + + } + switch (__nis_log->lh_state) { + case LOG_STABLE : + if (verbose) + syslog(LOG_INFO, "Log state is STABLE."); + error = __log_resync(__nis_log, p); + if (error) { + unlockTransLog("map_log", 1); + return (error); /* resync failed */ + } + break; + case LOG_RESYNC : + if (verbose) + syslog(LOG_INFO, "Log state is RESYNC."); + error = __log_resync(__nis_log, p); + if ((error) || (p == FNISLOG)) { + unlockTransLog("map_log", 1); + return (error); + } + __nis_log->lh_state = LOG_STABLE; + msync((caddr_t)__nis_log, sizeof (log_hdr), MS_SYNC); + break; + case LOG_UPDATE : + if (verbose) + syslog(LOG_INFO, "Log state is IN UPDATE."); + error = __log_resync(__nis_log, p); + if ((error) || (p == FNISLOG)) { + unlockTransLog("map_log", 1); + return (error); + } + update = __nis_log->lh_tail; + /* + * If the last update was a dir invalidator, + * a full resync will happen and restore + * consistancy. + */ + if (update->lu_time == ULONG_MAX) { + invalid_directory = strdup(update->lu_dirname); + } + /* + * Check to see if the update was written to the + * log. + */ + if (update->lu_xid == __nis_log->lh_xid) { + /* never made it, so we're done. */ + __nis_log->lh_state = LOG_STABLE; + msync((caddr_t)__nis_log, + sizeof (log_hdr), MS_SYNC); + unlockTransLog("map_log", 1); + return (0); + } + if (update->lu_time == ULONG_MAX) { + unlockTransLog("map_log", 1); + return (0); + } + /* Back out the change */ + + /* + * We're just trying to clean the local DB, and hence + * don't want to use the LDAP repository. + */ + __db_disallowLDAP(); + ret = abort_transaction(0); + __db_allowLDAP(); + + /* + * Although begin_transaction() returns with the + * translog lock held, we're executing in a different + * process. Since abort_transaction() will unlock, + * we must not. + */ + return (ret); + break; + case LOG_CHECKPOINT : + if (verbose) + syslog(LOG_INFO, "Log state is IN CHECKPOINT"); + if (p == FNISLOG) { + error = __log_resync(__nis_log, p); + unlockTransLog("map_log", 1); + return (error); + } + strcpy(logname, nis_data(BACKUP_LOG)); + fd = open(logname, O_RDONLY); + if (fd == -1) { + syslog(LOG_ERR, "map_log: missing backup."); + unlockTransLog("map_log", 1); + return (-1); + } + error = read(fd, &log_size, sizeof (long)); + if (error == -1) { + syslog(LOG_ERR, + "map_log: unable to read backup"); + close(fd); + unlockTransLog("map_log", 1); + return (-1); + } + error = read(fd, __nis_log, log_size); + close(fd); + if ((error == -1) || (error != log_size)) { + __nis_log->lh_state = LOG_CHECKPOINT; + syslog(LOG_ERR, + "map_log: Backup log truncated, fatal error."); + unlockTransLog("map_log", 1); + return (-1); + } + /* Manually resync the log */ + error = __log_resync(__nis_log, p); + if (! error) { + __nis_log->lh_state = LOG_STABLE; + msync((caddr_t)__nis_log, sizeof (log_hdr), + MS_SYNC); + unlink(logname); + break; + } + syslog(LOG_ERR, + "map_log: Unable to resync after checkpoint restore."); + unlockTransLog("map_log", 1); + return (error); + break; + default : + syslog(LOG_ERR, + "Illegal log state, aborting."); + unlockTransLog("map_log", 1); + return (-7); + } + /* At this point everything should be cool. */ + unlockTransLog("map_log", 1); + return (0); +} + + +/* + * This code is very similar to nis_name_of() in libnsl. + * When a server lives in its own domain, its database files are named + * relative to the parent domain (that is, where it's credentials are + * stored. This version of nis_name_of() will take that into account. + * + * This is probably not the best file to put this routine into, but + * it is shared by rpc.nisd, nisbackup, and nisrestore. All of these + * programs need a common routine to determine the name of a database file. + */ + +/* + * relative_name() + * This internal function will remove from the NIS name, the domain + * name of the current server, this will leave the unique part in + * the name this becomes the "internal" version of the name. If this + * function returns NULL then the name we were given to resolve is + * bad somehow. + * + * A dynamically-allocated string is returned. + */ + +nis_name +relative_name(s) + char *s; /* string with the name in it. */ +{ + char *d; + char *buf; + int dl, sl; + name_pos p; + + if (s == NULL) + return (NULL); + + d = __nis_rpc_domain(); + if (d == NULL) + return (NULL); + dl = strlen(d); /* _always dot terminated_ */ + + buf = strdup(s); + if (buf == NULL) + return (NULL); + strcpy(buf, s); /* Make a private copy of 's' */ + sl = strlen(buf); + + if (dl == 1) { /* We're the '.' directory */ + buf[sl-1] = '\0'; /* Lose the 'dot' */ + return (buf); + } + + p = nis_dir_cmp(buf, d); + + /* 's' is above 'd' in the tree */ + if ((p == HIGHER_NAME) || (p == NOT_SEQUENTIAL) || (p == SAME_NAME)) { + free(buf); + return (NULL); + } + + /* Insert a NUL where the domain name starts in the string */ + buf[(sl - dl) - 1] = '\0'; + + /* Don't return a zero length name */ + if (buf[0] == '\0') { + free((void *)buf); + return (NULL); + } + + return (buf); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_svc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_svc.c new file mode 100644 index 0000000000..40a7fdaf00 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_svc.c @@ -0,0 +1,1076 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1990-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nis_log_svc.c + * + * This is the other part of the NIS+ logging facility. It contains functions + * used by the service. The other part, nis_log_common.c contains functions + * that are shared between the service and nislog the executable. + * + * These functions are implemented on top of the mmap primitives and + * will change when user level threads are available. Mostly they can benefit + * from having reader/writer locks in the log itself. + * + * The normal sequence of events is as follows : + * At boot time : + * map_log(SHARED); + * + * For each update : + * xid = begin_transaction(who); + * add_update(entry); + * ... + * Then on commit : + * end_transaction(xid); + * Else on abort : + * abort_transaction(); + */ + +#include <time.h> +#include <syslog.h> +#include <sys/fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <rpc/types.h> +#include <rpc/rpc.h> +#include <rpc/xdr.h> +#include <rpcsvc/nis.h> +#include <limits.h> +#include "nis_proc.h" +#include "log.h" +#include "nis_mt.h" +#include "nisdb_mt.h" + +u_long cur_xid; +nis_name cur_princp; + +/* + * in_checkpoint, in_update, updatetime_cache moved to nis_log_common.c, + * extern'd here. + */ +extern NIS_HASH_TABLE *updatetime_cache; +extern int in_checkpoint; +extern int in_update; +extern log_hdr *__nis_log; +extern int __nis_logfd; +extern unsigned long __maxloglen, __loghiwater, __nis_logsize, __nis_filesize; + +/* + * variable used to assure that nisping warnings both do not flood the log + * and also do get sent out periodically (moved to nis_log_common.c). + */ +extern long next_warntime; + +extern nis_object* get_root_object(); + +/* + * abort_transaction() + * + * This function backs out a transaction in progress that was interrupted + * by a server crash or some other unrecoverable error. + * Note that the updates are backed out in reverse order from the way they + * were applied. This is currently not strictly required for correctness + * but will be required if a gang-modify operation is ever supported. + */ +int +abort_transaction(n_xid) + int n_xid; +{ + log_hdr *log = __nis_log; + XDR xdrs; + u_char *data; + log_entry le; + log_upd *cur, *upd; + int error; + nis_error stat; + nis_db_result *dbres; + nis_db_list_result *lres; + u_long xid; + int prevWasModify, doingModify = 0; + + ASSERTWHELD(translog); + + /* + * Check to see if the update was written to the + * log. + */ + upd = log->lh_tail; + xid = upd->lu_xid; + for (cur = upd; cur->lu_xid > log->lh_xid; cur = cur->lu_prev) { + xdrmem_create(&xdrs, (char *)cur->lu_data, cur->lu_size, + XDR_DECODE); + memset((char *)&le, 0, sizeof (le)); + if (! xdr_log_entry(&xdrs, &le)) { + syslog(LOG_ERR, + "Unable to decode transaction in log! Data LOST"); + continue; + } + + prevWasModify = doingModify; + doingModify = cur->lu_magic == LOG_UPD_MODMAG; + /* + * Toggle the TSD 'doingModify' flag if necessary. + */ + if (doingModify && !prevWasModify) { + __nisdb_get_tsd()->doingModify = 1; + } else if (!doingModify && prevWasModify) { + __nisdb_get_tsd()->doingModify = 0; + } + + switch (le.le_type) { + case ADD_NAME : + dbres = db_lookup(le.le_name); + if (dbres->status == NIS_SUCCESS) { + stat = __db_remove(le.le_name, + dbres->obj); + if (stat != NIS_SUCCESS) { + syslog(LOG_CRIT, + "abort_transaction: Failed to remove '%s'.", le.le_name); + abort(); + } + } else if (dbres->status != NIS_NOTFOUND) { + syslog(LOG_CRIT, + "abort_transaction: Internal database error (%d)", dbres->status); + abort(); + } + /* Successfully backed it out */ + break; + + case REM_NAME : + dbres = db_lookup(le.le_name); + if (dbres->status == NIS_NOTFOUND) { + stat = __db_add(le.le_name, + &(le.le_object), 0); + if (stat != NIS_SUCCESS) { + syslog(LOG_CRIT, + "abort_transaction: Failed to add '%s'.", le.le_name); + abort(); + } + } else if (dbres->status != NIS_SUCCESS) { + syslog(LOG_CRIT, + "abort_transaction: Internal database error."); + abort(); + } + /* Successfully backed it out */ + break; + + /* Skip the new modified object */ + case MOD_NAME_NEW : + break; + + /* Add the old modified object over the new one */ + case MOD_NAME_OLD : + dbres = db_lookup(le.le_name); + if (dbres->status != NIS_SUCCESS) { + syslog(LOG_CRIT, + "abort_transaction: Internal database error."); + abort(); + } + if (! same_oid(dbres->obj, &le.le_object)) { + stat = __db_add(le.le_name, + &(le.le_object), 1); + if (stat != NIS_SUCCESS) { + syslog(LOG_CRIT, + "abort_transaction: Failed to unmodify '%s'.", le.le_name); + abort(); + } + } + /* Successfully backed it out */ + break; + + case ADD_IBASE : + lres = db_list(le.le_name, + le.le_attrs.le_attrs_len, + le.le_attrs.le_attrs_val); + if (lres->status == NIS_SUCCESS) { + if (lres->numo > 1) { + syslog(LOG_CRIT, + "abort_transaction: Internal error, log entry corrupt (%s).", + __make_name(&le)); + abort(); + } + stat = __db_remib(le.le_name, + le.le_attrs.le_attrs_len, + le.le_attrs.le_attrs_val); + if (stat != NIS_SUCCESS) { + syslog(LOG_CRIT, + "abort_transaction: Failed to remove '%s'.", __make_name(&le)); + abort(); + } + } else if (lres->status != NIS_NOTFOUND) { + syslog(LOG_CRIT, + "abort_transaction: Internal database error."); + abort(); + } + /* Successfully backed it out */ + break; + case REM_IBASE : + lres = db_list(le.le_name, + le.le_attrs.le_attrs_len, + le.le_attrs.le_attrs_val); + if (lres->status == NIS_NOTFOUND) { + stat = __db_addib(le.le_name, + le.le_attrs.le_attrs_len, + le.le_attrs.le_attrs_val, + &(le.le_object)); + if (stat != NIS_SUCCESS) { + syslog(LOG_CRIT, + "abort_transaction: Failed to re-add '%s'.", __make_name(&le)); + abort(); + } + } else if (lres->status == NIS_SUCCESS) { + if (lres->numo > 1) { + syslog(LOG_CRIT, + "abort_transaction: Internal error, log entry corrupt (%s).", + __make_name(&le)); + abort(); + } + } else { + syslog(LOG_CRIT, + "abort_transaction: Internal database error."); + abort(); + } + break; + } + xdr_free((xdrproc_t)xdr_log_entry, (char *)&le); + /* back up one update */ + log->lh_tail = cur->lu_prev; + } + + if (log->lh_tail->lu_xid != log->lh_xid) { + unlockTransLog("abort_transaction", 1); + return (-1); /* Didn't back them all out! */ + } + + log->lh_state = LOG_STABLE; + sync_header(); + + unlockTransLog("abort_transaction", 1); + + return (0); +} + +NIS_HASH_TABLE stamp_list = NIS_HASH_TABLE_MT_INIT; +NIS_HASH_TABLE old_stamp_list = NIS_HASH_TABLE_MT_INIT; + +static int +add_stamp(name, utime) + nis_name name; + u_long utime; +{ + stamp_item *si; + + si = (stamp_item *)(nis_find_item(name, &stamp_list)); + if (si) { + if (si->utime < utime) + si->utime = utime; + __nis_release_item((NIS_HASH_ITEM *)si, &stamp_list, -1); + return (0); + } + si = (stamp_item *)(XCALLOC(1, sizeof (stamp_item))); + if (! si) { + syslog(LOG_CRIT, "add_stamp(): out of memory."); + abort(); + } + si->item.name = (nis_name) XSTRDUP(name); + if (! si->item.name) { + syslog(LOG_CRIT, "add_stamp(): out of memory."); + abort(); + } + si->utime = utime; + if (__nis_insert_item_mt((NIS_HASH_ITEM *)si, &stamp_list, 0) == 0) { + free(si->item.name); + free(si); + si = __nis_find_item_mt(name, &stamp_list, 0, 0); + /* + * If we can find it now, then __nis_insert_item_mt() + * probably failed because the item already existed, which + * is OK. If we can't find it, it's still possible that + * everything's fine (item didn't exist when we first tried + * to find it, had been created when we tried to insert, and + * then had been removed when we tried to find it again), + * so we just issue a warning. + */ + if (si == 0) { + syslog(LOG_WARNING, "add_stamp(): unable to insert " + "item for \"%s\"", name); + } + } + return (1); /* first time we've seen it. */ +} + +static void +add_old_stamp(name, utime, uxid) + nis_name name; + u_long utime; + u_long uxid; +{ + old_stamp_item *si; + + si = (old_stamp_item *)(nis_find_item(name, &old_stamp_list)); + if (si) { + if (si->utime < utime) + si->utime = utime; + __nis_release_item((NIS_HASH_ITEM *)si, &old_stamp_list, -1); + return; + } + si = (old_stamp_item *)(XCALLOC(1, sizeof (old_stamp_item))); + if (! si) { + syslog(LOG_CRIT, "add_old_stamp(): out of memory."); + abort(); + } + si->item.name = (nis_name) XSTRDUP(name); + if (! si->item.name) { + syslog(LOG_CRIT, "add_old_stamp(): out of memory."); + abort(); + } + si->utime = utime; + si->stamped = 0; + si->xid = uxid; + if (__nis_insert_item_mt((NIS_HASH_ITEM *)si, &old_stamp_list, 0) == + 0) { + free(si->item.name); + free(si); + si = __nis_find_item_mt(name, &old_stamp_list, 0, 0); + /* + * If we can find it now, then __nis_insert_item_mt() + * probably failed because the item already existed, which + * is OK. If we can't find it, it's still possible that + * everything's fine (item didn't exist when we first tried + * to find it, had been created when we tried to insert, and + * then had been removed when we tried to find it again), + * so we just issue a warning. + */ + if (si == 0) { + syslog(LOG_WARNING, "add_old_stamp(): unable to insert " + "item for \"%s\"", name); + } + } +} + + +/* + * This routine inserts the specified update timestamp for the specified + * directory into the new checkpointed data area. It tries to insert a + * timestamp with the xid specified. If this xid is less than or equal + * to the last xid appended to the new checkpointed data area, then + * increment the offset_xid and use the (last_xid + offset_xid) as the + * new xid for the timestamp entry. + */ +static void +insert_chkpt_stamp(name, utime, xid, offset_xid, last_xid, addr_p) +nis_name name; /* directory name */ +ulong utime; /* last update timestamp */ +ulong xid; /* last xid seen for this directory */ +ulong *offset_xid; /* xid offset which is only incremented */ + /* when xid to be inserted is <= the last */ + /* xid in the new checkpoint area. */ +ulong *last_xid; /* last xid for checkpointed transactions */ +ulong *addr_p; /* address pointer where the update */ + /* timestamp should go. */ +{ + XDR o_xdrs; + log_upd *o_upd; + log_entry to_le; + u_long o_upd_size, o_le_size; + + ASSERTWHELD(translog); + + if (name == NULL) + return; + memset((char *)&to_le, 0, sizeof (to_le)); + to_le.le_princp = nis_local_principal(); + to_le.le_time = utime; + to_le.le_type = UPD_STAMP; + to_le.le_name = name; + __type_of(&(to_le.le_object)) = NIS_NO_OBJ; + to_le.le_object.zo_name = ""; + to_le.le_object.zo_owner = ""; + to_le.le_object.zo_group = ""; + to_le.le_object.zo_domain = name; + o_le_size = xdr_sizeof(xdr_log_entry, &to_le); + /* value 20 below is a random added for extra padding */ + if ((o_upd = (log_upd *) malloc(o_le_size + sizeof (log_upd) + 20 + + strlen(to_le.le_object.zo_domain))) == NULL) { + syslog(LOG_CRIT, "insert_chkpt_stamp(): out of memory."); + abort(); + } + o_upd->lu_magic = LOG_UPD_MAGIC; + o_upd->lu_prev = NULL; + o_upd->lu_next = NULL; + if (xid <= *last_xid) { + (*offset_xid)++; + o_upd->lu_xid = (*last_xid) + (*offset_xid); + } else + o_upd->lu_xid = xid; + *last_xid = o_upd->lu_xid; + o_upd->lu_time = utime; + o_upd->lu_size = o_le_size; + xdrmem_create(&o_xdrs, (char *)o_upd->lu_data, o_upd->lu_size, + XDR_ENCODE); + if (!xdr_log_entry(&o_xdrs, &to_le)) { + syslog(LOG_CRIT, + "insert_chkpt_stamp(): xdr_log_entry failed."); + abort(); } + o_upd->lu_dirname = (char *)(NISRNDUP((((u_long) o_upd) + + sizeof (log_upd) + o_upd->lu_size))); + strcpy(o_upd->lu_dirname, to_le.le_object.zo_domain); + o_upd_size = XID_SIZE(o_upd); + memmove((char *)(*addr_p), (char *)o_upd, o_upd_size); + *addr_p += o_upd_size; + + if (verbose) { + syslog(LOG_INFO, + "checkpoint_log: tombstone xid %d, time %d (%s) created", + o_upd->lu_xid, o_upd->lu_time, o_upd->lu_dirname); + } + free(o_upd); +} + + +/* + * make_tombstone creates the timestamp (tombstone) for the name + * specified. + * If no old timestamp found in old_stamp_list, just stamp it with utime. + * If it's already stamped with an old timestamp don't stamp it again. + * If it's not already stamped with the old timestamp, stamp it with utime. + * + * make_tombstone will also remove the item from old_stamp_list. + */ +static void +make_tombstone(name, utime) +nis_name name; +ulong utime; +{ + old_stamp_item *o_si; + log_entry le; + u_long xid; + + ASSERTWHELD(translog); + + o_si = (old_stamp_item *)(nis_find_item(name, &old_stamp_list)); + if ((!o_si) || (!o_si->stamped)) { + if (verbose) + syslog(LOG_INFO, + "make_tombstone: making timestamp %d for %s", + utime, name); + + /* set up the log entry */ + memset((char *)&le, 0, sizeof (le)); + le.le_princp = nis_local_principal(); + le.le_time = utime; + le.le_type = UPD_STAMP; + le.le_name = name; + __type_of(&(le.le_object)) = NIS_NO_OBJ; + le.le_object.zo_name = ""; + le.le_object.zo_owner = ""; + le.le_object.zo_group = ""; + le.le_object.zo_domain = name; + cur_xid = __nis_log->lh_xid + 1; + cur_princp = le.le_princp; + add_update(&le); + __nis_log->lh_xid++; + sync_header(); + } + if (o_si) { + o_si = nis_remove_item(o_si->item.name, &old_stamp_list); + if (o_si != 0) { + if (o_si->item.name != 0) + free(o_si->item.name); + free(o_si); + } + } +} + + +/* + * checkpoint_log() + * + * This function removes all transactions up to the indicated time from + * the log file. + * + * It returns 0 on failure, 1 on success. + */ + +int +checkpoint_log() +{ + log_upd *cur, *nxt; + u_long addr_p, upd_size; + int error; + int fd, i, num = 0; + XDR xdrs; + log_entry le; + int first, ret; + stamp_item *si; + old_stamp_item *o_si; + char backup[1024]; + ulong last_xid = 0, offset_xid = 0; + + if (verbose) + syslog(LOG_INFO, "Checkpointing the log."); + + if (lockTransLog("checkpoint_log", 1, 0) != 0) + return (0); + + { + __nis_hash_item_mt *e; + while ((e = __nis_pop_item_mt(&stamp_list)) != 0) { + XFREE(e); + } + } + + if (__nis_log->lh_state != LOG_STABLE) { + syslog(LOG_INFO, + "checkpoint_log: Unable to checkpoint, log unstable."); + unlockTransLog("checkpoint_log", 1); + return (0); + } + + /* XXX This should be a spin locks for the transaction funcs */ + in_checkpoint = TRUE; + strcpy(backup, nis_data(BACKUP_LOG)); + + fd = open(backup, O_WRONLY+O_SYNC+O_CREAT+O_TRUNC, 0600); + if (fd == -1) { + syslog(LOG_ERR, + "checkpoint_log: Unable to checkpoint, can't open backup log (%s).", + backup); + in_checkpoint = FALSE; + unlockTransLog("checkpoint_log", 1); + return (0); + } + + /* + * Make a backup of the log in two steps, write the size of the log + * and then a copy of the log. + */ + __nis_logsize = (__nis_log->lh_tail) ? LOG_SIZE(__nis_log) : + sizeof (log_hdr); + if (write(fd, &__nis_logsize, sizeof (long)) != sizeof (long)) { + syslog(LOG_ERR, + "checkpoint_log: Unable to checkpoint, disk full."); + close(fd); + unlink(backup); + in_checkpoint = FALSE; + unlockTransLog("checkpoint_log", 1); + return (0); + } + if (verbose) + syslog(LOG_INFO, + "checkpoint_log: Backup log %s created.", backup); + /* + * Now set the state to RESYNC so if we have to recover and read + * this back in, and we get screwed while reading it, we won't + * get into major trouble. This still leaves open one window : + * a) Start checkpoint + * b) Successfully backup to BACKUP + * c) Set log state to CHECKPOINT + * c) crash. + * d) Reboot and start reading in the backup log + * e) crash. + * f) reboot and now the log appears to be resync'ing without + * all of its data. + */ + __nis_log->lh_state = LOG_RESYNC; + if (write(fd, __nis_log, __nis_logsize) != __nis_logsize) { + syslog(LOG_ERR, + "checkpoint_log: Unable to checkpoint, disk full."); + close(fd); + unlink(backup); + __nis_log->lh_state = LOG_STABLE; + sync_header(); + in_checkpoint = FALSE; /* Unlock */ + unlockTransLog("checkpoint_log", 1); + return (0); + } + if (verbose) + syslog(LOG_INFO, "checkpoint_log: Backup log %s written.", + backup); + close(fd); + + /* If we crash here we're ok since the log hasn't changed. */ + __nis_log->lh_state = LOG_CHECKPOINT; + sync_header(); + + addr_p = (u_long)(__nis_log->lh_head); + /* + * If this transaction hasn't been fully replicated then + * leave it in the log. Othersize don't bother moving it. + */ + if (verbose) + syslog(LOG_INFO, "checkpoint_log: Checkpointing ..."); + + for (cur = __nis_log->lh_head, num = 0; cur; cur = nxt) { + nxt = cur->lu_next; + xdrmem_create(&xdrs, (char *)cur->lu_data, cur->lu_size, + XDR_DECODE); + memset((char *)&le, 0, sizeof (log_entry)); + if (!xdr_log_entry(&xdrs, &le)) { + syslog(LOG_ERR, + "checkpoint_log: cannot read transaction log entry."); + /* free up any malloc'd storage */ + xdr_free(xdr_log_entry, (char *)&le); + unlockTransLog("checkpoint_log", 1); + return (0); + } + /* ignore dummy entries used by delta-update */ + if (cur->lu_time == ULONG_MAX) { + xdr_free(xdr_log_entry, (char *)&le); + continue; + } + first = add_stamp(cur->lu_dirname, cur->lu_time); + if (! nis_isstable(&le, first)) { + /* check the old stamp list */ + o_si = (old_stamp_item *) + (nis_find_item(cur->lu_dirname, + &old_stamp_list)); + if ((o_si != NULL) && (o_si->stamped == 0)) { + /* insert the old timestamp */ + insert_chkpt_stamp(o_si->item.name, o_si->utime, + o_si->xid, &offset_xid, &last_xid, + &addr_p); + o_si->stamped = 1; + num++; + } else if ((o_si == NULL) && + (le.le_type == UPD_STAMP)) { + /* + * if the the trasaction saved is of UPD_STAMP + * type, then add it into the old_stamp_list + * and mark it as stamped. + */ + add_old_stamp(cur->lu_dirname, cur->lu_time, + cur->lu_xid); + o_si = (old_stamp_item *) + (nis_find_item(cur->lu_dirname, + &old_stamp_list)); + if (o_si != 0) + o_si->stamped = 1; + } + __nis_release_item((NIS_HASH_ITEM *)o_si, + &old_stamp_list, -1); + cur->lu_xid += offset_xid; + last_xid = cur->lu_xid; + if (verbose) + syslog(LOG_INFO, + "checkpoint_log: Transaction %d, time %d (%s) kept", + cur->lu_xid, cur->lu_time, cur->lu_dirname); + + upd_size = XID_SIZE(cur); + memmove((char *)addr_p, (char *)cur, upd_size); + addr_p += upd_size; + num++; + } else { + if (verbose) + syslog(LOG_INFO, + "checkpoint_log: Transaction %d, time %d (%s) removed", + cur->lu_xid, cur->lu_time, cur->lu_dirname); + add_old_stamp(cur->lu_dirname, cur->lu_time, + cur->lu_xid); + } + /* free up any malloc'd storage */ + xdr_free(xdr_log_entry, (char *)&le); + } + + if (num == 0) { + /* Deleted all of the entries. */ + if (verbose) + syslog(LOG_INFO, + "checkpoint_log: all entries removed."); + __nis_log->lh_head = NULL; + __nis_log->lh_tail = NULL; + __nis_log->lh_num = 0; + sync_header(); + ret = ftruncate(__nis_logfd, FILE_BLK_SZ); + if (ret == -1) { + syslog(LOG_ERR, + "checkpoint_log: cannot truncate transaction log file"); + abort(); + } + __nis_filesize = FILE_BLK_SZ; + ret = (int)lseek(__nis_logfd, __nis_filesize, SEEK_SET); + if (ret == -1) { + syslog(LOG_ERR, + "checkpoint_log: cannot increase transaction log file size"); + abort(); + } + ret = (int)write(__nis_logfd, "+", 1); + if (ret != 1) { + syslog(LOG_ERR, + "cannot write one character to transaction log file"); + abort(); + } + __nis_logsize = sizeof (log_hdr); + if (msync((caddr_t)__nis_log, __nis_logsize, MS_SYNC)) { + perror("msync:"); + syslog(LOG_CRIT, "unable to mysnc() LOG"); + abort(); + } + + } else { + if (verbose) + syslog(LOG_INFO, + "checkpoint_log: some entries removed."); + __nis_log->lh_xid = last_xid; + __nis_log->lh_num = num; + sync_header(); + error = __log_resync(__nis_log, FCHKPT); + if (error) { + syslog(LOG_CRIT, + "checkpoint_log: Checkpoint failed, unable to resync."); + abort(); + } + } + + /* + * Write back "last update" stamps for all of the directories + * we've processed. + */ + LOCK_LIST(&stamp_list, "checkpoint_log(stamp_list)"); + in_checkpoint = FALSE; + while (stamp_list.first) { + nis_result *res; + nis_object *d_obj; + int srv; + ulong_t ttime; + + si = (stamp_item *)(stamp_list.first); + (void) nis_remove_item(si->item.name, &stamp_list); + d_obj = NULL; + res = nis_lookup(si->item.name, MASTER_ONLY); + if ((res->status != NIS_NOTFOUND) && + (res->status != NIS_NOSUCHNAME)) { + d_obj = res->objects.objects_val; + if (res->status == NIS_SUCCESS) { + if (__type_of(d_obj) != NIS_DIRECTORY_OBJ) { + syslog(LOG_WARNING, + "checkpoint_log: removed timestamp for %s (not a directory)", + si->item.name); + } else if ((srv = nis_isserving(d_obj)) != 0) { + /* + * If we're a replica, we want to keep + * the last_update() time as our + * UPD_STAMP. We may have LDAP-sourced + * updates that are more recent, but + * that shouldn't show up to a + * NIS_CPTIME. + */ + if (srv == 1) { + ttime = si->utime; + } else { + ttime = + last_update(si->item.name); + if (ttime == 0) + ttime = si->utime; + } + make_tombstone(si->item.name, + ttime); + } else { + if (verbose) + syslog(LOG_INFO, + "checkpoint_log: removed timestamp for %s (we no longer serve)", + si->item.name); + } + } else { + /* + * If nis_lookup() failed, we most likely + * aren't the master. Hence, we retain the + * existing last_update() time as our + * UPD_STAMP + */ + ttime = last_update(si->item.name); + if (ttime == 0) + ttime = si->utime; + make_tombstone(si->item.name, ttime); + } + } else { + if (verbose) + syslog(LOG_INFO, + "checkpoint_log: removed timestamp for %s (no longer exists)", + si->item.name); + } + XFREE(si->item.name); + XFREE(si); + nis_freeresult(res); + } + ULOCK_LIST(&stamp_list, "checkpoint_log(stamp_list)"); + + __nis_log->lh_state = LOG_STABLE; + sync_header(); + unlink(backup); + next_warntime = 0l; + if (verbose) + syslog(LOG_INFO, "checkpoint_log: Checkpoint complete."); + + /* re-generate the timestamp cache */ + init_updatetime(); + unlockTransLog("checkpoint_log", 1); + + return (1); +} + +/* + * last_update() + * + * This function scans the log backwards for the last timestamp of an update + * that occurred on the named directory. If it cannot find an update for that + * directory it returns 0L. + */ +u_long +last_update(name) + nis_name name; +{ + log_upd *upd; + + /* + * Last update time for the root object is gotten from the object + * itself, or, it that does not exist, 0. + */ + if (root_object_p(name)) { + nis_object* robj; + + if (robj = get_root_object()) { + u_long ttime; + ttime = robj->zo_oid.mtime; + nis_destroy_object(robj); + return (ttime); + } else { + return (0L); + } + } + + /* + * Always uses the cached update timestamp. However, if somehow + * the cache is not available, it will fallback to the old way of + * using the transaction log. In a readonly child, we insist on + * using the cache to avoid using corrupted data in the shared + * transaction log address space. + */ + + if (updatetime_cache != NULL) { + stamp_item *si; + u_long utime; + + si = (stamp_item *)(nis_find_item(name, updatetime_cache)); + if (si) { + utime = si->utime; + if (verbose) + syslog(LOG_INFO, + "last_update: (cached) returning %d for %s.", + utime, name); + __nis_release_item(si, updatetime_cache, -1); + /* + * If the update time is ULONG_MAX, the directory + * is invalid and should be dumped in its entirety. + * That's supposed to be handled during startup + * (the 'invalid_directory' code in nis_main.c), + * but if we got ULONG_MAX here, we do what we + * can to force a full dump by returning zero. + */ + return ((utime != ULONG_MAX) ? utime : 0); + } + } else { + if (verbose) + syslog(LOG_INFO, "last_update: No cache."); + if (readonly) { + if (verbose) + syslog(LOG_INFO, + "last_update: returning 0 for %s", name); + return (0L); + } + (void) lockTransLog("last_update", 1, 0); + upd = __nis_log->lh_tail; + while (upd) { + if (nis_dir_cmp(name, upd->lu_dirname) == SAME_NAME) { + if (verbose) + syslog(LOG_INFO, + "last_update: (log) returning %d for %s.", + upd->lu_time, name); + unlockTransLog("last_update", 1); + return (upd->lu_time); + } + upd = upd->lu_prev; + } + unlockTransLog("last_update", 1); + } + if (verbose) + syslog(LOG_INFO, "last_update: returning 0 for %s", name); + return (0L); +} + +/* + * entries_since() + * + * This function returns a malloc'd array of entries for the passed + * directory since the given timestamp. This is used when dumping + * deltas to the replicas. It returns NULL if it can't find any + * entries. + * + * As part of the fix for bug 4186012, avoid including entries with + * a time stamp greater than or equal to the current time. This avoids + * distributing information about entries in the current second until + * that time has passed. This means that, say, replicas see all the + * updates for that second rather than there being a question about + * which of the many possible updates with the same time stamp they + * last saw. In the event of there being outstanding entries the + * directory is added to the ping list so that the master will, in + * time, nisping the replicas again. + */ +void +entries_since(dobj, dtime, res) + nis_object *dobj; + u_long dtime; + log_result *res; +{ + log_upd *upd; + log_upd *first; + u_char **updates, **tmp; + int n_updates, total_updates, i; + XDR xdrs; + time_t currtime; + char name[NIS_MAXPATH]; + + snprintf(name, NIS_MAXPATH, "%s.%s", dobj->zo_name, dobj->zo_domain); + + if (verbose) + syslog(LOG_INFO, "entries_since: dir '%s', time %d.", + name, dtime); + + res->lr_status = NIS_SUCCESS; + (void) lockTransLog("entries_since", 0, 0); + first = __nis_log->lh_tail; + while (first) { + if ((nis_dir_cmp(name, first->lu_dirname) == SAME_NAME) && + (first->lu_time == dtime)) + break; + first = first->lu_prev; + } + + if (! first) { + syslog(LOG_INFO, "entries_since: Replica of %s is out of date.", + name); + res->lr_status = NIS_RESYNC; + unlockTransLog("entries_since", 0); + return; + } + + total_updates = 128; + updates = (u_char **)XMALLOC(total_updates * sizeof (char *)); + if (! updates) { + if (verbose) + syslog(LOG_INFO, "entries_since(): out of memory"); + res->lr_status = NIS_NOMEMORY; + unlockTransLog("entries_since", 0); + return; + } + + n_updates = 0; + currtime = time(0); + for (upd = first->lu_next; upd && (upd->lu_xid <= __nis_log->lh_xid); + upd = upd->lu_next) { + if ((nis_dir_cmp(name, upd->lu_dirname) == SAME_NAME) && + (upd->lu_size > 4)) { + if (upd->lu_time >= currtime) { + if (verbose) + syslog(LOG_INFO, + "entries_since: avoiding returning entries updated " + "this second (from xid %d)", upd->lu_xid); + /* + * Make sure we ping the replicas + * soon. The master will only ping + * replicas once the updateBatchingTimeout + * has passed, so we estabtabllish a ping + * time that's the current time, minus the + * updateBatchingTimeout, plus one. This + * ensures the ping doesn't occur this second + * but ASAP afterwards. + */ + add_pingitem(dobj, + currtime - updateBatchingTimeout() + 1, + &ping_list); + break; + } + if (verbose) + syslog(LOG_INFO, "entries_since: xid %d", + upd->lu_xid); + updates[n_updates++] = &(upd->lu_data[0]); + if ((n_updates % 128) == 126) { + tmp = updates; + total_updates += 128; + updates = (u_char **) + XMALLOC(total_updates*sizeof (u_char*)); + if (! updates) { + syslog(LOG_INFO, + "entries_since(): out of memory"); + res->lr_status = NIS_NOMEMORY; + XFREE(tmp); + unlockTransLog("entries_since", 0); + return; + } + for (i = 0; i < n_updates; i++) + updates[i] = tmp[i]; + XFREE(tmp); + } + } + } + syslog(LOG_INFO, "entries_since: Found %d deltas for dir %s", + n_updates, name); + if (n_updates == 0) { + res->lr_status = NIS_SUCCESS; + XFREE(updates); + unlockTransLog("entries_since", 0); + return; + } + res->lr_entries.lr_entries_val = (log_entry *) + XCALLOC(n_updates, sizeof (log_entry)); + if (! res->lr_entries.lr_entries_val) { + res->lr_status = NIS_NOMEMORY; + XFREE(updates); + unlockTransLog("entries_since", 0); + return; + } + for (i = 0; i < n_updates; i++) { + /* + * NOTE : Since we're xdr decoding we don't really care + * what the "size" value is, we're only going to decode + * one log entry anyway. + */ + xdrmem_create(&xdrs, (char *)updates[i], 0x4000000, + XDR_DECODE); + if (!xdr_log_entry(&xdrs, (res->lr_entries.lr_entries_val+i))) { + res->lr_status = NIS_NOMEMORY; + break; + } + } + res->lr_entries.lr_entries_len = n_updates; + XFREE(updates); + unlockTransLog("entries_since", 0); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_main.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_main.c new file mode 100644 index 0000000000..49c2170331 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_main.c @@ -0,0 +1,1385 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 1990-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nis_main.c + * + * This is the main() module for the NIS+ service. It is compiled separately + * so that the service can parse certain options and initialize the database + * before starting up. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <rpc/rpc.h> +#include <syslog.h> +#include <signal.h> +#include <ucontext.h> +#include <time.h> +#include <string.h> +#include <wait.h> +#include <sys/types.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <errno.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/nis_db.h> +#include <dirent.h> +#include "nis_svc.h" +#include "nis_proc.h" +#include "log.h" +#include <stropts.h> +#include <poll.h> +#include <limits.h> +#include <rpcsvc/nis_dhext.h> +#include <ldap_parse.h> +#include <ldap_util.h> +#include <values.h> +#include <unistd.h> + + +/* Default size for the RPC credential cache */ +#define CRED_CACHESZ_DEF 1024 + +#ifdef MEM_DEBUG +extern void init_malloc(); +extern void xdump(); +#endif + +FILE *cons = NULL; +extern int (*__clear_directory_ptr)(nis_name); +extern int clear_directory(nis_name); + +#define invalid_directory (__nis_get_tsd()->invalid_directory) + +/* Defined in nis_log_common.c */ +extern int need_checkpoint; /* The log is "dirty" */ +extern pid_t master_pid; /* Master PROCESS id */ + +int _rpcsvcdirty; +int max_children = 128; /* Very large number */ +int secure_level = 2; /* Security level 2 = max supported */ +int force_checkpoint = 0; /* Set when we wish to force a c.p. op */ +int checkpoint_all = 0; /* Set when we wish to cp entire db */ +int readonly = 0; /* When true the service is "read only" */ +int readonly_pid = 0; /* Our child who is watching out for us */ +int hup_received = 0; /* To tell us when to exit */ +int auth_verbose = 0; /* Messages on auth info */ +int resolv_pid = 0; /* Resolv server pid */ +CLIENT *resolv_client = NULL; /* Resolv client handle */ +char *resolv_tp = "ticots"; /* Resolv netid used on resolv_setup() */ +struct timeval start_time; /* Time service started running. */ +extern unsigned long __maxloglen; /* maximum transaction log size */ +unsigned int heap_start; /* sbrk(0) at start of time */ + +extern uint_t next_refresh; /* next time to re-load dot file */ + +#ifdef DEBUG +int debug = 1; +#else +int debug = 0; +#endif + +struct upd_log *nis_log; /* Pointer to the mmap'd log. */ + +extern int optind, opterr; +extern char *optarg; + +extern nis_object* get_root_object(); +extern cp_result *do_checkpoint(); + +extern void nis_prog_svc(); +extern void ypprog_svc(); +extern void ypprog_1(); + +/* routines for update timestamp cache */ +extern void init_updatetime(); + +extern void *__nis_lock_db_directory(nis_name, int, int *, char *); +extern int __nis_ulock_db_directory(nis_name, int, int, char *); +extern db_status db_defer(nis_name); +extern db_status db_commit(nis_name); +extern db_status db_rollback(nis_name); + +/* + * Global state variables, these variables contain information about the + * state of the server. + */ +int verbose = 0; /* Verbose mode, LOG_INFO messages are */ + /* generated for most functions. */ +int root_server = 0; /* TRUE if this server is a root server */ +int static_root = 0; /* TRUE if the network is partitioned. */ +int emulate_yp = FALSE; /* NIS compat mode or not */ +int resolv_flag = 0; /* Resolv on no host match */ +int tflag = FALSE; /* command-line option 't' */ +NIS_HASH_TABLE ping_list = NIS_HASH_TABLE_MT_INIT; + /* List of directory names that need to */ + /* be notified of updates */ +int ping_pid = 0; /* Pinger pid for pinger process */ +NIS_HASH_TABLE upd_list = NIS_HASH_TABLE_MT_INIT; + /* List of directory names that have */ + /* pending updates */ +ulong_t cp_time = 0; /* Time of last checkpoint */ +NIS_HASH_TABLE checkpoint_list = NIS_HASH_TABLE_MT_INIT; + /* List of directory names that have */ + /* pending checkpoint */ + +static void +timetodie(proc) +int proc; +{ + hup_received = 1; +} + +void +check_updaters() +{ + ping_item *pp, *nxt; + int pid; + ulong_t lu, curtime = ~0UL; + struct sigaction sigactn; + int pending_updates = 0; + struct timeval tp; + NIS_HASH_TABLE tmpupd; + ping_item *tmppp; + + /* Check if any item on the list is due for update */ + if (gettimeofday(&tp, 0) != -1) { + curtime = tp.tv_sec; + __nis_init_hash_table(&tmpupd, 0); + LOCK_LIST(&upd_list, "check_updaters(upd_list)"); + for (pp = (ping_item *)(upd_list.first); pp != 0; pp = nxt) { + nxt = (ping_item *)(pp->item.nxt_item); + if (pp->utime <= curtime) { + pending_updates = 1; + /* Save a copy on the local list */ + tmppp = malloc(sizeof (*tmppp)); + if (tmppp != NULL) { + /* Copy pointers OK */ + *tmppp = *pp; + (void) __nis_insert_item_mt(tmppp, + &tmpupd, 0); + } +#ifdef NIS_MT_DEBUG + else { + abort(); + } +#endif /* NIS_MT_DEBUG */ + } + } + ULOCK_LIST(&upd_list, "check_updaters(upd_list)"); + if (!pending_updates) { + if (verbose) + syslog(LOG_INFO, + "check_updaters: no pending updates"); + return; + } + } + + if (cons) + fprintf(cons, "check_updaters : starting resync\n"); + if (verbose) { + syslog(LOG_INFO, "check_updaters: Starting resync."); + } + for (pp = (ping_item *)(tmpupd.first); pp; pp = nxt) { + nxt = (ping_item *)(pp->item.nxt_item); + lu = last_update(pp->item.name); + if (cons) + fprintf(cons, "check_updaters : update %s\n", + pp->item.name); + if ((pp->mtime > lu) && (pp->utime <= curtime)) { + if (replica_update(pp)) { + tmppp = nis_remove_item(pp->item.name, + &upd_list); + if (tmppp != NULL) { + XFREE(tmppp->item.name); + if (tmppp->obj) + nis_destroy_object(tmppp->obj); + XFREE(tmppp); + } + /* The 'tmpupd' list is purged later */ + } else { + /* + * No use continuously retrying, so backoff + * exponentially. + */ + tmppp = pp; + pp = __nis_find_item_mt(pp->item.name, + &upd_list, -1, NULL); + if (pp != NULL) { + if (curtime != ~0UL) + pp->utime = curtime + pp->delta; + pp->delta *= 2; + if (pp->delta > MAX_UPD_LIST_TIME_INCR) { + pp->delta = MAX_UPD_LIST_TIME_INCR; + if (cons) + fprintf(cons, + "check_updaters: unable to resync %s\n", + pp->item.name); + syslog(LOG_WARNING, + "check_updaters: unable to resync %s", + pp->item.name); + } + (void) __nis_release_item(pp, &upd_list, + -1); + } + pp = tmppp; + } + } else if (pp->mtime <= lu) { + tmppp = nis_remove_item(pp->item.name, &upd_list); + if (tmppp != NULL) { + XFREE(tmppp->item.name); + if (tmppp->obj) + nis_destroy_object(tmppp->obj); + XFREE(tmppp); + } + } + } + /* + * We just copied the 'item.name' and 'obj' pointers for the + * elements in the 'tmpupd' list, so we only need to free the + * list elements themselves. + */ + for (pp = (ping_item *)tmpupd.first; pp != NULL; pp = tmppp) { + tmppp = (ping_item *)pp->item.nxt_item; + free(pp); + } +} + +void +check_pingers() +{ + ping_item *pp, *nxt; + struct timeval ctime; + + LOCK_LIST(&ping_list, "check_pingers(ping_list)"); + if (ping_list.first == NULL) { + ULOCK_LIST(&ping_list, "check_pingers(ping_list)"); + return; + } + + gettimeofday(&ctime, 0); + if (cons) + fprintf(cons, "check_pingers : \n"); + for (pp = (ping_item *)ping_list.first; pp; pp = nxt) { + /* save next pointer in case we remove it */ + nxt = (ping_item *)(pp->item.nxt_item); + + if ((pp->mtime + updateBatchingTimeout()) > ctime.tv_sec) + continue; + + if (verbose && cons) + fprintf(cons, "check_pingers: ping %s\n", + pp->item.name); + +/* + * A successful return from ping_replicas() means that + * a ping request has been sent to the replicas. It does + * not ensures that replica has indeed received the ping + * request. + */ + nis_remove_item(pp->item.name, &ping_list); + if (!ping_replicas(pp)) + nis_insert_item((NIS_HASH_ITEM *)pp, &ping_list); + } + ULOCK_LIST(&ping_list, "check_pingers(ping_list)"); +} + +#include <netconfig.h> + +extern int __rpc_bindresvport_ipv6(int, struct sockaddr *, int *, int, + char *); + +/* + * A modified version of svc_tp_create(). The difference is that + * nis_svc_tp_create() will try to bind to a privilege port if the + * the server is to emulate YP and it's using the INET[6] protocol family. + * + * The high level interface to svc_tli_create(). + * It tries to create a server for "nconf" and registers the service + * with the rpcbind. It calls svc_tli_create(); + */ +static SVCXPRT * +nis_svc_tp_create(dispatch, prognum, versnum, nconf) + void (*dispatch)(); /* Dispatch function */ + ulong_t prognum; /* Program number */ + ulong_t versnum; /* Version number */ + struct netconfig *nconf; /* Netconfig structure for the network */ +{ + SVCXPRT *xprt; + int fd; + struct t_info tinfo; + + if (nconf == (struct netconfig *)NULL) { + (void) syslog(LOG_ERR, + "nis_svc_tp_create: invalid netconfig structure for prog %d vers %d", + prognum, versnum); + return ((SVCXPRT *)NULL); + } + fd = RPC_ANYFD; + if ((emulate_yp) && (strcmp(nconf->nc_protofmly, NC_INET) == 0 || + strcmp(nconf->nc_protofmly, NC_INET6) == 0)) { + fd = t_open(nconf->nc_device, O_RDWR, &tinfo); + if (fd == -1) { + (void) syslog(LOG_ERR, + "nis_svc_tp_create: could not open connection for %s", + nconf->nc_netid); + return ((SVCXPRT *)NULL); + } + if (__rpc_bindresvport_ipv6(fd, (struct sockaddr *)NULL, + (int *)NULL, 8, + nconf->nc_protofmly) == -1) { + (void) t_close(fd); + fd = RPC_ANYFD; + } + } + xprt = svc_tli_create(fd, nconf, (struct t_bind *)NULL, 0, 0); + if (xprt == (SVCXPRT *)NULL) { + return ((SVCXPRT *)NULL); + } + (void) rpcb_unset(prognum, versnum, nconf); + if (svc_reg(xprt, prognum, versnum, dispatch, nconf) == FALSE) { + (void) syslog(LOG_ERR, + "nis_svc_tp_create: Could not register prog %d vers %d on %s", + prognum, versnum, nconf->nc_netid); + SVC_DESTROY(xprt); + return ((SVCXPRT *)NULL); + } + return (xprt); +} + +/* + * Modified version of 'svc_create' that maintains list of handles. + * The only difference between this and svc_create is that + * 1. nis_svc_create maintains the list of handles created so that they + * can be reused later for re-registeration. + * This is required for nis_put_offline. + * 2. nis_svc_create uses the public netconfig interfaces, instead of + * private rpc interfaces. + */ +struct xlist { + SVCXPRT *xprt; /* Server handle */ + struct xlist *next; /* Next item */ +}; + +/* + * Note: nis_xprtlist is only used in nis_svc_create() and nis_svc_reg() + * below. Since nis_svc_create() is used only in the initial, + * single-threaded, phase of the rpc.nisd, no synchronization is + * needed. + */ + +/* A link list of all the handles */ +static struct xlist *nis_xprtlist = (struct xlist *)NULL; + +static int +nis_svc_create(dispatch, prognum, versnum, target_nc_flag) + void (*dispatch)(); /* Dispatch function */ + ulong_t prognum; /* Program number */ + ulong_t versnum; /* Version number */ + unsigned target_nc_flag; /* value of netconfig flag */ +{ + struct xlist *l; + int num = 0; + SVCXPRT *xprt; + struct netconfig *nconf; + NCONF_HANDLE *handle; + + if ((handle = setnetconfig()) == NULL) { + (void) syslog(LOG_ERR, + "nis_svc_create: could not get netconfig information"); + return (0); + } + while (nconf = getnetconfig(handle)) { + if (!(nconf->nc_flag & target_nc_flag)) + continue; + for (l = nis_xprtlist; l; l = l->next) { + if (strcmp(l->xprt->xp_netid, nconf->nc_netid) == 0) { + /* Found an old one, use it */ + (void) rpcb_unset(prognum, versnum, nconf); + if (svc_reg(l->xprt, prognum, versnum, + dispatch, nconf) == FALSE) + (void) syslog(LOG_ERR, + "nis_svc_create: could not register prog %d vers %d on %s", + prognum, versnum, nconf->nc_netid); + else + num++; + break; + } + } + if (l == (struct xlist *)NULL) { + /* It was not found. Now create a new one */ + xprt = nis_svc_tp_create(dispatch, prognum, + versnum, nconf); + if (xprt) { + l = (struct xlist *)malloc(sizeof (*l)); + if (l == (struct xlist *)NULL) { + (void) syslog(LOG_ERR, + "nis_svc_create: no memory"); + return (0); + } + l->xprt = xprt; + l->next = nis_xprtlist; + nis_xprtlist = l; + num++; + } + } + } + endnetconfig(handle); + /* + * In case of num == 0; the error messages are generated by the + * underlying layers; and hence not needed here. + */ + return (num); +} + +/* + * Establish resync service for the specified directory, per the + * nisplusLDAPresyncService and nisplusLDAPdumpError configuration + * attributes. + */ +db_status +nis_put_offline(nis_name dirname, bool_t fullDump) +{ + db_status stat = DB_SUCCESS; + + switch (ldapConfig.resyncService) { + + case directory_locked: + if (!fullDump) { + /* Write lock the specified directory */ + if (__nis_lock_db_directory(dirname, -1, 0, dirname) == + NULL) { + if (verbose) + syslog(LOG_ERR, + "nis_put_offline: Error locking \"%s\"", dirname); +#ifdef NIS_MT_DEBUG + abort(); +#endif /* NIS_MT_DEBUG */ + } + break; + } + /* Else, if full dump, fall through to 'from_copy' */ + + case from_copy: + /* + * Defer changes for the directory. + */ + stat = db_defer(dirname); + break; + + case from_live: + /* Nothing to do */ + break; + + default: +#ifdef NIS_MT_DEBUG + abort(); +#endif /* NIS_MT_DEBUG */ + stat = DB_INTERNAL_ERROR; + break; + } + + return (stat); +} + +/* + * Resume full service for specified directory + */ +db_status +nis_put_online(nis_name dirname, __nis_defer_t commitAction, bool_t fullDump) +{ + char *dirl, *curdir, *nxtdir; + int ret = 1; + db_status stat = DB_SUCCESS; + + switch (ldapConfig.resyncService) { + + case directory_locked: + if (!fullDump) { + /* Unlock the directory */ + if (!__nis_ulock_db_directory(dirname, -1, 0, + dirname)) { + if (verbose) + syslog(LOG_ERR, + "nis_put_online: Error unlocking \"%s\"", dirname); +#ifdef NIS_MT_DEBUG + abort(); +#endif /* NIS_MT_DEBUG */ + } + break; + } + /* Else, if full dump, fall through to 'from_copy' */ + + case from_copy: + /* Commit or rollback */ + if (commitAction == d_commit) + stat = db_commit(dirname); + else if (commitAction == d_rollback) + stat = db_rollback(dirname); + else { + stat = DB_BADQUERY; +#ifdef NIS_MT_DEBUG + abort(); +#endif /* NIS_MT_DEBUG */ + } + break; + + case from_live: + /* Nothing to do */ + break; + + default: +#ifdef NIS_MT_DEBUG + abort(); +#endif /* NIS_MT_DEBUG */ + break; + } + + return (stat); +} + +static void +update_cache_data(nis_object* root_obj) +{ + /* make sure the cache data is accurate */ + writeColdStartFile(&(root_obj->DI_data)); + __nis_CacheRestart(); +} + +static void +print_options() +{ + fprintf(stderr, "Options supported by this version :\n"); + fprintf(stderr, "\th - print this help message.\n"); + fprintf(stderr, "\tC - open diagnostic channel on /dev/console\n"); + fprintf(stderr, "\tF - force checkpoint at startup time\n"); + fprintf(stderr, "\tA - authentication verbose messages\n"); + fprintf(stderr, "\tL [n] - Max load (n) of child processes\n"); + fprintf(stderr, "\tf - force registration even if program # in use\n"); + fprintf(stderr, "\tv - enable verbose mode\n"); + fprintf(stderr, "\tY - emulate NIS (YP) service\n"); + fprintf(stderr, "\tB - emulate NIS (YP) dns resolver service\n"); + fprintf(stderr, "\tt netid - use netid as transport for resolver\n"); + fprintf(stderr, "\td [dictionary] - user defined dictionary\n"); + fprintf(stderr, "\tS [n] - Security level (n) 0,1, or 2\n"); + fprintf(stderr, "\tD - debug mode (don't fork)\n"); + fprintf(stderr, "\tc - checkpoint time in seconds (ignored)\n"); + fprintf(stderr, "\tT n - Size of transaction log in megabytes\n"); + fprintf(stderr, "\tm file - Name of LDAP mapping config file\n"); + fprintf(stderr, "\tx attr=val - Configuration attribute name/value\n"); + fprintf(stderr, "\tz n - Maximum rpc record size in bytes (>= %d)\n", + RPC_MAXDATASIZE); + exit(0); +} + +/* + * Loop thru mech list from security conf file and set + * the RPC GSS service name(s). Stop processing list if + * the classic AUTH_DES compat entry is encountered. + */ +static void +set_rpc_gss_svc_names() +{ + mechanism_t **mechs; + + if (mechs = __nis_get_mechanisms(FALSE)) { + int slen; + mechanism_t **mpp; + char svc_name[NIS_MAXNAMELEN+1]; + char *lh = nis_local_host(); + + if (! lh) { + syslog(LOG_ERR, + "can't set RPC GSS service name: can't get local host name"); + __nis_release_mechanisms(mechs); + return; + } + + /* '@' + NUL = 2 */ + if (strlen(lh) + strlen(NIS_SVCNAME_NISD) + 2 > + sizeof (svc_name)) { + syslog(LOG_ERR, + "can't set RPC GSS service name: svc_name bufsize too small"); + __nis_release_mechanisms(mechs); + return; + } + /* service names are of the form svc@server.dom */ + (void) sprintf(svc_name, "%s@%s", NIS_SVCNAME_NISD, lh); + /* remove trailing '.' */ + slen = strlen(svc_name); + if (svc_name[slen - 1] == '.') + svc_name[slen - 1] = '\0'; + + for (mpp = mechs; *mpp; mpp++) { + mechanism_t *mp = *mpp; + + if (AUTH_DES_COMPAT_CHK(mp)) + break; + + if (! VALID_MECH_ENTRY(mp)) { + syslog(LOG_ERR, + "%s: invalid mechanism entry name '%s'", + NIS_SEC_CF_PATHNAME, + mp->mechname ? mp->mechname : "NULL"); + continue; + } + + if (rpc_gss_set_svc_name(svc_name, mp->mechname, + 0, NIS_PROG, + NIS_VERSION)) { + if (verbose) + syslog(LOG_INFO, + "RPC GSS service name for mech '%s' set", + mp->mechname); + } else { + if (secure_level > 1) { + rpc_gss_error_t err; + + rpc_gss_get_error(&err); + syslog(LOG_ERR, +"can't set RPC GSS svc name '%s' for mech '%s': RPC GSS err = %d, sys err = %d", + svc_name, mp->mechname, + err.rpc_gss_error, + err.system_error); + } else { + if (verbose) + syslog(LOG_INFO, + "can't set RPC GSS service name for mech '%s'", + mp->mechname); + } + } + } + __nis_release_mechanisms(mechs); + return; + } +} + +void +main(argc, argv) + int argc; + char *argv[]; +{ + int status = 0, i, c; + nis_object *rootobj; + char buf[80]; + char logname[80]; + struct stat s; + char *dict = NULL; + int pid; + int force = 0, mb; + struct rlimit rl; + int minfd; + int open_console = 0; + struct sigaction sigactn; + bool_t massage_dict; + sigset_t new_mask; + int sig_recvd; + int cred_cache = 0; + int rpc_irtimeout = -1; + int concurrency = -1; + char *ldapConfFile = 0; + char **ldapCLA = 0; + int numLA = 0; + + /* + * increase the internal RPC server cache size to 1024. + * If it fails to increase, then just use the default (=128). + */ + + int connmaxrec = -1; + int fdlmtremove = 8192; + + /* + * We cannot use the shared directory cache yet (possible + * deadlock), so we start up the local cache. + */ + (void) __nis_CacheLocalInit(&next_refresh); + + /* Set number of file descriptors to unlimited */ + + if (!rpc_control(RPC_SVC_USE_POLLFD, &fdlmtremove)) { + syslog(LOG_ERR, + "unable to set RPC file descriptors to unlimited"); + } + + + /* + * __clear_directory_ptr is a global defined in libnsl. + * We need to set this here so that clear_directory() be called from + * within nis_dump. This is part of the servers serving stale data + * bug fix. See 1179965. + */ + __clear_directory_ptr = &clear_directory; + + /* + * Make sure that files created by stdio do not have + * extra permission. We allow group read, but we don't + * allow world to read or write. We disallow write for + * obvious reasons, but also disallow read so that + * tables can't be read by world (thus bypassing the + * NIS+ access controls. + */ + (void) umask(027); + + heap_start = (unsigned int) sbrk(0); /* before any allocs */ + + /* + * Process the command line arguments + */ + opterr = 0; + chdir("/var/nis"); + while ((c = getopt(argc, argv, "hCFDAL:fvYBS:rd:T:t:p:i:n:m:x:z:")) != + -1) { + switch (c) { + case 'h' : /* internal help screen */ + print_options(); + break; + case 'T' : + mb = atoi(optarg); + if ((mb < 4) || (mb > 129)) { + fprintf(stderr, "Illegal log size.\n"); + exit(1); + } + __maxloglen = mb * 1024 * 1024; + break; + + case 'C' : + open_console++; + break; + case 'F' : + force_checkpoint = TRUE; + need_checkpoint = TRUE; + break; + case 'A' : + auth_verbose++; + break; + case 'Y' : + emulate_yp = TRUE; + break; + case 'B' : + resolv_flag = TRUE; + break; + case 't' : + tflag = TRUE; + resolv_tp = optarg; + break; + case 'v' : + verbose = 1; + break; + case 'd' : + dict = optarg; + break; + case 'S' : + secure_level = atoi(optarg); + break; + case 'r' : + /* obsolete option */ + root_server = -1; + fprintf(stderr, +"The -r option is obsolete and no longer necessary for root servers.\n"); + break; + case 'f' : + force = TRUE; + break; + case 'p' : + cred_cache = atoi(optarg); + if ((cred_cache < 128) || (cred_cache > 8192)) { + fprintf(stderr, + "Illegal credential cache size.\n"); + exit(1); + } + break; + case 'i' : + rpc_irtimeout = atoi(optarg); + if (rpc_irtimeout < 0) { + fprintf(stderr, + "Illegal rpc inter-record timeout.\n"); + exit(1); + } + break; + case 'L' : + max_children = atoi(optarg); + if (max_children <= 0) { + fprintf(stderr, "Illegal load value\n"); + exit(1); + } + break; + case 'z' : + connmaxrec = atoi(optarg); + break; + case 'D' : + debug = 1; + break; + case 'n': + concurrency = atoi(optarg); + break; + case 'm': + /* Config file name */ + ldapConfFile = optarg; + break; + case 'x': + /* Attribute assignment */ + ldapCLA = realloc(ldapCLA, + (numLA + 2) * sizeof (ldapCLA[0])); + if (ldapCLA == 0) { + fprintf(stderr, + "Out of memory. realloc(%d) => NULL\n", + (numLA+2)*sizeof (ldapCLA[0])); + exit(1); + } + ldapCLA[numLA++] = optarg; + ldapCLA[numLA] = 0; + break; + case '?' : + fprintf(stderr, + "usage: rpc.nisd [ -ACDFhlv ] [ -Y [ -B [ -t netid ]]]\n"); + fprintf(stderr, + "\t[ -d dictionary ] [ -L load ] [ -S level ]\n"); + fprintf(stderr, "\t[ -m file ] " + "[ -x attr=val] [ -z max record size]\n"); + exit(1); + } + } + + /* + * The "emulate YP" option can be requested on the command line + * or in the defaults file; command-line option overrides the + * file. The value from the defaults file (ENABLE_NIS_YP_EMULATION=), + * obtained via parseConfig above, only needs to be checked if + * command-line option -Y is NOT used. + */ + if (emulate_yp == FALSE) + emulate_yp = ldapConfig.emulate_yp; + + /* Complete syntax checking now that defaults are processed. */ + if (resolv_flag == TRUE) { + if (emulate_yp == FALSE) { + fprintf(stderr, + "Option -B requires option -Y also.\n"); + exit(1); + } + } + if (tflag == TRUE) { + if (resolv_flag == FALSE) { + fprintf(stderr, + "Option -t requires options -Y and -B also.\n"); + exit(1); + } + } + + if (! debug) { + switch (fork()) { + case -1: + fprintf(stderr, "Couldn't fork a process exiting.\n"); + exit(1); + case 0: + break; + default: + exit(0); + } + + closelog(); + closefrom(0); + (void) open("/dev/null", O_RDONLY); + (void) open("/dev/null", O_WRONLY); + (void) dup(1); + pid = setsid(); + openlog("nisd", LOG_PID+LOG_NOWAIT, LOG_DAEMON); + } + +#ifdef MEM_DEBUG + init_xmalloc(); +#endif + if (open_console == 1) + cons = fopen("/dev/console", "w"); + else if (open_console > 1) + cons = stdout; + syslog(LOG_INFO, "NIS+ service started."); + gettimeofday(&start_time, 0); + master_pid = getpid(); + if (verbose) + syslog(LOG_INFO, "verbose mode set."); + + if (!cred_cache) + cred_cache = CRED_CACHESZ_DEF; + /* set the credential cache size */ + if (!__rpc_control(CLCR_SET_CRED_CACHE_SZ, &cred_cache)) + syslog(LOG_ERR, + "rpc.nisd: cannot set credential cache size to %d", + cred_cache); + else + if (verbose) + syslog(LOG_INFO, + "rpc.nisd: credential cache size set to %d", + cred_cache); + + /* set RPC inter-record timeout */ + if (rpc_irtimeout >= 0) + if (!rpc_control(RPC_SVC_IRTIMEOUT_SET, &rpc_irtimeout)) + syslog(LOG_ERR, + "rpc.nisd: cannot set ir timeout to %d", + rpc_irtimeout); + else + if (verbose) + syslog(LOG_INFO, + "rpc.nisd: ir timeout set to %d seconds", + rpc_irtimeout); + + /* Parse LDAP mapping config */ + { + int stat = parseConfig(ldapCLA, ldapConfFile); + if (stat == 1) { + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "NIS+/LDAP mapping inactive"); + } else if (stat != 0) { + logmsg(MSG_NOTIMECHECK, LOG_ERR, + "Aborting after NIS+/LDAP mapping parse error"); + exit(1); + } else { + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "NIS+/LDAP mapping parsed and initialized"); + } + } + + /* + * The data base functions use stdio. The stdio routines + * can only handle file descriptors up to 255. To make + * more of these low-numbered descriptors available, we + * bump up the file descriptor limit, and tell the RPC + * library (our other main source of file descriptors) to + * try to use descriptors numbered 256 and above. + */ + getrlimit(RLIMIT_NOFILE, &rl); + rl.rlim_cur = RLIM_INFINITY; + rl.rlim_max = RLIM_INFINITY; + setrlimit(RLIMIT_NOFILE, &rl); + minfd = 256; + rpc_control(__RPC_CLNT_MINFD_SET, (char *)&minfd); + + if (nis_local_directory() == NULL) { + if (debug) + fprintf(stderr, "NIS+ Directory not set. Exiting.\n"); + else + syslog(LOG_ERR, "NIS+ Directory not set. Exiting."); + exit(1); + } + + if (debug) { + fprintf(stderr, "NIS+ Server startup.\n"); + } + + /* + * Set non-blocking mode, and establish maximum record size, + * for connection oriented RPC transports. + */ + { + /* If not set via the '-z' option, use the attribute value */ + if (connmaxrec == -1) + connmaxrec = ldapConfig.maxRPCRecordSize; + + if (connmaxrec < RPC_MAXDATASIZE) { + syslog(LOG_WARNING, + "Illegal max rpc record size specified %d, " + "setting to default %d", + connmaxrec, RPC_MAXDATASIZE); + connmaxrec = RPC_MAXDATASIZE; + } + if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) { + syslog(LOG_WARNING, + "unable to set maximum RPC record size of %d", + connmaxrec); + } + } + + /* Establish number of RPC service threads */ + { + int mtmode = RPC_SVC_MT_AUTO; + + /* If not set via the '-n' option, use the attribute value */ + if (concurrency < 0) + concurrency = ldapConfig.numberOfServiceThreads; + + /* Zero means three plus number of processors available */ + if (concurrency <= 0) { + long numprc; + /* + * If the requested functionality isn't available, + * sysconf(3C) returns -1 and leaves errno unchanged. + * Hence, establish a good default errno. + */ + errno = ENOTSUP; + numprc = sysconf(_SC_NPROCESSORS_ONLN); + if (numprc <= 0) { + syslog(LOG_WARNING, + "Unable to determine number of " + "processors on-line; assuming one: %m"); + numprc = 1; + } + concurrency = 3 + numprc; + } + + if (!rpc_control(RPC_SVC_MTMODE_SET, &mtmode)) { + syslog(LOG_WARNING, "Could not set RPC auto MT mode"); + } + if (!rpc_control(RPC_SVC_THRMAX_SET, &concurrency)) { + syslog(LOG_WARNING, + "Could not set RPC concurrency = %d", + concurrency); + } + } + + /* + * Fix for bug #1248972 - Block SIGCHLD in the parent + * thread, so all subsequent threads will inherit the + * same signal mask - i.e. block SIGCHLD. + */ + (void) sigemptyset(&new_mask); + (void) sigaddset(&new_mask, SIGCHLD); + (void) thr_sigsetmask(SIG_BLOCK, &new_mask, NULL); + + if (debug) { + fprintf(stderr, "Database initialization ...\n"); + } + + /* + * We're still single-threaded, so no need to acquire the + * 'setup_resolv' lock. + */ + if (resolv_flag) + setup_resolv(&resolv_flag, &resolv_pid, + &resolv_client, resolv_tp, 0); + + massage_dict = FALSE; + if (!dict) { + if (stat(nis_old_data(NULL), &s) == -1) { /* No old */ + if (stat(nis_data(NULL), &s) == -1) { /* No New */ + if (errno == ENOENT) { + strcpy(buf, nis_data(NULL)); + if (mkdir(buf, 0700)) { + perror("rpc.nisd"); + syslog(LOG_ERR, + "rpc.nisd: Unable to create NIS+ directory %s", buf); + exit(1); + } + } else { + perror("rpc.nisd"); + syslog(LOG_ERR, + "rpc.nisd: unable to stat NIS+ directory %s.", buf); + exit(1); + } + } + strcpy(buf, nis_data(NULL)); + } else if (stat(nis_data(NULL), &s) != -1) { /* Old and New */ + /* + * Handle the case for a host called data: + * - ONly the transaction log needs to be + * renamed. + * - the dict has already been massaged and + * named correctly. + */ + if (strcmp(NIS_DIR, + nis_leaf_of(nis_local_host())) == 0) { + char oldstr[NIS_MAXNAMELEN]; + char newstr[NIS_MAXNAMELEN]; + + sprintf(oldstr, "%s.log", nis_old_data(NULL)); + strcpy(newstr, LOG_FILE); + if (rename(oldstr, newstr) == -1) { + syslog(LOG_ERR, + "Unable to rename NIS+ transaction log."); + exit(1); + } + strcpy(buf, nis_data(NULL)); + dict = buf; + /* No need to massage dict */ + } else { + syslog(LOG_ERR, + "Old and new dir structures cannot coexist."); + exit(1); + } + } else { /* Old, No New => massage dict. */ + massage_dict = TRUE; + strcpy(buf, nis_old_data(NULL)); + } + strcat(buf, ".dict"); + dict = buf; + } + if (debug) + fprintf(stderr, "Dictionary is %s\n", buf); + status = db_initialize(dict); + if (status == 0) { + if (debug) + fprintf(stderr, "Unable to initialize %s\n", buf); + else + syslog(LOG_ERR, "Unable to initialize %s", buf); + exit(1); + } + + /* + * Now, rename the `hostname` directory if necessary + * and massage the dictionary file. This must be done + * _after_ the dictionary has been initialiazed. Remember, + * the dictionary would have been initialized with dict name + * based on the old structure. + * + */ + if (massage_dict) { + char oldbuf[NIS_MAXNAMELEN], newbuf[NIS_MAXNAMELEN]; + char oldstr[NIS_MAXNAMELEN]; + char newstr[NIS_MAXNAMELEN]; + char newdict[NIS_MAXNAMELEN]; + + + /* Massage the dictionary file */ + sprintf(oldbuf, "/%s/", nis_leaf_of(nis_local_host())); + __make_legal(oldbuf); + sprintf(newbuf, "/%s/", NIS_DIR); + sprintf(newdict, "%s.dict", nis_data(NULL)); + if (db_massage_dict(newdict, oldbuf, newbuf) != DB_SUCCESS) { + syslog(LOG_ERR, + "Unable to change database dictionary."); + exit(1); + } + + + /* + * Now, rename the old structure. This includes the following: + * - directory containing the tables. + * We don't worry about the dictionary file and its log + * since db_massage_dict() will take care of that for us. + * We also don't worry about the dictionary log file, since + * db_massage_dict() will checkpoint before it makes any + * changes. + * + * However, we do need to change the NIS+ transaction log. + */ + strcpy(oldstr, nis_old_data(NULL)); + strcpy(newstr, nis_data(NULL)); + if (rename(oldstr, newstr) == -1) { + syslog(LOG_ERR, + "Unable to rename directory structure."); + exit(1); + } + sprintf(oldstr, "%s.log", nis_old_data(NULL)); + strcpy(newstr, LOG_FILE); + if (rename(oldstr, newstr) == -1) { + syslog(LOG_ERR, + "Unable to rename NIS+ transaction log."); + exit(1); + } + /* Now, reinitialize the dictionary */ + status = db_initialize(newdict); + if (status == 0) { + if (debug) + fprintf(stderr, + "Unable to REinitialize %s\n", newdict); + else + syslog(LOG_ERR, "Unable to initialize %s", + newdict); + exit(1); + } + } + rootobj = get_root_object(); + if (rootobj) + root_server = 1; + else if (root_server == -1) { + /* if -r option is specified in the command line */ + root_server = 0; + fprintf(stderr, + "No root object present; running as non-root server.\n"); + } + + if (root_server) { + update_cache_data(rootobj); /* must do after detach */ + if (verbose) + syslog(LOG_INFO, "Service running as root server."); + if (we_serve(&(rootobj->DI_data), MASTER_ONLY) && + !verify_table_exists(__nis_rpc_domain())) + exit(1); + nis_destroy_object(rootobj); /* free; not needed anymore */ + } + + if (debug) { + fprintf(stderr, "... database initialization complete.\n"); + fprintf(stderr, "Transaction log initialization ...\n"); + } + + if (! status) + syslog(LOG_ERR, "WARNING: Dictionary not initialized!"); + + sprintf(logname, "%s", LOG_FILE); + if (map_log(logname, FNISD)) { + if (debug) + fprintf(stderr, "Transaction log corrupt. Exiting.\n"); + else + syslog(LOG_ERR, "Transaction log corrupt. Exiting."); + exit(1); + } + + /* initialize the timestamp cache table */ + init_updatetime(); + + if (debug) { + fprintf(stderr, "... transaction log initialized.\n"); + } + + /* Initialize in-core list of directories served by this server */ + (void) nis_server_control(SERVING_LIST, DIR_INITLIST, NULL); + + /* + * If we crashed during update, directory_invalid will contain + * the name of the invalidated directory; otherwise, it will + * be NULL. (map_log sets this) + */ + if (invalid_directory) { + nis_object id[1], *invdir = id; + struct ticks t[1]; + ping_item dummy_ping[1]; + int drastic_measures = 0; + + syslog(LOG_WARNING, + "directory %s corrupted during update; attempting recovery", + invalid_directory); + clear_directory(invalid_directory); + /* forge a ping item; fill just enought for replica_update */ + if (__directory_object(invalid_directory, t, 0, &invdir) != + NIS_SUCCESS) { + syslog(LOG_WARNING, + "recovery for %s failed; couldn't get directory object", + invalid_directory); + drastic_measures = 1; + } else { + dummy_ping->item.name = invalid_directory; + dummy_ping->mtime = 0; + dummy_ping->obj = invdir; + if (!replica_update(dummy_ping)) { + syslog(LOG_WARNING, + "recovery for %s failed; couldn't resync", + invalid_directory); + /* + * replica_update will also have invalidated + * this directory, but just to be sure... + */ + drastic_measures = 1; + } + } + + if (drastic_measures) { + syslog(LOG_WARNING, + "Forcing resync by setting update time to 0 for %s", + invalid_directory); + syslog(LOG_WARNING, + "You may need to restore from backup"); + make_stamp(invalid_directory, 0); + } else + syslog(LOG_WARNING, + "Recovery for %s completed", invalid_directory); + } + + /* Up-/down-load data to/from LDAP */ + i = loadLDAPdata(); + if (i != 0) { + logmsg(MSG_NOTIMECHECK, LOG_WARNING, + "Exiting after LDAP data load"); + exit(i > 0 ? 0 : 1); + } + + /* rpc registration */ + if (debug) { + fprintf(stderr, "RPC program registration ...\n"); + } + + rpcb_unset(NIS_PROG, NIS_VERSION, NULL); + if (emulate_yp) + { + rpcb_unset(YPPROG, YPVERS, NULL); + rpcb_unset(YPPROG, YPVERS_ORIG, NULL); + } + i = nis_svc_create(nis_prog_svc, NIS_PROG, NIS_VERSION, NC_VISIBLE); + if (! i) + exit(1); + else if (verbose) + syslog(LOG_INFO, "NIS+ service listening on %d transports.", i); + if (emulate_yp) { + i = nis_svc_create(ypprog_svc, YPPROG, YPVERS, NC_VISIBLE); + if (! i) + exit(1); + else if (verbose) + syslog(LOG_INFO, + "NIS service listening on %d transports.", i); + i = nis_svc_create(ypprog_1, YPPROG, YPVERS_ORIG, NC_VISIBLE); + if (! i) + exit(1); + else if (verbose) + syslog(LOG_INFO, "Created %d YPVERS_ORIG handles.", i); + } + + set_rpc_gss_svc_names(); + + __svc_nisplus_enable_timestamps(); + + if (debug) { + fprintf(stderr, "... RPC registration complete.\n"); + fprintf(stderr, "Service starting.\n"); + } + + { + pthread_t servloop_thread; + pthread_attr_t attr; + int stat; + + (void) pthread_attr_init(&attr); + if ((stat = pthread_create(&servloop_thread, &attr, + servloop, 0)) != 0) { + syslog(LOG_ERR, + "error %d creating servloop thread; exiting", + stat); + if (cons || debug) + fprintf(stderr, + "error %d creating servloop thread; exiting\n", + stat); + exit(1); + } + (void) pthread_attr_destroy(&attr); + + if (cons || debug) + fprintf(stderr, + "servloop thread started; ready to roll...\n"); + + svc_run(); + /* Not reached */ + } +} + +int +__rpcsec_gss_is_server() +{ + return (1); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.c new file mode 100644 index 0000000000..ec7a48817e --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.c @@ -0,0 +1,159 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/time.h> +#include <sys/types.h> +#include <errno.h> +#include <malloc.h> +#include <syslog.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include "nis_proc.h" + +DECLRWLOCK(upd_list); +DECLRWLOCK(ping_list); +DECLRWLOCK(nisopstats); +DECLRWLOCK(table_cache); +DECLRWLOCK(dircachestats); +DECLRWLOCK(translog); + +extern void check_updaters(void); +extern void check_pingers(void); + +/* + * We replace the __start_clock() and __stop_clock() functons from libnsl + * with our own versions that provide per-thread clocks. + */ +int +__start_clock(int clk) { + nis_tsd_t *tsd; + if ((clk >= MAXCLOCKS) || (clk < 0) || + ((tsd = __nis_get_tsd())->clocks[clk].tv_sec)) + return (FALSE); + + (void) gettimeofday(&(tsd->clocks[clk]), 0); + return (TRUE); +} + +uint32_t +__stop_clock(int clk) { + struct timeval now; + uint32_t secs, mics; + nis_tsd_t *tsd; + + if ((clk >= MAXCLOCKS) || (clk < 0) || + (!((tsd = __nis_get_tsd())->clocks[clk].tv_sec))) + return (0); + + (void) gettimeofday(&now, 0); + secs = (uint32_t)(now.tv_sec - tsd->clocks[clk].tv_sec); + mics = (uint32_t)(now.tv_usec - tsd->clocks[clk].tv_usec); + if (mics < 0) { + mics += 1000000; + secs -= 1; + } + mics += 1000000*secs; + tsd->clocks[clk].tv_sec = 0; + return (mics); +} + +static nis_tsd_t nis_shared_tsd; +static pthread_key_t nis_tsd_key; + +void +__nis_tsd_destroy(void *key) { + + nis_tsd_t *tsd = (nis_tsd_t *)key; + cleanupblock_t *rb, *next_rb; + + if (tsd != 0) { + /* Free memory allocated by nis_get_static_storage() */ + if (tsd->censor_object_buf.buf != 0) + free(tsd->censor_object_buf.buf); + /* Purge loose ends */ + __nis_thread_cleanup(tsd); + for (rb = tsd->ragblocks; rb != 0; rb = next_rb) { + next_rb = rb->next; +#ifdef NIS_MT_DEBUG + printf("%d: 0x%x freed\n", pthread_self(), rb); +#endif /* NIS_MT_DEBUG */ + free(rb); + } + free(tsd); + } +} + +int +__nis_init_tsd_key(void) { + + return (pthread_key_create(&nis_tsd_key, __nis_tsd_destroy)); +} + +#pragma init(__nis_init_tsd_key) + +nis_tsd_t * +__nis_get_tsd(void) { + nis_tsd_t *tsd; + + if ((tsd = pthread_getspecific(nis_tsd_key)) == 0) { + /* No TSD; create it */ + if ((tsd = malloc(sizeof (*tsd))) != 0) { + /* Initialize TSD */ + memset(tsd, 0, sizeof (*tsd)); + /* Register TSD */ + if (pthread_setspecific(nis_tsd_key, tsd) != 0) { + /* Can't store key; abort */ +#ifdef NIS_MT_DEBUG + abort(); +#endif /* NIS_MT_DEBUG */ + free(tsd); + tsd = &nis_shared_tsd; + } + } else { + /* No memory ? */ +#ifdef NIS_MT_DEBUG + abort(); +#endif /* NIS_MT_DEBUG */ + tsd = &nis_shared_tsd; + } + } + + return (tsd); +} + +void +__nis_thread_cleanup(nis_tsd_t *tsd) { + + if (tsd->looseends != 0) { + do_cleanup(tsd->looseends); + tsd->looseends = 0; + } +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.h new file mode 100644 index 0000000000..009bb37442 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.h @@ -0,0 +1,162 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef _NIS_MT_H +#define _NIS_MT_H + +#include <pthread.h> +#include <synch.h> +#include <sys/types.h> +#include <stdlib.h> +#include <rpcsvc/nis_callback.h> +#include <rpcsvc/yp_prot.h> + +#include <nisdb_rw.h> +#include <nis_hashitem.h> + +/* RW locks */ +USERWLOCK(upd_list); /* nis_main.c */ +USERWLOCK(ping_list); /* nis_main.c */ +USERWLOCK(nisopstats); /* nis_service.c, nis_xx_proc.c */ +USERWLOCK(table_cache); /* nis_db.c */ +USERWLOCK(dircachestats); /* nis_subr_proc.c */ +USERWLOCK(translog); /* nis_log_common.c, nis_log_svc.c */ + +/* Functions */ +extern int msleep(ulong_t); +extern void mark_activity(void); +extern void *servloop(void *); +extern void *callback_thread(void *); +extern void *dumpsvc_thread(void *); + +extern void wakeup_servloop(void); +extern time_t updateBatchingTimeout(void); +extern void setPingWakeup(time_t); + +/* + * MAXCLOCKS normally in libnsl/nis/gen/nis_local.h, but we roll our own + * thread-specific clocks. + */ +#define MAXCLOCKS 16 + +#define MAXRAGS 1024 +/* Probably don't need MAXRAGS for a single thread. MAXRAGS_THR is a guess */ +#define MAXRAGS_THR (MAXRAGS/16) + +/* Keep track of allocated rag blocks */ +typedef struct cleanupblockstruct { + struct cleanupblockstruct *next; +} cleanupblock_t; + +/* Statistics structure from nis_subr_proc.c */ +typedef struct { + int successes; + int errors; + int ticks; + ulong_t utime; +} repl_stats_t; + +/* yp_all() response structure from yp_ns_proc.c */ +typedef char *string_t; +struct ypresp_all { + long status; + string_t table_name; + nis_name princp; + int key_column_ndx; + nis_object *table_zobj; +}; + +/* Thread-specific data */ +typedef struct { + struct timeval clocks[MAXCLOCKS]; + struct cleanup *looseends; + struct cleanup *rags[MAXRAGS_THR]; + struct cleanup *free_rags; + uint32_t cleanup_tag; + cleanupblock_t *ragblocks; + nis_name invalid_directory; + struct nis_sdata censor_object_buf; + struct nis_sdata modify_entry_buf; + struct nis_sdata __ibops_buf; + nis_db_result db_add_res; + nis_db_result db_remove_res; + struct nis_sdata local_buf__get_xdr_buf; + struct nis_sdata local_buf__get_string_buf; + struct nis_sdata local_buf__get_entry_col; + struct nis_sdata local_buf__get_table_col; + struct nis_sdata local_buf__get_attrs; + repl_stats_t repl_stats; + uint_t nis_cptime_svc_res; + nis_error nis_mkdir_svc_result; + nis_error nis_rmdir_svc_result; + char yp_ns_proc_record[YPMAXRECORD]; + char yp_ns_proc_keyval[YPMAXRECORD]; + bool_t ypproc_domain_svc_isserved; + bool_t ypproc_domain_nonack_svc_isserved; + struct ypresp_master ypproc_master_svc_resp; + char ypproc_master_svc_masterbuf[YPMAXPEER]; + struct ypresp_val ypproc_match_svc_resp; + struct ypresp_key_val ypproc_first_svc_resp; + struct ypresp_key_val ypproc_next_svc_resp; + struct ypresp_all ypproc_all_svc_resp; + struct ypresp_maplist ypproc_maplist_svc_maplist; + char xdr_ypresp_all_short_tblnm[YPMAXMAP]; + char *best_host_address_best_address; + char getcaller_inet_buf[256]; + char map2table_tbl[YPMAXMAP]; + char map2table_col[NIS_MAXATTRNAME]; + bool_t nis_callback_svc_res; +} nis_tsd_t; + +extern nis_tsd_t *__nis_get_tsd(void); +extern void __nis_thread_cleanup(nis_tsd_t *); +extern void __nis_free_items_mt(nis_tsd_t *); + +/* Arguments for thread entry point functions */ +typedef struct { + nis_fn_result *fnr; + nis_object *ib_obj; + nis_attr a[NIS_MAXATTR]; + int na; + int nm; + int all_read; + char pname[1024]; + cback_data cbarg; + CLIENT *cback; + char cbhostname[NIS_MAXNAMELEN]; + char ibr_name[NIS_MAXNAMELEN]; +} callback_thread_arg_t; + +typedef struct { + char da_dir[1024]; + char pname[1024]; + CLIENT *cback; + ulong_t ttime; +} dumpsvc_thread_arg_t; + +#endif /* _NIS_MT_H */ diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_multival.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_multival.c new file mode 100644 index 0000000000..65018ac5f7 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_multival.c @@ -0,0 +1,857 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 1997-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef LINT +static char SCCSID[] = "%Z%%M% %I% %E% SMI"; +#endif + +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <ctype.h> +#include <rpcsvc/nis.h> +#include "nis_proc.h" +#include "nis_mt.h" + +/* + * TA_MULTIVAL and TA_SEARCHABLE are mutually exclusive. The + * separator character for multivalue searches is stored in the + * upper byte of tc_flags and is accessed with TA_MULTISEP. + * + * XXX these defines should be moved to rpcsvc/nis.h eventually XXX + */ +#define TA_MULTIVAL (0xff000000) +#define TA_MULTISEP(x) (((x) & 0xff000000) >> 24) + +#ifndef _REENTRANT +#define strtok_r(a, b, c) strtok((a), (b)) +#endif /* _REENTRANT */ + +/* + * If FORCE_GROUP is defined, then the members column of the group + * table is considered a multival column, even if TA_MULTIVAL + * isn't set on the column. + */ +#define FORCE_GROUP 1 + +struct dir_item { + NIS_HASH_ITEM item; + NIS_HASH_TABLE tables; +}; +typedef struct dir_item dir_item; + +struct tab_item { + NIS_HASH_ITEM item; + nis_db_list_result *res; + NIS_HASH_TABLE columns; + dir_item *dir; +}; +typedef struct tab_item tab_item; + +struct col_item { + NIS_HASH_ITEM item; + int colnum; + char sep[2]; + int case_insensitive; + NIS_HASH_TABLE entries; + tab_item *tab; +}; +typedef struct col_item col_item; + +struct ent_item { + NIS_HASH_ITEM item; + nis_object **obj; + int nobj; + col_item *col; +}; +typedef struct ent_item ent_item; + +static NIS_HASH_TABLE dirs = NIS_HASH_TABLE_MT_INIT; + +static tab_item *find_table(nis_object *, int); +static col_item *find_column(nis_object *, char *, int); +static ent_item *find_entry(nis_object *, char *, char *, int); + +static tab_item *load_table(nis_object *, int); +static col_item *load_column(tab_item *, table_col *, int, int); +static ent_item *load_entry(col_item *, nis_object *, int); + +static void free_table(tab_item *); +static void free_column(col_item *); +static void free_entry(ent_item *); + +static void strlower(char *); + +#define RW -1 +#define NONE 0 +#define RO 1 + +/* + * The find_table(), find_column(), and find_entry() functions leave the + * respective NIS_HASH_TABLE entries locked. The release_*() functions below + * unlock those tables in the correct order. + */ +static void +release_directory(dir_item *dir) { + if (dir != 0) + (void) __nis_release_item(dir, &dirs, __nis_item_access(dir)); +} + +static void +release_table(tab_item *tab, int release_dir) { + + dir_item *dir; + + if (tab != 0 && (dir = tab->dir) != 0) { + (void) __nis_release_item(tab, &dir->tables, + __nis_item_access(tab)); + if (release_dir) + release_directory(dir); + } +} + +static void +release_column(col_item *col, int release_tab) { + + tab_item *tab; + + if (col != 0 && (tab = col->tab) != 0) { + (void) __nis_release_item(col, &tab->columns, + __nis_item_access(col)); + if (release_tab) + release_table(tab, 1); + } +} + +static void +release_entry(ent_item *ent, int release_col) { + + col_item *col; + + if (ent != 0 && (col = ent->col) != 0) { + (void) __nis_release_item(ent, &col->entries, + __nis_item_access(ent)); + if (release_col) + release_column(col, 1); + } +} + +/* + * Check to see if the attribute in 'attr' is a multival + * attribute. + */ +int +multival_check(nis_object *tobj, nis_attr *attr, table_col *cols, int ncols) +{ + int i; + +#ifdef FORCE_GROUP + if (strcasecmp(tobj->TA_data.ta_type, "group_tbl") == 0 && + strcasecmp(attr->zattr_ndx, "members") == 0) { + return (1); + } +#endif /* FORCE_GROUP */ + for (i = 0; i < ncols; i++) { + if (strcasecmp(cols[i].tc_name, attr->zattr_ndx) == 0) { + /* if column is searchable, then it is not multival */ + if (cols[i].tc_flags & TA_SEARCHABLE) + return (0); + if ((cols[i].tc_flags & TA_MULTIVAL) != 0) + return (1); + } + } + return (0); +} + +/* + * Determine which attributes refer to multival columns. All of + * the regular attributes are moved to the beginning of 'attr' + * and the multival attributes are moved to the end. The + * 'nattr' and 'nmulti' variables are set to the number of each + * type of attributes. + * + * We assume that most tables don't have multival columns, so do + * a quick scan and return if this is the case. + */ +nis_error +multival_attr(nis_object *tobj, nis_attr *attr, int *nattr, int *nmulti) +{ + int i; + int front; + int back; + table_col *cols; + int ncols; + int *is_multival; + int tmp; + nis_attr tmp_attr; + + if (__type_of(tobj) == DIRECTORY_OBJ) { + *nmulti = 0; + return (NIS_SUCCESS); + } + + cols = tobj->TA_data.ta_cols.ta_cols_val; + ncols = tobj->TA_data.ta_cols.ta_cols_len; + for (i = 0; i < ncols; i++) { + if (cols[i].tc_flags & TA_MULTIVAL) + break; + } +#ifdef FORCE_GROUP + if (strcasecmp(tobj->TA_data.ta_type, "group_tbl") == 0) { + i = 3; /* members column number */ + } +#endif /* FORCE_GROUP */ + if (i >= ncols) { + *nmulti = 0; + return (NIS_SUCCESS); + } + + /* determine which elements of attr are multival */ + is_multival = (int *)malloc(*nattr * sizeof (is_multival[0])); + if (is_multival == NULL) { + return (NIS_NOMEMORY); + } + for (i = 0; i < *nattr; i++) { + is_multival[i] = multival_check(tobj, &attr[i], cols, ncols); + } + + /* partition sort to put regular attributes first, multival last */ + front = 0; + back = *nattr - 1; + while (front <= back) { + if (!is_multival[front]) + front += 1; + else if (is_multival[back]) + back -= 1; + else { + tmp_attr = attr[front]; + attr[front] = attr[back]; + attr[back] = tmp_attr; + tmp = is_multival[front]; + is_multival[front] = is_multival[back]; + is_multival[back] = tmp; + front += 1; + back -= 1; + } + } + + if (front < *nattr) { + *nmulti = *nattr - front; + *nattr = front; + } else { + *nmulti = 0; + } + + free((void *)is_multival); + return (NIS_SUCCESS); +} + +/* + * Do a multival search on nis_object. If all of the attributes + * in 'm' have matches, then the object should not be filtered out. + */ +int +multival_filter(nis_object *tobj, int nm, nis_attr *m, nis_object *obj) +{ + int i; + int st; + int len; + char *p; + int vlen; + char *val; + char *last; + col_item *col; + + for (i = 0; i < nm; i++) { + if ((col = find_column(tobj, m[i].zattr_ndx, RO)) == NULL) + return (1); + p = ENTRY_VAL(obj, col->colnum); + len = ENTRY_LEN(obj, col->colnum); + if (p == NULL || len == 0 || p[len-1] != '\0') { + release_column(col, 1); + return (1); + } + /* make copy of value because strtok leaves trail of '\0' */ + p = strdup(p); + if (p == NULL) { + release_column(col, 1); + return (1); + } + + val = strtok_r(p, col->sep, &last); + while (val) { + vlen = strlen(val) + 1; /* include '\0' */ + if (vlen == m[i].zattr_val.zattr_val_len) { + if (col->case_insensitive) { + st = strcasecmp(val, + m[i].zattr_val.zattr_val_val); + } else { + st = strcmp(val, + m[i].zattr_val.zattr_val_val); + } + if (st == 0) + break; + } + val = strtok_r(NULL, col->sep, &last); + } + free((void *)p); + release_column(col, 1); + if (val == NULL) + return (1); + } + + /* found a match for all attributes, don't filter this object */ + return (0); +} + +/* + * Search the table specified by 'tobj' for all of the objects that + * are selected by the attributes in 'm'. This is basically a + * join operation. For the first attribute, we retrieve a list of + * objects. For subsequent attributes, we do a join and keep only + * the objects that occur in both lists. + */ +void +multival_list(nis_object *tobj, int nm, nis_attr *m, nis_db_list_result *res) +{ + int i; + int j; + int k; + int n; + char *val; + int len; + ent_item *ent; + obj_list *olist; + int count = 0; + int nobj = 0; + nis_object **objs = NULL; + + for (i = 0; i < nm; i++) { + val = m[i].zattr_val.zattr_val_val; + len = m[i].zattr_val.zattr_val_len; + if (val == NULL || len == 0 || val[len-1] != '\0') { + res->status = NIS_BADATTRIBUTE; + return; + } + ent = find_entry(tobj, m[i].zattr_ndx, val, RO); + if (!ent) { + res->status = NIS_NOTFOUND; + free((void *)objs); + return; + } + + if (objs == NULL) { + objs = (nis_object **)malloc( + ent->nobj * sizeof (nis_object *)); + if (objs == NULL) { + release_entry(ent, 1); + res->status = NIS_NOMEMORY; + return; + } + for (j = 0; j < ent->nobj; j++) { + objs[j] = ent->obj[j]; + } + nobj = ent->nobj; + count = nobj; + } else { + for (j = 0; j < nobj; j++) { + if (objs[j] == NULL) + continue; + for (k = 0; k < ent->nobj; k++) { + if (objs[j] == ent->obj[k]) + break; + } + if (k >= ent->nobj) { + objs[j] = NULL; + count -= 1; + } + } + } + release_entry(ent, 1); + } + + if (count == 0) { + res->status = NIS_NOTFOUND; + free((void *)objs); + return; + } + + olist = (obj_list *)calloc(count, sizeof (obj_list)); + + n = 0; + for (i = 0; i < nobj; i++) { + if (objs[i]) { + olist[n].o = nis_clone_object(objs[i], 0); + n++; + } + } + free((void *)objs); + + if (n != count) { + syslog(LOG_ERR, + "multival_list: miscounted objects (%d, %d)", n, count); + } + + res->status = NIS_SUCCESS; + res->objs = olist; + res->numo = count; +} + +/* + * A table has been modified in some way, so we must free up + * the entries associated with it. + */ +void +multival_invalidate(nis_object *obj) +{ + dir_item *dir; + tab_item *tab; + + if (__type_of(obj) != TABLE_OBJ && __type_of(obj) != ENTRY_OBJ) + return; + + dir = (dir_item *)nis_find_item_rw(obj->zo_domain, &dirs, RO); + if (dir == NULL) + return; + + tab = (tab_item *)nis_remove_item(obj->zo_name, &dir->tables); + release_directory(dir); + if (tab) + free_table(tab); +} + +static +tab_item * +find_table(nis_object *tobj, int rw) +{ + int i; + table_col *cols; + int ncols; + dir_item *dir; + tab_item *tab; + + dir = (dir_item *)nis_find_item_rw(tobj->zo_domain, &dirs, RO); + if (dir == NULL) + return (NULL); + + tab = (tab_item *)nis_find_item_rw(tobj->zo_name, &dir->tables, rw); + if (tab != 0) + tab->dir = dir; + return (tab); +} + +static +col_item * +find_column(nis_object *tobj, char *name, int rw) +{ + tab_item *tab; + col_item *col; + + tab = find_table(tobj, RO); + if (tab == NULL) { + /* + * While loading the table, we need write access (at least + * to parts of it). + */ + tab = load_table(tobj, RW); + if (tab == NULL) { + /* remove any partially loaded table information */ + multival_invalidate(tobj); + return (NULL); + } + } + col = (col_item *)nis_find_item_rw(name, &tab->columns, rw); + if (col != 0) { + col->tab = tab; + } else { + release_table(tab, 1); + } + return (col); +} + +static +ent_item * +find_entry(nis_object *tobj, char *key, char *val, int rw) +{ + col_item *col; + ent_item *ent; + + strlower(key); + col = find_column(tobj, key, RO); + if (col == NULL) + return (NULL); + if (col->case_insensitive) + strlower(val); + ent = (ent_item *)nis_find_item_rw(val, &col->entries, rw); + if (ent != 0) { + ent->col = col; + } else { + release_column(col, 1); + } + return (ent); +} + +static +col_item * +load_column(tab_item *tab, table_col *tc, int n, int rw) +{ + col_item *col; + + col = (col_item *)calloc(1, sizeof (col_item)); + if (col == NULL) + return (NULL); + col->item.name = strdup(tc->tc_name); + if (col->item.name == NULL) { + free((void *)col); + return (NULL); + } + col->colnum = n; + col->sep[0] = TA_MULTISEP(tc->tc_flags); + col->sep[1] = '\0'; + if (tc->tc_flags & TA_CASE) + col->case_insensitive = 1; + else + col->case_insensitive = 0; + col->tab = tab; + if (!nis_insert_item_rw((NIS_HASH_ITEM *)col, &tab->columns, rw)) { + free((void *)col->item.name); + free((void *)col); + return (NULL); + } + return (col); +} + +/* + * load_entry() + * + * The col entry val/len should be checked before calling load_entry to make + * sure it is non-NULL, non-empty, length > 0, and NUL terminated. + */ +static +ent_item * +load_entry(col_item *col, nis_object *obj, int rw) +{ + char *p; + char *val; + int len; + char *last; + ent_item *ent = 0; + + p = ENTRY_VAL(obj, col->colnum); + len = ENTRY_LEN(obj, col->colnum); + if (p == NULL || len == 0 || p[0] == '\0' || + (len > 0 && p[len-1] != '\0')) { + syslog(LOG_ERR, + "load_entry: bad entry; empty/NULL or nonNUL terminated"); + return (NULL); + } + + /* make a copy of p because strtok leaves a trail of '\0' */ + p = strdup(p); + if (p == NULL) + return (NULL); + + val = strtok_r(p, col->sep, &last); + while (val) { + /* + * We're going to modify the entry, so we need RW access. + * XXX We expect the caller to be load_table(), and it + * doesn't use the return value other than to check for + * success (non-NULL). Thus, we make no attempt to return + * the (last created) entry with the requested access. + */ + if (ent != 0) { + /* Release entry from previous lap */ + release_entry(ent, 1); + } + ent = (ent_item *)nis_find_item_rw(val, &col->entries, RW); + if (ent == NULL) { + ent = (ent_item *)calloc(1, sizeof (ent_item)); + if (ent == NULL) { + free((void *)p); + return (NULL); + } + ent->item.name = strdup(val); + if (ent->item.name == NULL) { + free((void *)p); + free((void *)ent); + return (NULL); + } + if (col->case_insensitive) { + strlower(ent->item.name); + } + ent->obj = NULL; + ent->nobj = 0; + ent->col = col; + if (!nis_insert_item_rw((NIS_HASH_ITEM *)ent, + &col->entries, RW)) { + free((void *)p); + free((void *)ent->item.name); + free((void *)ent); + return (NULL); + } + } + ent->obj = (nis_object **)realloc(ent->obj, + (ent->nobj + 1) * sizeof (entry_obj *)); + if (ent->obj == NULL) { + ent->nobj = 0; + free((void *)p); + release_entry(ent, 1); + return (NULL); + } + ent->obj[ent->nobj] = obj; + ent->nobj += 1; + + val = strtok_r(NULL, col->sep, &last); + } + + free((void *)p); + return (ent); +} + +static +tab_item * +load_table(nis_object *tobj, int rw) +{ + int i; + int j; + int multival; + table_col *cols; + table_col *col; + table_col colbuf; + dir_item *dir; + tab_item *tab; + int ncols; + col_item **collist; + char name[NIS_MAXNAMELEN]; + char tblbuf[NIS_MAXNAMELEN * 2]; + char *table; + nis_db_list_result *res; + obj_list *objs; + int nobj; + ent_item *tmpent = 0; + + /* + * Get directory item. Create it if it doesn't exist. + */ + dir = (dir_item *)nis_find_item_rw(tobj->zo_domain, &dirs, RO); + if (dir == NULL) { + dir = (dir_item *)calloc(1, sizeof (dir_item)); + if (dir == NULL) + return (NULL); + dir->item.name = strdup(tobj->zo_domain); + if (dir->item.name == NULL) { + free((void *)dir); + return (NULL); + } + if (!nis_insert_item_rw((NIS_HASH_ITEM *)dir, &dirs, RO)) { + free((void *)dir->item.name); + free((void *)dir); + return (NULL); + } + } + + /* create table item and add to directory item */ + tab = (tab_item *)calloc(1, sizeof (tab_item)); + if (tab == NULL) { + __nis_release_item(dir, &dirs, RO); + return (NULL); + } + tab->item.name = strdup(tobj->zo_name); + if (tab->item.name == NULL) { + free((void *)tab); + __nis_release_item(dir, &dirs, RO); + return (NULL); + } + tab->dir = dir; + if (!nis_insert_item_rw((NIS_HASH_ITEM *)tab, &dir->tables, rw)) { + free((void *)tab->item.name); + free((void *)tab); + __nis_release_item(dir, &dirs, RO); + return (NULL); + } + + cols = tobj->TA_data.ta_cols.ta_cols_val; + ncols = tobj->TA_data.ta_cols.ta_cols_len; + + /* + * We keep a copy of each of the columns in an array so that + * we can load the entries for each one. Each element of the + * array is also stored in 'tab'. + */ + collist = (col_item **)calloc(ncols, sizeof (col_item *)); + if (collist == NULL) { + release_table(tab, 1); + return (NULL); + } + + for (i = 0; i < ncols; i++) { + multival = 0; + if ((cols[i].tc_flags & TA_SEARCHABLE) == 0 && + (cols[i].tc_flags & TA_MULTIVAL) != 0) { + multival = 1; + col = &cols[i]; + } +#ifdef FORCE_GROUP + if (!multival) { + if (i == 3 && + strcasecmp(tobj->TA_data.ta_type, + "group_tbl") == 0) { + multival = 1; + colbuf = cols[i]; + colbuf.tc_flags |= ((',' << 24) & 0xff000000); + col = &colbuf; + } + } +#endif /* FORCE_GROUP */ + if (multival) { + collist[i] = load_column(tab, col, i, RO); + if (collist[i] == NULL) { + int c; + + for (c = 0; c++; c < i) + release_column(collist[c], 0); + release_table(tab, 1); + free((void *)collist); + return (NULL); + } + } + } + + sprintf(name, "%s.%s", tobj->zo_name, tobj->zo_domain); + table = internal_table_name(name, tblbuf); + if (table == NULL) { + for (i = 0; i < ncols; i++) + release_column(collist[i], 0); + release_table(tab, 1); + free((void *)collist); + return (NULL); + } + + res = db_list_flags(name, 0, NULL, FN_NORAGS); + if (res == 0 || + (res->status != NIS_SUCCESS && res->status != NIS_NOTFOUND)) { + for (i = 0; i < ncols; i++) { + release_column(collist[i], 0); + } + release_table(tab, 1); + free((void *)collist); + if (res != 0) + free_db_list(res); + return (NULL); + } + + tab->res = res; + objs = tab->res->objs; + for (i = 0; i < tab->res->numo; i++) { + for (j = 0; j < ncols; j++) { + if (collist[j]) { + char *p; + int len; + + /* + * Make sure we have a valid column string + * before calling load_entry. + */ + p = ENTRY_VAL(objs[i].o, + (collist[j])->colnum); + len = ENTRY_LEN(objs[i].o, + (collist[j])->colnum); + if (p == NULL || len == 0 || p[0] == '\0' || + (len > 0 && p[len-1] != '\0')) + continue; + + if (!(tmpent = load_entry(collist[j], + objs[i].o, RO))) { + int c; + + for (c = 0; c < ncols; c++) + release_column(collist[c], 0); + release_table(tab, 1); + free((void *)collist); + return (NULL); + } + release_entry(tmpent, 0); + } + } + } + + for (i = 0; i < ncols; i++) + release_column(collist[i], 0); + + free((void *)collist); + + return (tab); +} + +static +void +free_table(tab_item *tab) +{ + col_item *col; + + while ((col = (col_item *)nis_pop_item(&tab->columns)) != NULL) { + free_column(col); + } + free((void *)tab->item.name); + free_db_list(tab->res); + free((void *)tab); +} + +static +void +free_column(col_item *col) +{ + ent_item *ent; + + while ((ent = (ent_item *)nis_pop_item(&col->entries)) != NULL) { + free_entry(ent); + } + free((void *)col->item.name); + free((void *)col); +} + +static +void +free_entry(ent_item *ent) +{ + free((void *)ent->item.name); + free((void *)ent->obj); + free((void *)ent); +} + +static +void +strlower(char *s) +{ + while (*s) { + if (isupper(*s)) + *s = tolower(*s); + s++; + } +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ns_proc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ns_proc.c new file mode 100644 index 0000000000..6401e7af87 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ns_proc.c @@ -0,0 +1,1038 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1990-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Ported from : + * "@(#)nis_ns_proc.c 1.15 91/03/01 Copyr 1990 Sun Micro"; + * + * nis_ns_proc.c + * + * This module contains the actual implementation of NIS version 3. + * NB : It provides the routines that the dispatch function in nis_svc.c + * call. That file, nis_svc.c, is automatically generated and reflects the + * interface definition that is described in the nis.x file. When the + * nis.x file changes, you must make sure that any parameters that change + * get reflected in these routines. + * + * This module contains the Namespace manipulation procedures. + */ + +#include <stdio.h> +#include <syslog.h> +#include <stdlib.h> +#include <rpc/rpc.h> +#include <rpc/clnt.h> +#include <rpc/svc.h> +#include <rpc/auth.h> +#include <rpc/auth_des.h> +#include <rpcsvc/nis.h> +#include "nis_proc.h" + +extern bool_t xdr_nis_result(); + +extern nis_object* get_root_object(); +extern int update_root_object(nis_name, nis_object *); +extern void add_pingitem_with_name(char *buf, nis_object *dir, + u_long ptime, NIS_HASH_TABLE *tbl); +extern nis_name __nis_local_root(); +extern char *relative_name(); + +/* + * Handle the case in which we have the directory object but not its table. + * It is called when we try to lookup an item and get back a NIS_NOSUCHTABLE. + * On master, table should always exist; missing due to a mkdir failure + * master: create table & return (item) not found + * master & readonly: return system error + * On replica: indicates we have not seen directory's update yet. + * replica: add to pinglist and return NIS_NOT_ME + * replica & readonly: return NIS_NOT_ME + */ +nis_error +recover_from_no_table(nis_object* d_obj, nis_name dirname) +{ + if (we_serve(&(d_obj->DI_data), MASTER_ONLY)) { + if (readonly) + return (NIS_TRYAGAIN); + else { + /* create table */ + nis_error status; + + if ((status = db_create(dirname, &tbl_prototype)) != + NIS_SUCCESS) { + syslog(LOG_ERR, + "Unable to create table %s: %s.", + dirname, nis_sperrno(status)); + return (status); + } else + return (NIS_SUCCESS); + } + } else { + /* replica */ + if (!readonly) /* force update */ + add_pingitem(d_obj, time(0), &upd_list); + return (NIS_NOT_ME); + } +} + +/* + * nis_lookup is the basic function used by clients of the name service. + * This function translates the request into an access of the local + * database. + */ +nis_result * +nis_lookup_svc(argp, reqstp) + ns_request *argp; + struct svc_req *reqstp; +{ + nis_result *res; + nis_db_result *dbres; + nis_object *d_obj = 0; + char pname[1024]; + struct ticks t; + int read_dir, /* read access on directory */ + read_obj; /* read access on an object */ + nis_error xx; + char *p; + + if (verbose) + syslog(LOG_INFO, "LOOKUP_SVC : '%s'", argp->ns_name); + __start_clock(0); + res = (nis_result *)XCALLOC(1, sizeof (nis_result)); + add_cleanup((void (*)()) XFREE, (char *)(res), "ns_lookup result"); + + if (reqstp) + nis_getprincipal(pname, reqstp); + else + pname[0] = '\0'; + + if ((p = relative_name(argp->ns_name)) == NULL) { + int want_root; + nis_name root_dir = __nis_local_root(); + + want_root = root_dir && + (nis_dir_cmp(argp->ns_name, root_dir) == SAME_NAME); + if (!want_root) + res->status = NIS_NOT_ME; + else { + if (! root_server) { + /* maybe we have been added */ + xx = __directory_object(argp->ns_name, + &t, 0, &d_obj); + res->aticks = t.aticks; + res->dticks = t.dticks; + res->zticks = t.zticks; + res->cticks = t.cticks; + if (d_obj == NULL) + res->status = NIS_NOT_ME; + else { + /* make copy to be returned */ + nis_object* clone; + + clone = nis_clone_object(d_obj, 0); + d_obj = clone; + } + } else { + d_obj = get_root_object(); + if (d_obj == NULL) + res->status = NIS_NAMEUNREACHABLE; + } + + if (d_obj) { + read_dir = __can_do(NIS_READ_ACC, + d_obj->zo_access, + d_obj, pname); + if (! read_dir) { + res->status = NIS_PERMISSION; + nis_destroy_object(d_obj); + } else { + NIS_RES_OBJECT(res) = d_obj; + NIS_RES_NUMOBJ(res) = 1; + res->status = NIS_SUCCESS; + add_cleanup(nis_destroy_object, + (char *)(d_obj), + "ns_lookup objects"); + } + } + } + res->zticks = __stop_clock(0); + return (res); + } + free((void *)p); + + /* + * Make sure we are allowed to READ this directory. + * + * POLICY: servers _must_ be able to read their own directory + * objects. + */ + xx = __directory_object(nis_domain_of(argp->ns_name), &t, 0, &d_obj); + res->aticks = t.aticks; + res->dticks = t.dticks; + res->zticks = t.zticks; + res->cticks = t.cticks; + if (d_obj == NULL) { + res->status = xx; /* status from __directory_object call */ + res->zticks = __stop_clock(0); + return (res); + } + + read_dir = __can_do(NIS_READ_ACC, d_obj->zo_access, d_obj, pname); + + /* Look it up in the data base */ + dbres = db_lookup(argp->ns_name); + if (dbres->status != NIS_SUCCESS) { + if (dbres->status == NIS_NOSUCHTABLE) { + /* This means table of domain does not exist */ + dbres->status = + recover_from_no_table(d_obj, + nis_domain_of(argp->ns_name)); + /* for sure, argp->ns_name will not be there */ + if (dbres->status == NIS_SUCCESS) + dbres->status = NIS_NOTFOUND; + } + if (read_dir) { + res->status = dbres->status; + res->dticks = dbres->ticks; + } else { + res->status = NIS_PERMISSION; + res->dticks = 0; + } + res->zticks = __stop_clock(0); + if (verbose) + syslog(LOG_INFO, "nis_lookup : exit status %s", + nis_sperrno(res->status)); + return (res); + } + + read_obj = __can_do(NIS_READ_ACC, dbres->obj->zo_access, + dbres->obj, pname); + + if (! read_dir && ! read_obj) { + res->status = NIS_PERMISSION; + res->zticks = __stop_clock(0); + if (verbose) + syslog(LOG_INFO, + "nis_lookup : exit status PERMISSION DENIED"); + return (res); + } + + res->status = NIS_SUCCESS; + res->objects.objects_len = 1; + res->objects.objects_val = dbres->obj; + res->dticks = dbres->ticks; + res->zticks = __stop_clock(0); + + if (verbose) + syslog(LOG_INFO, "nis_lookup : exit status %s", + nis_sperrno(res->status)); + + return (res); +} + +/* + * Internal function that adds an object into the namespace. + */ +static void +add_object(name, obj, dobj, princp, res) + nis_name name; + nis_object *obj, + *dobj; + nis_name princp; + nis_result *res; +{ + directory_obj *d; + nis_db_result *dbres; + u_long xid, ttime; + int i; + int add_ok = 0; + + /* + * d points at the directory specific information + */ + d = &(dobj->DI_data); + + dbres = db_lookup(name); + res->dticks += dbres->ticks; + if (dbres->status != NIS_NOTFOUND) { + if (dbres->status == NIS_SUCCESS) + res->status = NIS_NAMEEXISTS; + else + res->status = dbres->status; + return; + } + + /* + * If we are adding a directory, make sure that it is a legitimate + * name, i.e. the new directory must be a descendant of the domain + * whose server is making this request. If we are in the root domain, + * then we can add a new directory object in the same domain. + */ + if (__type_of(obj) == NIS_DIRECTORY_OBJ) { + switch (nis_dir_cmp(obj->DI_data.do_name, dobj->DI_data.do_name)) { + case LOWER_NAME: + break; + case SAME_NAME: + if (root_server) break; + default: + res->status = NIS_BADNAME; + return; + } + } + + /* + * Verify access rights. Check first to see if the directory object + * grants the right. If not, then check to see if the object access + * rights for that type of object grant the right. And if that + * doesn't then abort with PERMISSION denied. + */ + add_ok = __can_do(NIS_CREATE_ACC, dobj->zo_access, dobj, princp); + if (! add_ok) { + if (auth_verbose) { + syslog(LOG_INFO, + "add_object : create DENIED by directory %s.%s", + dobj->zo_name, dobj->zo_domain); + } + for (i = 0; i < d->do_armask.do_armask_len; i++) { + if (obj->zo_data.zo_type == OATYPE(d, i)) { + add_ok = __can_do(NIS_CREATE_ACC, + OARIGHTS(d, i), dobj, princp); + if (auth_verbose) { + syslog(LOG_INFO, + "add_object : create %s by object type.", + (add_ok) ? "ALLOWED" : "DENIED"); + } + break; + } + } + } else if (auth_verbose) { + syslog(LOG_INFO, + "add_object : create is ALLOWED by the directory."); + } + + if (! add_ok) { + res->status = NIS_PERMISSION; + return; + } + + /* + * Start the transaction, if we can't get an XID abort + */ + xid = begin_transaction(princp); + if (! xid) { + res->status = NIS_TRYAGAIN; + return; + } + + /* Do the add operation */ + obj->zo_oid.ctime = (unsigned long)time(0); + obj->zo_oid.mtime = obj->zo_oid.ctime; + dbres = db_add(name, obj, 0); + res->dticks += dbres->ticks; + res->status = dbres->status; + if (res->status != NIS_SUCCESS) + abort_transaction(xid); + else { + ttime = (u_long)(time(0)); + end_transaction(xid); /* COMMIT */ + /* Notify replicates of the change. */ + if (dobj->DI_data.do_servers.do_servers_len > 1) + add_pingitem(dobj, ttime, &ping_list); + } + return; + +} + +/* + * Internal function that removes an object from the namespace. + */ +static void +remove_object(name, obj, dobj, princp, res) + nis_name name; + nis_object *obj, + *dobj; + nis_name princp; + nis_result *res; +{ + directory_obj *d; + nis_object *old; + nis_db_result *dbres; + u_long xid, ttime; + int i, rem_ok; + + /* + * d points at the directory specific information + */ + d = &(dobj->DI_data); + + dbres = db_lookup(name); + res->dticks += dbres->ticks; + if (dbres->status != NIS_SUCCESS) { + res->status = dbres->status; + return; + } + + /* This is the existing object in the database */ + old = dbres->obj; + + if (obj && ! same_oid(obj, old)) { + res->status = NIS_NOTSAMEOBJ; + return; + } + + /* + * Verify delete access rights. Check first to see if the directory + * object grants the right. If not, then check to see if the object + * itself grants the right, then finally check to see of we have + * this access right for this type of object. + */ + rem_ok = __can_do(NIS_DESTROY_ACC, dobj->zo_access, dobj, princp); + if (auth_verbose) { + syslog(LOG_INFO, + "remove_object : destroy %s by directory %s.%s", + (rem_ok) ? "ALLOWED" : "DENIED", + dobj->zo_name, dobj->zo_domain); + } + if (! rem_ok) { + rem_ok = __can_do(NIS_DESTROY_ACC, old->zo_access, old, princp); + if (auth_verbose) { + syslog(LOG_INFO, + "remove_object : destroy %s by object %s.%s", + (rem_ok) ? "ALLOWED" : "DENIED", + old->zo_name, old->zo_domain); + } + } + + if (! rem_ok) { + for (i = 0; i < d->do_armask.do_armask_len; i++) { + if (old->zo_data.zo_type == OATYPE(d, i)) { + rem_ok = __can_do(NIS_DESTROY_ACC, + OARIGHTS(d, i), dobj, princp); + break; + } + } + } + if (! rem_ok) { + res->status = NIS_PERMISSION; + if (auth_verbose) { + syslog(LOG_INFO, + "remove_object : destroy DENIED by object type."); + } + return; + } else if (auth_verbose) { + syslog(LOG_INFO, + "remove_object : destroy ALLOWED by object type."); + } + + /* + * Start the transaction, if we can't get an XID abort + */ + xid = begin_transaction(princp); + if (! xid) { + res->status = NIS_TRYAGAIN; + return; + } + + /* Do the remove operation */ + dbres = db_remove(name, old, time(0)); + res->dticks += dbres->ticks; + res->status = dbres->status; + if (res->status != NIS_SUCCESS) + abort_transaction(xid); + else { + ttime = (u_long)(time(0)); + end_transaction(xid); /* COMMIT */ + /* Notify replicates of the change. */ + if (dobj->DI_data.do_servers.do_servers_len > 1) + add_pingitem(dobj, ttime, &ping_list); + /* + * No need to flush the caches - they have been flushed + * inside db_remove + */ + } +} + +/* macro to get to the table column structures */ +#define TABLE_COL(o, n) o->TA_data.ta_cols.ta_cols_val[n] + +/* + * Internal function that modifies an object in the namespace. + */ +static void +modify_object(name, obj, dobj, princp, res) + nis_name name; + nis_object *obj, + *dobj; + nis_name princp; + nis_result *res; +{ + directory_obj *d; + nis_object *old; + nis_db_result *dbres; + u_long xid, ttime; + int i, mod_ok; + log_entry le; + nis_object mod_obj; + + /* + * d points at the directory specific information + */ + d = &(dobj->DI_data); + + dbres = db_lookup(name); + res->dticks += dbres->ticks; + if (dbres->status != NIS_SUCCESS) { + res->status = dbres->status; + return; + } + + /* This is the existing object in the database */ + old = dbres->obj; + + /* + * Verify modify access rights. Check first to see if the directory + * object grants the right. If not, then check to see if the object + * itself grants the right, then finally check to see if we have + * this access right for this type of object. + */ + mod_ok = __can_do(NIS_MODIFY_ACC, dobj->zo_access, dobj, princp); + if (auth_verbose) { + syslog(LOG_INFO, + "modify_object : modify %s by directory %s.%s", + (mod_ok) ? "ALLOWED" : "DENIED", + dobj->zo_name, dobj->zo_domain); + } + if (! mod_ok) { + mod_ok = __can_do(NIS_MODIFY_ACC, old->zo_access, old, princp); + if (auth_verbose) { + syslog(LOG_INFO, + "remove_object : modify %s by object %s.%s", + (mod_ok) ? "ALLOWED" : "DENIED", + old->zo_name, old->zo_domain); + } + } + + if (! mod_ok) { + for (i = 0; i < d->do_armask.do_armask_len; i++) { + if (old->zo_data.zo_type == OATYPE(d, i)) { + if (! __can_do(NIS_MODIFY_ACC, OARIGHTS(d, i), + dobj, princp)) { + res->status = NIS_PERMISSION; + if (auth_verbose) { + syslog(LOG_INFO, + "modify_object : modify DENIED by object type."); + } + return; + } else { + if (auth_verbose) { + syslog(LOG_INFO, + "modify_object : modify ALLOWED by object type."); + } + mod_ok = 1; + break; + } + } + } + } + /* + * if no one allows modify then fail. + */ + if (! mod_ok) { + res->status = NIS_PERMISSION; + return; + } + + /* + * POLICY : Allow changing type ? + * + * ANSWER : No, only changing the meta data + * such as access etc is allowed. + */ + if (__type_of(old) != __type_of(obj)) { + res->status = NIS_BADOBJECT; + return; + } + + if (nis_dir_cmp(old->zo_domain, obj->zo_domain) != SAME_NAME) { + res->status = NIS_BADNAME; + return; + } + + /* + * Start the transaction, if we can't get an XID abort + */ + xid = begin_transaction(princp); + if (! xid) { + res->status = NIS_TRYAGAIN; + return; + } + + /* + * Now we generate a log entry with the old value so that if + * we crash, the transaction system can restore the object + * to its pre-modified state. + */ + memset((char *)&le, 0, sizeof (le)); + le.le_type = MOD_NAME_OLD; + le.le_time = (u_long) (time(0)); + le.le_name = name; + le.le_attrs.le_attrs_len = 0; + le.le_object = *old; + add_update(&le); + + mod_obj = *old; + if (same_oid(old, obj)) { + mod_obj.zo_owner = obj->zo_owner; + mod_obj.zo_group = obj->zo_group; + mod_obj.zo_access = obj->zo_access; + mod_obj.zo_ttl = obj->zo_ttl; + } else if (obj->zo_oid.mtime || obj->zo_oid.ctime) { + res->status = NIS_NOTSAMEOBJ; + abort_transaction(xid); + return; + } + + /* + * Data replacement of the variant part. + * NOTE: We play games with TABLE objects because we can't + * allow their schema to change on the fly. But there are + * fields in the table data that _can_ change such as the + * path of the table, separator character, and access rights. + * + * As in the case of ENTRY objects, we let the 'same oid' test be + * the signal that it is ok to overwrite these variables. This + * is overloading the OID value but I can't think of any other + * way to signal this operation without changing the protocol at + * this point. + */ + if (__type_of(old) != NIS_TABLE_OBJ) + mod_obj.zo_data = obj->zo_data; + else if (same_oid(old, obj)) { + mod_obj.TA_data.ta_path = obj->TA_data.ta_path; + mod_obj.TA_data.ta_sep = obj->TA_data.ta_sep; + mod_obj.TA_data.ta_type = obj->TA_data.ta_type; + for (i = 0; i < mod_obj.TA_data.ta_maxcol; i++) { + if (TABLE_COL(obj, i).tc_flags & TA_MODIFIED) + TABLE_COL((&mod_obj), i).tc_rights = + TABLE_COL(obj, i).tc_rights; + } + } + + mod_obj.zo_oid.mtime = (unsigned long)time(0); + /* + * Now we add the object over the previous + * one. This instructs the database to + * discard the current one and replace it + * with our modified one. (And it's atomic) + */ + dbres = db_add(name, &mod_obj, 1); + res->dticks += dbres->ticks; + res->status = dbres->status; + if (res->status != NIS_SUCCESS) + abort_transaction(xid); + else { + nis_taglist taglist; + nis_tag tags; + + ttime = (u_long)(time(0)); + end_transaction(xid); /* COMMIT */ + + /* Notify replicates of the change. */ + if (dobj->DI_data.do_servers.do_servers_len > 1) + add_pingitem(dobj, ttime, &ping_list); + + /* + * No need to flush the caches - they have been flushed + * inside db_remove + */ + if (__type_of(old) == NIS_DIRECTORY_OBJ) { + /* + * Send all the servers that serve this particular + * directory object a flush_cache message. + * We dont do this for group and table caches because + * they are served by those servers; and we can + * just hope that they get the updates real fast. + * For Directory objects, since we dont want the servers + * to throw out the cached directory objects and then go + * and refresh it with the stale copies, we will use + * TAG_DCACHE_ONE_REFRESH which will + * get a fresh copy of the object from the master. + * + * XXX: Since we are sending this message to all + * servers (including MASTER), this will lead to + * multiple calls to the master. One could have + * perhaps passed a modified directory object + * without the master. + */ + tags.tag_type = TAG_DCACHE_ONE_REFRESH; + tags.tag_val = old->DI_data.do_name; + taglist.tags.tags_len = 1; + taglist.tags.tags_val = &tags; + (void) nis_mcast_tags(&old->DI_data, &taglist); + } + } +} + +/* + * If prim and sec contain the same servers, return 0. + * If sec contains different servers than prim, result is set to: + * <all prim servers> <sec servers that are not in prim's list> + */ +static int +merge_srv_list(directory_obj* prim, directory_obj* sec, nis_server**result) +{ + nis_server* answer = 0; + int psize = prim->do_servers.do_servers_len; + int ssize = sec->do_servers.do_servers_len; + nis_server* prim_server = prim->do_servers.do_servers_val; + nis_server* sec_server = sec->do_servers.do_servers_val; + int i, j, diff = 0, newsize = 0, newadd; + + /* First count how many names in sec are different */ + for (i = 0; i < ssize; i++) { + for (j = 0; j < psize; j++) { + if (nis_dir_cmp(prim_server[j].name, + sec_server[i].name) == SAME_NAME) + break; + } + if (j == psize) + ++diff; + } + + if (diff == 0) + return (0); + + /* different names were found on sec_server list */ + newsize = psize+diff; + answer = (nis_server*)malloc(newsize*sizeof (nis_server)); + + if (answer == 0) + return (0); + + /* Copy prim first */ + for (i = 0; i < psize; i++) + answer[i] = prim_server[i]; + + newadd = i; + for (i = 0; i < ssize; i++) { + for (j = 0; j < psize; j++) { + if (nis_dir_cmp(prim_server[j].name, + sec_server[i].name) == SAME_NAME) + break; + } + if (j == psize) + answer[newadd++] = sec_server[i]; + } + + *result = answer; + return (newsize); +} + +static void +modify_root_object(nis_name name, + nis_object *obj, + nis_name princp, + nis_result *res) +{ + nis_object* oldroot = get_root_object(); + nis_object newroot; + u_long ttime; + + if (! oldroot) { + syslog(LOG_ERR, "Cannot read %s!", ROOT_OBJ); + res->status = NIS_SYSTEMERROR; + return; + } + + if (__can_do(NIS_MODIFY_ACC, oldroot->zo_access, oldroot, princp)) { + newroot = *oldroot; /* copy all fields from old object */ + + if (same_oid(oldroot, obj)) { + /* updating non-data portion as well. */ + newroot.zo_owner = obj->zo_owner; + newroot.zo_group = obj->zo_group; + newroot.zo_access = obj->zo_access; + newroot.zo_ttl = obj->zo_ttl; + } else if (obj->zo_oid.mtime || obj->zo_oid.ctime) { + /* somehow got an outdated copy of object. */ + res->status = NIS_NOTSAMEOBJ; + nis_destroy_object(oldroot); + return; + } /* else, only interested in changing data portion. */ + + newroot.DI_data = obj->DI_data; /* always update data */ + ttime = (u_long)(time(0)); + newroot.zo_oid.mtime = ttime; + if (update_root_object(name, &newroot)) { + nis_server* merged_srvs = 0; + int howmany; + + howmany = merge_srv_list(&(newroot.DI_data), + &(oldroot->DI_data), + &merged_srvs); + /* howmany is 0 if new and old list are the same */ + if (howmany > 0) { + directory_obj *dobj = &(newroot.DI_data); + dobj->do_servers.do_servers_val = merged_srvs; + dobj->do_servers.do_servers_len = howmany; + } + if (newroot.DI_data.do_servers.do_servers_len > 1) + add_pingitem_with_name(ROOT_OBJ, + &newroot, + ttime, + &ping_list); + if (merged_srvs) + free(merged_srvs); + res->status = NIS_SUCCESS; + } else + res->status = NIS_MODFAIL; + } else + res->status = NIS_PERMISSION; + + /* newroot just copies contents of other objects; need not be freed */ + nis_destroy_object(oldroot); +} + +/* + * __nis_nameops() + * + * This code actually implements the operation that is requested. + * It is collected here in one place. + */ +static nis_result * +nis_nameops(op, name, obj, princp) + int op; /* operation to perform */ + nis_name name; /* Object's name */ + nis_object *obj; /* Object to use (NULL for rem) */ + nis_name princp; /* Principal making the request */ +{ + nis_result *res; + nis_object *d_obj; + struct ticks t; + nis_error xx; + char optxt[32]; + + res = (nis_result *)XCALLOC(1, sizeof (nis_result)); + add_cleanup((void (*)())XFREE, (char *)res, "nameops result"); + if (readonly) { + res->status = NIS_TRYAGAIN; + return (res); + } + + if (auth_verbose) { + switch (op) { + case ADD_OP : + strcpy(optxt, "ADD"); + break; + case REM_OP : + strcpy(optxt, "REMOVE"); + break; + case MOD_OP : + strcpy(optxt, "MODIFY"); + break; + } + syslog(LOG_INFO, "Object operation '%s' for principal %s", + optxt, princp); + } + + if (((op == ADD_OP) || (op == MOD_OP)) && (obj == NULL)) { + res->status = NIS_BADOBJECT; + return (res); + } + + /* + * don't allow an object to be added without an owner. + */ + if ((op == ADD_OP) && (strlen(obj->zo_owner) < 2)) { + res->status = NIS_BADOBJECT; + return (res); + } + + /* + * We have to handle the root object specially if we want to + * manipulate it using the NIS code. + */ + if (root_server && + (nis_dir_cmp(name, __nis_rpc_domain()) == SAME_NAME)) { + + /* All we allow is a modify */ + if (op != MOD_OP) { + res->status = NIS_BADREQUEST; + return (res); + } + modify_root_object(name, obj, princp, res); + return (res); + } + + /* + * Fetch the directory object for this domain. + */ + xx = __directory_object(nis_domain_of(name), &t, TRUE, &d_obj); + res->aticks = t.aticks; + res->dticks = t.dticks; + res->zticks = t.zticks; + res->cticks = t.cticks; + if ((xx != NIS_SUCCESS) && (xx != NIS_S_SUCCESS)) { + res->status = xx; + return (res); + } + + /* + * Get the last update time. If the current time is earlier + * someone has set the clock back which is very bad as far + * as NIS+ is concerned. This will catch almost all possible + * situations except the single second when time has finally + * caught up with the last entry in the log. Fixing this + * would require more radical changes to the whole update + * mechanism which is potentially risky and may even require + * protocol changes. + */ + if (last_update(d_obj->DI_data.do_name) > time(0)) { + syslog(LOG_ERR, "Update rejected because system time is " + "earlier than most recent log entry"); + res->status = NIS_SYSTEMERROR; + return (res); + } + + switch (op) { + case ADD_OP : + add_object(name, obj, d_obj, princp, res); + break; + case REM_OP : + remove_object(name, obj, d_obj, princp, res); + break; + case MOD_OP : + modify_object(name, obj, d_obj, princp, res); + break; + } + + return (res); +} + +/* + * nis_add, this function will add a name to the name space if the + * permissions are allowed. The object that is about to be added to + * must allow "CREATE" access to the principal making the request. + */ +nis_result * +nis_add_svc(argp, reqstp) + ns_request *argp; + struct svc_req *reqstp; +{ + nis_result *res; + char pname[1024]; + char *p; + + __start_clock(0); /* Timing information */ + if (verbose) + syslog(LOG_INFO, "ADD_SVC : '%s'", argp->ns_name); + if ((p = relative_name(argp->ns_name)) == NULL) + return (nis_make_error(NIS_BADNAME, 0, 0, 0, __stop_clock(0))); + free((void *)p); + + nis_getprincipal(pname, reqstp); + res = nis_nameops(ADD_OP, argp->ns_name, + argp->ns_object.ns_object_val, pname); + if (verbose) + syslog(LOG_INFO, "nis_add : exit status %s", + nis_sperrno(res->status)); + + res->zticks += __stop_clock(0); + return (res); +} + +/* + * nis_modify, this function modifies the contents of the object that + * are named. It checks the MODIFY access right in the existing object + * and then does the operation. + */ +nis_result * +nis_modify_svc(argp, reqstp) + ns_request *argp; + struct svc_req *reqstp; +{ + nis_result *res; + char pname[1024]; + char *p = NULL; + + __start_clock(0); + if (verbose) + syslog(LOG_INFO, "MODIFY_SVC : '%s'", argp->ns_name); + if (! root_server && ((p = relative_name(argp->ns_name)) == NULL)) + return (nis_make_error(NIS_BADNAME, 0, 0, 0, __stop_clock(0))); + if (p) + free((void *)p); + + nis_getprincipal(pname, reqstp); + res = nis_nameops(MOD_OP, argp->ns_name, + argp->ns_object.ns_object_val, pname); + if (verbose) + syslog(LOG_INFO, "nis_modify : exit status %s", + nis_sperrno(res->status)); + res->zticks += __stop_clock(0); + return (res); +} + +/* + * nis_remove removes and object from the name space. You will notice it + * shares a lot of code with the Add and remove functions. + */ +nis_result * +nis_remove_svc(argp, reqstp) + ns_request *argp; + struct svc_req *reqstp; +{ + nis_result *res; + char *principal, pname[1024]; + char *p; + + __start_clock(0); + if (verbose) + syslog(LOG_INFO, "REMOVE_SVC : '%s'", argp->ns_name); + if ((p = relative_name(argp->ns_name)) == NULL) + return (nis_make_error(NIS_BADNAME, 0, 0, 0, __stop_clock(0))); + free((void *)p); + + /* + * If 'reqstp' is NULL, we're being called internally, and supply + * the local principal. + */ + if (reqstp != 0) { + nis_getprincipal(pname, reqstp); + principal = pname; + } else { + principal = nis_local_principal(); + } + res = nis_nameops(REM_OP, argp->ns_name, + argp->ns_object.ns_object_val, + principal); + if (verbose) + syslog(LOG_INFO, "nis_remove : exit status %s", + nis_sperrno(res->status)); + res->zticks += __stop_clock(0); + return (res); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_opacc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_opacc.c new file mode 100644 index 0000000000..79f8d7ee31 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_opacc.c @@ -0,0 +1,400 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1998-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <malloc.h> +#include <syslog.h> +#include <string.h> +#include <rpc/rpc.h> +#include <rpc/svc.h> +#include <rpcsvc/nis.h> +#include "nis_proc.h" + + +typedef enum {STRING, EMPTY, NIL, NONE} __match_class; + +static bool_t __nis_dir_op_access(char *, char *, nis_name, char *); +static __match_class __subop_match_class(char *); +static __match_class __entry_match_class(char *, entry_obj *); + +#define nil(x) (x)?(x):"<NULL>" + +#define OP_ACC_TABLE "proto_op_access" +#define OP_COL "op" +#define SUBOP_COL "subop" +#define SUBOP_COL_NUM 1 +#define MAX_OPNAME "NIS_FINDDIRECTORY" +#define MAX_SUBOPNAME "Make sure this is longer than the longest tag etc." + +/* + * Verify access to the specified NIS+ protocol operation (and, optionally, + * sub-operation). Parameters: + * + * op Required. Name of operation. Example: "NIS_PING". + * + * subop Optional. Name of sub-operation. Example: "TAG_DEBUG" + * for the "NIS_STATUS" operation. + * + * dir Optional. NIS+ directory for which check is performed. + * If NULL, all directories served by this rpc.nisd are + * checked. + * + * pname Optional. Name of principal. If NULL, this routine + * derives the principal name from the reqstp argument. + * + * reqstp Optional and ignored unless pname == NULL. The RPC + * request. + */ +bool_t +nis_op_access(char *op, char *subop, nis_name dir, char *pname, + struct svc_req *reqstp) { + + char pnamebuf[1024]; + char *dirl; + + + /* No check at security levels 0 and 1 */ + if (secure_level < 2) + return (TRUE); + + /* Sanity check arguments */ + if (op == 0 || + (pname == 0 && reqstp == 0) || + (strlen(op) > sizeof (MAX_OPNAME)) || + (subop != 0 && strlen(subop) > sizeof (MAX_SUBOPNAME))) + return (FALSE); + + /* Get the principal name */ + if (pname == 0) { + pname = pnamebuf; + nis_getprincipal(pname, reqstp); + } + +#ifdef OPACCDEBUG + printf("nis_op_access(%s, %s, %s, %s, 0x%x)\n", + op, (subop!=0)?subop:"<NULL>", (dir!=0)?dir:"<NULL>", + pname, reqstp); +#endif /* OPACCDEBUG */ + + if (dir != 0 && dir[0] != '\0') { + return (__nis_dir_op_access(op, subop, dir, pname)); + } else { + char *curdir, *nxtdir; + + if (nis_server_control(SERVING_LIST, DIR_GETLIST, &dirl) == 0) + return (FALSE); + +#ifdef OPACCDEBUG + printf("serving list = \"%s\"\n", dirl); +#endif /* OPACCDEBUG */ + + for (curdir = nxtdir = dirl; *curdir != '\0'; curdir = nxtdir) { + while (*nxtdir != ' ' && *nxtdir != '\0') + nxtdir++; + if (*nxtdir != '\0') + *nxtdir++ = '\0'; + if (!__nis_dir_op_access(op, subop, curdir, pname)) { + free(dirl); + return (FALSE); + } + } + free(dirl); + } + + return (TRUE); +} + + +static +bool_t __nis_dir_op_access(char *op, char *subop, nis_name dir, char *pname) { + + char lookup[NIS_MAXNAMELEN + /* dir name */ + sizeof (OP_ACC_TABLE) + /* table name */ + sizeof (MAX_OPNAME) + /* search ... */ + sizeof (MAX_SUBOPNAME)+ /* ... criteria */ + sizeof ("[=,=]. ")]; /* syntax + NUL */ + ib_request req; + nis_error err; + nis_result *res; + int i; + __match_class required_match, best_entry, be_match, tmp; + bool_t ret = TRUE; + + + if (subop == 0) + sprintf(lookup, "[%s=%s]%s.%s", OP_COL, op, OP_ACC_TABLE, dir); + else + sprintf(lookup, "[%s=%s,%s=%s]%s.%s", + OP_COL, op, SUBOP_COL, subop, OP_ACC_TABLE, dir); + + err = nis_get_request(lookup, 0, 0, &req); + if (err != NIS_SUCCESS) + return (TRUE); + + res = __nis_local_lookup(&req, 0, 1, 0, 0); + + nis_free_request(&req); + +#ifdef OPACCDEBUG + printf("nis_local_lookup(%s) => 0x%x, status = %d\n", + lookup, res, (res!=0)?res->status:-1); +#endif /* OPACCDEBUG */ + + /* No result at all or no such table => assume access OK */ + if (res == 0) + return (TRUE); + else if (res->status == NIS_NOSUCHTABLE) { + nis_freeresult(res); + return (TRUE); + } + + /* + * If we didn't find any entries, then one of two situations apply: + * + * (1) We were looking for 'op' only, and if it isn't in the + * the table, we allow access. + * + * (2) We were looking for both 'op' and 'subop'. It's possible + * that there's an entry for 'op' only, so try again. + */ + if (res->status == NIS_NOTFOUND || res->status == NIS_PARTIAL) { + nis_freeresult(res); + if (subop == 0) { + return (TRUE); + } else { + return (__nis_dir_op_access(op, 0, dir, pname)); + } + } else if (res->status != NIS_SUCCESS) { + /* + * XXX Should we succeed or fail ? + * For maximum backward compatibility, we declare success + */ +#ifdef OPACCDEBUG + for (i = 0; i < NIS_RES_NUMOBJ(res); i++) { + printf("\t------------- %d --------------\n", i); + nis_print_object(&(NIS_RES_OBJECT(res)[i])); + } +#endif /* OPACCDEBUG */ + nis_freeresult(res); + return (TRUE); + } + + /* + * If there was more than one result, look for one that: + * + * (1) has a matching sub-operation, or + * + * (2) has an empty string in the subop field, or + * + * (3) has a NIL subop field, or + * + * (4) has no subop field + * + * in that order. + */ + if (NIS_RES_NUMOBJ(res) == 1) { +#ifdef OPACCDEBUG + printf("\tone matching entry\n"); +#endif /* OPACCDEBUG */ + ret = __can_do(NIS_READ_ACC, NIS_RES_OBJECT(res)->zo_access, + NIS_RES_OBJECT(res), pname); + } else if (NIS_RES_NUMOBJ(res) > 1) { + required_match = __subop_match_class(subop); + best_entry = 0; + be_match = __entry_match_class(subop, + &(NIS_RES_OBJECT(res)->EN_data)); +#ifdef OPACCDEBUG + printf("\t%d matching entries\n", NIS_RES_NUMOBJ(res)); +#endif /* OPACCDEBUG */ + for (i = 1; i < NIS_RES_NUMOBJ(res); i++) { +#ifdef OPACCDEBUG + printf("\t%s\n", NIS_RES_OBJECT(res)[i].zo_name); +#endif /* OPACCDEBUG */ + tmp = __entry_match_class(subop, + &(NIS_RES_OBJECT(res)[i].EN_data)); + if (tmp <= be_match) { + be_match = tmp; + best_entry = i; + if (be_match <= required_match) + break; + } + } +#ifdef OPACCDEBUG + printf("\trequired_match = %d, be_match = %d, best_entry = %d\n", + required_match, be_match, best_entry); + printf("\t\t%s %s\n", + ENTRY_VAL(&(NIS_RES_OBJECT(res)[best_entry]), 0), + nil(ENTRY_VAL(&(NIS_RES_OBJECT(res)[best_entry]), 1))); +#endif /* OPACCDEBUG */ + ret = __can_do(NIS_READ_ACC, + NIS_RES_OBJECT(res)[best_entry].zo_access, + &(NIS_RES_OBJECT(res)[best_entry]), pname); + } + + nis_freeresult(res); +#ifdef OPACCDEBUG + printf("\t%s\n", ret?"OK":"NO ACCESS"); +#endif /* OPACCDEBUG */ + return (ret); +} + + +/* Map callback pid/tid to anonymous number */ +static callback_id *callback_id_list = NULL; +static anonid_t callback_anonid = 0; +static DECLMUTEXLOCK(anonid); + +/* + * Add id to callback list + */ +anonid_t +nis_add_callback_id(pthread_t id, nis_name pname) { + callback_id *entry = malloc(sizeof(callback_id)); + callback_id *cp; + anonid_t anonid; + + if (entry == NULL) { + syslog(LOG_WARNING, "nis_add_callback_pid: unable to malloc"); + return(NOANONID); + } + + entry->id = id; + entry->pname = strdup(pname); + + MUTEXLOCK(anonid, "nis_add_callback_id"); + + /* + * In the MT case, we use the thread id as the anonymous id. We do + * this because we want to avoid race conditions by having the thread + * itself both insert and remove its entry on the callback_id_list. + * Hence, since the id is returned by the parent of the thread, + * the implication is that the anonymous id must be something known + * to both, and the thread id fits that requirement. + */ + entry->anonid = id; + cp = callback_id_list; + entry->next = cp; + callback_id_list = entry; + MUTEXUNLOCK(anonid, "nis_add_callback_id"); + + return(anonid); +} + +/* + * Remove callback id + */ +void +nis_delete_callback_id(pthread_t id) { + callback_id *entry, **prev; + + if (id == INV_PTHREAD_ID) + return; + + MUTEXLOCK(anonid, "nis_delete_callback_id"); + for (prev = &callback_id_list, entry = *prev; entry != NULL; + prev = &(entry->next), entry = entry->next) { + if (entry->id == id) { + *prev = entry->next; + free(entry->pname); + free(entry); + break; + } + } + MUTEXUNLOCK(anonid, "nis_delete_callback_id"); + + return; +} + +/* + * Return the id and principal name corresponding to an anonymous id + */ +pthread_t +nis_get_callback_id(anonid_t anonid, nis_name pname, int pnamelen) { + + callback_id *entry; + pthread_t id = INV_PTHREAD_ID; + + MUTEXLOCK(anonid, "nis_get_callback_id"); + for (entry = callback_id_list; entry != NULL; entry = entry->next) { + if (entry->anonid == anonid) { + id = entry->id; + if (pname != 0) { + strncpy(pname, entry->pname, pnamelen); + } + break; + } + } + MUTEXUNLOCK(anonid, "nis_get_callback_id"); + + return(id); +} + +/* Classify a subop string */ +__match_class +__subop_match_class(char *subop) { + + if (subop == 0) { +#ifdef OPACCDEBUG + printf("\t\t\t<NIL>\n"); +#endif /* OPACCDEBUG */ + return (NIL); + } else if (*subop == '\0') { +#ifdef OPACCDEBUG + printf("\t\t\t<EMPTY>\n"); +#endif /* OPACCDEBUG */ + return (EMPTY); + } else { +#ifdef OPACCDEBUG + printf("\t\t\t%s\n", subop); +#endif /* OPACCDEBUG */ + return (STRING); + } +} + +/* + * Classify the subop in an entry. If it's a string, compare to the + * subop argument. + */ +__match_class +__entry_match_class(char *subop, entry_obj *entry) { + + __match_class ret; + char *entry_subop; + + if (entry == 0 || entry->en_cols.en_cols_len <= SUBOP_COL_NUM) { + ret = NONE; + } else { + entry_subop = + entry->en_cols.en_cols_val[SUBOP_COL_NUM].ec_value.ec_value_val; + ret = __subop_match_class(entry_subop); + if (ret == STRING && + (subop == 0 || strcmp(subop, entry_subop) != 0)) + ret = NONE; + } + return (ret); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_proc.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_proc.h new file mode 100644 index 0000000000..4ef2185f88 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_proc.h @@ -0,0 +1,472 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _NIS_PROC_H +#define _NIS_PROC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <pthread.h> +#include <nis_servlist.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * nis_proc.h + * + * This module contains definitions used by the NIS service. + * + */ + +/* + * Structure definitions used by the various functions + */ +struct nis_db_result { + nis_error status; /* Status of the result */ + nis_object *obj; /* Pointer to the object returned */ + unsigned long ticks; /* Number of clock ticks used */ +}; + +typedef struct nis_db_result nis_db_result; + +struct obj_list { + u_char r; /* readable flag */ + nis_object *o; /* object */ +}; + +typedef struct obj_list obj_list; +struct nis_db_list_result { + nis_error status; /* return status */ + obj_list *objs; /* Objects returned */ + int numo; /* Number of objects */ + unsigned long ticks; /* Database ticks */ +}; + +typedef struct nis_db_list_result nis_db_list_result; + +struct nis_fn_result { + nis_error status; /* Return status */ + nis_object *obj; /* The object returned */ + netobj cookie; /* The magic cookie (db state) */ + unsigned long ticks; /* the obligatory ticks value */ +}; + +typedef struct nis_fn_result nis_fn_result; + +struct limits { + int max_attrval; /* Maximum length of 1 attribute */ + int max_attr; /* Maximum number of attributes */ + int max_columns; /* Maximum number of columns */ +}; + +struct ticks { + u_long aticks; + u_long dticks; + u_long zticks; + u_long cticks; +}; + +struct object_list { + int num; + nis_object *objs; +}; + +struct cleanup { + void (*func)(); + void *data; + char *tag; + int id; + struct cleanup *next; +}; +typedef struct cleanup cleanup; + +struct xdr_clean_data { + bool_t (*xdr_func)(); + char *xdr_data; +}; +typedef struct xdr_clean_data xdr_clean_data; + + +#define INIT_UPD_LIST_TIME_INCR 120 +#define MAX_UPD_LIST_TIME_INCR 1920 + +/* + * "nis_mt.h" needs some of the declarations above (notably nis_fn_result), + * but also redefines some items below, so it needs to be included here. + */ +#include "nis_mt.h" +/* + * We include <nisdb_ldap.h> and "nis_ldap.h" here as a convenience for + * the rpc.nisd source files. + */ +#include <nisdb_ldap.h> +#include "nis_ldap.h" + +struct ping_item { + NIS_HASH_ITEM item; /* Item struct for hash functions */ + u_long mtime; /* Time of the update */ + nis_object *obj; /* Directory object. */ + u_long utime; /* Time we will retry this entry */ + u_long delta; /* Current value of time increment */ +}; + +typedef struct ping_item ping_item; + +struct sdata { + void *buf; /* Memory allocation pointer */ + u_long size; /* Size of allocated data */ +}; + +/* operations statistics vector */ +struct ops_stats { + u_long op; /* Operation (procedure number) */ + u_long calls; /* Times called */ + u_long errors; /* Error encountered in call */ + int cursamp; /* current sample index */ + u_long tsamps[16]; /* last 16 time measurements */ +}; + +/* + * Used for caching table objects + */ +struct table_item { + NIS_HASH_ITEM ti_item; /* Generic ITEM tag */ + nis_object *ibobj; /* Table object */ +}; + +/* + * Map callback pid/tid to an anonymous number + */ +typedef pid_t anonid_t; +struct callback_id { + pthread_t id; + anonid_t anonid; + nis_name pname; + struct callback_id *next; +}; +typedef struct callback_id callback_id; + +/* + * Forward reference declarations for functions in the service and in + * the nis library + */ + +#ifdef __STDC__ +/* in nis_db.c */ +extern void __make_legal(char *); +extern nis_db_result *db_add(nis_name, nis_object *, int); +extern nis_error __db_add(nis_name, nis_object *, int); +extern nis_db_result *db_remove(nis_name, nis_object *, u_long); +extern nis_error __db_remove(nis_name, nis_object *); +extern nis_db_result *db_lookup(nis_name); +extern nis_db_result *db_lookup_deferred(nis_name, bool_t); +extern nis_db_list_result *db_list(nis_name, int, nis_attr *); +extern nis_db_list_result *db_list_flags(nis_name, int, nis_attr *, u_long); +extern void free_db_list(nis_db_list_result *); +extern nis_db_result *db_addib(nis_name, int, nis_attr *, nis_object *, + nis_object *); +extern nis_error __db_addib(nis_name, int, nis_attr *, nis_object *); +extern nis_error __db_addib_nolog(nis_name, int, nis_attr *, + nis_object *); +extern nis_error __db_addib_nosync(nis_name, int, nis_attr *, + nis_object *); +extern nis_db_result *db_remib(nis_name, int, nis_attr *, obj_list *, int, + nis_object *, u_long); +extern nis_error __db_remib(nis_name, int, nis_attr *); +extern nis_error __db_remib_nosync(nis_name, int, nis_attr *); +extern nis_fn_result *db_firstib(nis_name, int, nis_attr *, int, char *); +extern nis_fn_result *db_nextib(nis_name, netobj *, int, char *); +extern nis_error db_find_table(nis_name); +extern nis_error db_create(nis_name, table_obj*); +extern nis_error db_destroy(nis_name); +extern void db_flush(nis_name, netobj *); +extern void add_checkpoint(nis_name); +extern int checkpoint_db(); +extern nis_name __table_name(nis_name); +extern char *internal_table_name(nis_name, char *); +extern int nis_db_sync_log(void); +extern nis_error db_configure(char *); +extern int loadLDAPdata(void); + +/* in nis_subr_proc.c */ +extern int nis_strcasecmp(char *, char *); +extern int nis_isserving(nis_object *); +extern nis_error __directory_object(nis_name, struct ticks *, int, + nis_object **); +extern nis_error __directory_object_msg(nis_name, struct ticks *, int, + nis_object **, int); +extern void nis_getprincipal(char *, struct svc_req *); +extern nis_error get_object_safely(nis_name, u_long, nis_object **); +extern nis_result *svc_lookup(nis_name, u_long); +extern int __can_do(u_long, u_long, nis_object *, nis_name); +extern u_char *__get_xdr_buf(int); +extern char *__get_string_buf(int); +extern entry_col *__get_entry_col(int); +extern table_col *__get_table_col(int); +extern nis_attr *__get_attrs(int); +extern int nis_isstable(log_entry *, int); +extern void flush_tablecache(nis_name); +extern u_long nis_cptime(nis_server *, nis_name); +extern u_long nis_cptime_msg(nis_server *, nis_name, bool_t, bool_t); +extern void make_stamp(nis_name, u_long); +extern int replica_update(ping_item *); +extern int ping_replicas(ping_item *); +extern void add_pingitem(nis_object *, u_long, NIS_HASH_TABLE *); +extern void do_cleanup(cleanup *); +extern void add_cleanup(void (*)(), void *, char *); +extern void do_xdr_cleanup(xdr_clean_data *); +extern void add_xdr_cleanup(bool_t (*)(), char *, char *); +extern nis_result *__nis_local_lookup(ib_request *, u_long, int, void *, + int (*)()); + +/* in nis_ib_proc.c */ +extern nis_object *nis_censor_object_attr(nis_object *, table_col *, + nis_name, u_int, nis_attr *); +extern nis_object *nis_censor_object(nis_object *, table_col *, nis_name); +extern endpoint *__nis_alt_callback_server(endpoint *, u_int, + struct netbuf *, char **, int); + +/* in nis_log_svc.c */ +extern u_long last_update(nis_name); +extern int apply_transaction(log_entry *); +extern int checkpoint_log(void); +extern u_long add_update(log_entry *); +extern u_long add_update_nosync(log_entry *, void **); +extern int abort_transaction(int); +extern void entries_since(nis_object *, u_long, log_result *); + +/* in nis_log_common.c */ +extern int begin_transaction(nis_name); +extern int end_transaction_x(int, int); +extern int end_transaction(int); +extern int lockTransLog(char *, int, int); +extern void unlockTransLog(char *, int); +extern int map_log(char *, int); +extern void sync_log(); + +/* nis_opacc.c */ +extern bool_t nis_op_access(char *, char *, nis_name, char *, + struct svc_req *); +extern void nis_delete_callback_id(pthread_t); +extern pthread_t nis_get_callback_id(anonid_t, nis_name, int); +extern anonid_t nis_add_callback_id(pthread_t, nis_name); + +#else + +/* in nis_db.c */ +extern void __make_legal(); +extern nis_db_result *db_add(); +extern nis_error __db_add(); +extern nis_db_result *db_remove(); +extern nis_error __db_remove(); +extern nis_db_result *db_lookup(); +extern nis_db_result *db_lookup_deferred(); +extern nis_db_list_result *db_list(); +extern nis_db_list_result *db_list_flags(); +extern void free_db_list(); +extern nis_db_result *db_addib(); +extern nis_error *__db_addib(); +extern nis_error *__db_addib_nolog(); +extern nis_error *__db_addib_nosync(); +extern nis_db_result *db_remib(); +extern nis_error __db_remib(); +extern nis_error __db_remib_nosync(); +extern nis_fn_result *db_firstib(); +extern nis_fn_result *db_nextib(); +extern nis_error db_find_table(); +extern nis_error db_create(); +extern nis_error db_destroy(); +extern void db_flush(); +extern void add_checkpoint(); +extern int checkpoint_db(); +extern nis_name __table_name(); +extern int nis_db_sync_log(); +extern nis_error db_configure(); +extern int loadLDAPdata(); + +/* in nis_subr_proc.c */ +extern int nis_strcasecmp(); +extern int nis_isserving(); +extern nis_error __directory_object(); +extern nis_error __directory_object_msg(); +extern void nis_getprincipal(); +extern nis_error get_object_safely(); +extern nis_result *svc_lookup(); +extern int __can_do(); +extern u_char *__get_xdr_buf(); +extern char *__get_string_buf(); +extern entry_col *__get_entry_col(); +extern table_col *__get_table_col(); +extern nis_attr *__get_attrs(); +extern int nis_isstable(); +extern void flush_tablecache(); +extern u_long nis_cptime(); +extern void make_stamp(); +extern void replica_update(); +extern void add_pingitem(); +extern void do_cleanup(); +extern void add_cleanup(); +extern void do_xdr_cleanup(); +extern void add_xdr_cleanup(); +extern nis_result *__nis_local_lookup(); + +/* in nis_ib_proc.c */ +extern nis_object *nis_censor_object_attr(); +extern nis_object *nis_censor_object(); +extern endpoint *__nis_alt_callback_server(); + + +/* in nis_log_svc.c */ +extern u_long last_update(); +extern int apply_transaction(); +extern int checkpoint_log(); +extern u_long add_update(); +extern u_long add_update_nosync(); +extern int abort_transaction(); +extern void entries_since(); + +/* in nis_log_common.c */ +extern int begin_transaction(); +extern int end_transaction_x(); +extern int end_transaction(); +extern int lockTransLog(); +extern void unlockTransLog(); +extern int map_log(); +extern void sync_log(); +extern int __log_resync(); +extern char *__make_name(); + +/* nis_opacc.c */ +extern bool_t nis_op_access(); +extern void nis_delete_callback_id(); +extern pid_t nis_get_callback_id(); +extern anonid_t nis_add_callback_id(); +#endif + +/* + * Declarations for global state used by the service. + */ +extern int root_server; /* set by the -r switch */ +extern int verbose; /* set by the -v switch */ +extern int static_root; /* set by network partition */ +extern int secure_level; /* set by the -S switch */ +extern NIS_HASH_TABLE upd_list; /* added during a ping. */ +extern NIS_HASH_TABLE ping_list; /* added during an update. */ +extern NIS_HASH_TABLE checkpoint_list; /* added for a checkpoint. */ +extern int checkpoint_all; +extern NIS_HASH_TABLE *table_cache; /* cache of table objects */ +extern int ping_pid, upd_pid; /* Processes doing the work */ + +/* + * MACRO definitions for the various source modules. + */ +#define STATUS(r) r.status +#define OBJECT(r) r.objects.objects_val +#define NUMOBJ(r) r.objects.objects_len +#define CBDATA(r) r.cookie +#define COOKIE(r) r.cookie +#define same_oid(o1, o2) (((o1)->zo_oid.mtime == (o2)->zo_oid.mtime) &&\ + ((o1)->zo_oid.ctime == (o2)->zo_oid.ctime)) + +#define ROOT_OBJ "root.object" +#define PARENT_OBJ "parent.object" + +#define ENVAL ec_value.ec_value_val +#define ENLEN ec_value.ec_value_len +#define ZAVAL zattr_val.zattr_val_val +#define ZALEN zattr_val.zattr_val_len + +#define FN_NOMANGLE 0 /* don't mangle objects on first/next calls */ +#define FN_MANGLE 1 /* mangle objects on first/next calls */ +#define FN_NORAGS 2 /* don't put malloc data on the rag list */ +#define FN_NOERROR 4 /* don't generate error on missing table */ + +#define ADD_OP 1 +#define REM_OP 2 +#define MOD_OP 3 +#define FIRST_OP 4 +#define NEXT_OP 5 + +#define GETSERVER(o, n) (((o)->DI_data.do_servers.do_servers_val) + n) +#define MAXSERVER(o) (o)->DI_data.do_servers.do_servers_len +#define MASTER(o) GETSERVER(o, 0) + +#define CHILDPROC (getpid() != master_pid) + +#define DIR_IDLE_TIME 120 + +#define NOANONID (anonid_t)-1 + +/* Global variables defined in nis_log_common.c */ +extern pid_t master_pid; + +/* Global variables defined in nis_main.c */ +extern cleanup *looseends; /* Data that needs to be freed */ +extern cleanup *free_rags; /* free cleanup structs that are available */ +extern int children; /* Server load control */ +extern int max_children; /* Max number of forked children */ +extern int readonly; /* When true the service is read only */ +extern int need_checkpoint; /* When true the log should be checkpointed */ +extern int force_checkpoint; /* When true the log WILL be checkpointed */ +extern int auth_verbose; /* verbose authorization messages */ +extern table_obj tbl_prototype; /* directory "table" format */ + +/* + * A set of defines for tracking memory problems. A simple memory allocator + * and checker is part of nis_malloc.c and when DEBUG is defined and this + * file is linked in, we generate messages on allocs/frees and check for + * freeing free memory, etc. + */ +#ifdef MEM_DEBUG +#define XFREE xfree +#define XMALLOC xmalloc +#define XCALLOC xcalloc +#define XSTRDUP xstrdup +#ifdef __STDC__ +extern void xfree(void *); +extern char *xstrdup(char *); +extern char *xmalloc(int); +extern char *xcalloc(int, int); +#else +extern void xfree(); +extern char *xstrdup(); +extern char *xmalloc(); +extern char *xcalloc(); +#endif /* __STDC__ */ +#else +#define XFREE free +#define XMALLOC malloc +#define XCALLOC calloc +#define XSTRDUP strdup +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _NIS_PROC_H */ diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_service.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_service.c new file mode 100644 index 0000000000..191c104a70 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_service.c @@ -0,0 +1,656 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * nis_service.c + * + * This module contains the dispatch functions for the NIS+ service. At one + * time it was generated by rpcgen, however, due to the requirement that it + * be able to compile to a 4.1/sockets version or a 5.0/tli version and the + * desire to collect statistics about the time spent in the service functions, + * it is now "real" source. Changes to the .x file will have to be reconciled + * with this file. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> /* getenv, exit */ +#include <syslog.h> +#include <signal.h> +#include <sys/types.h> +#include <memory.h> +#include <stropts.h> +#include <netconfig.h> +#include <sys/resource.h> /* rlimit */ +#include <rpc/rpc.h> +#include <rpc/svc.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/yp_prot.h> +#include <string.h> +#include <netdir.h> + +#include "nis_proc.h" +#include "nis_svc.h" + +#include <nisdb_mt.h> +#include <ldap_util.h> + +extern int _rpcpmstart; /* Started by a port monitor ? */ +extern int _rpcfdtype; /* Whether Stream or Datagram ? */ +extern int _rpcsvcdirty; /* Still serving ? */ + +/* + * Private object name checking and printing functions for NIS+ + */ +#define NIS_SVCARG_NOCHECK 0x00000000 +#define NIS_SVCARG_NULLPTR 0x00000001 +#define NIS_SVCARG_EMPTYSTRING 0x00000002 +#define NIS_SVCARG_TRAILINGDOT 0x00000004 +#define NIS_SVCARG_MAXLEN 0x00000008 +#define NIS_SVCARG_ROOTOBJECT 0x00000010 + +/* + * NIS_SVCARG_ROOTOBJECT is not included in the CHECKALL set (below) as + * in effect it is in reverse sense to the TRAILINGDOT check. + * In addition, we only want to check for the ROOTOBJECT in a few + * particular cases. + */ + +#define NIS_SVCARG_CHECKALL (NIS_SVCARG_NULLPTR | \ + NIS_SVCARG_EMPTYSTRING | \ + NIS_SVCARG_TRAILINGDOT | \ + NIS_SVCARG_MAXLEN) + +static bool_t legal_object_name(nis_name, uint_t); +static nis_name safe_object_name(nis_name); + + +/* + * NIS Version 2 (YP) Dispatch table + */ +extern int *ypproc_domain_svc(); +extern int *ypproc_domain_nonack_svc(); +extern struct ypresp_master *ypproc_master_svc(); +extern struct ypresp_val *ypproc_match_svc(); +extern struct ypresp_key_val *ypproc_first_svc(); +extern struct ypresp_key_val *ypproc_next_svc(); +extern struct ypresp_all *ypproc_all_svc(); +extern struct ypresp_maplist *ypproc_maplist_svc(); +extern bool xdr_ypresp_all(); + +void +ypprog_svc(rqstp, transp) + struct svc_req *rqstp; + register SVCXPRT *transp; +{ + union { + string_t ypproc_domain_svc_arg; + string_t ypproc_domain_nonack_svc_arg; + struct ypreq_key ypproc_match_svc_arg; + struct ypreq_nokey ypproc_first_svc_arg; + struct ypreq_key ypproc_next_svc_arg; + struct ypreq_nokey ypproc_all_svc_arg; + struct ypreq_nokey ypproc_master_svc_arg; + string_t ypproc_maplist_svc_arg; + } argument; + char *result; + bool (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + mark_activity(); + + _rpcsvcdirty = 1; + switch (rqstp->rq_proc) { + case NULLPROC: + (void) svc_sendreply(transp, xdr_void, (char *)NULL); + _rpcsvcdirty = 0; + return; + + case YPPROC_DOMAIN: + xdr_argument = xdr_ypdomain_wrap_string; + xdr_result = (bool (*)()) xdr_bool; + local = (char *(*)()) ypproc_domain_svc; + break; + + case YPPROC_DOMAIN_NONACK: + xdr_argument = xdr_ypdomain_wrap_string; + xdr_result = (bool (*)()) xdr_bool; + local = (char *(*)()) ypproc_domain_nonack_svc; + break; + + case YPPROC_MATCH: + xdr_argument = xdr_ypreq_key; + xdr_result = xdr_ypresp_val; + local = (char *(*)()) ypproc_match_svc; + break; + + case YPPROC_FIRST: + xdr_argument = xdr_ypreq_nokey; + xdr_result = xdr_ypresp_key_val; + local = (char *(*)()) ypproc_first_svc; + break; + + case YPPROC_NEXT: + xdr_argument = xdr_ypreq_key; + xdr_result = xdr_ypresp_key_val; + local = (char *(*)()) ypproc_next_svc; + break; + + case YPPROC_ALL: + xdr_argument = xdr_ypreq_nokey; + xdr_result = xdr_ypresp_all; + local = (char *(*)()) ypproc_all_svc; + break; + + case YPPROC_MASTER: + xdr_argument = xdr_ypreq_nokey; + xdr_result = xdr_ypresp_master; + local = (char *(*)()) ypproc_master_svc; + break; + + case YPPROC_MAPLIST: + xdr_argument = xdr_ypdomain_wrap_string; + xdr_result = xdr_ypresp_maplist; + local = (char *(*)()) ypproc_maplist_svc; + break; + + default: + svcerr_noproc(transp); + _rpcsvcdirty = 0; + return; + } + (void) memset((char *)&argument, 0, sizeof (argument)); + if (!svc_getargs(transp, (xdrproc_t)xdr_argument, (char *)&argument)) { + svcerr_decode(transp); + _rpcsvcdirty = 0; + return; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, (xdrproc_t)xdr_result, + result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, (char *)&argument)) { + syslog(LOG_ERR, "yp_svc: unable to free arguments"); + exit(1); + } + + __nis_thread_cleanup(__nis_get_tsd()); + + _rpcsvcdirty = 0; +} + +struct ops_stats nisopstats[24]; + +static void +start_stat(p) + ulong_t p; +{ + if ((p > 0) && (p < 24)) { + WLOCK(nisopstats); + nisopstats[p].calls++; + WULOCK(nisopstats); + __start_clock(7); + } +} + +static void +stop_stat(p, e) + ulong_t p; + int e; +{ + struct ops_stats *os; + ulong_t ndx; + + if ((p > 0) && (p < 24)) { + WLOCK(nisopstats); + os = &(nisopstats[p]); + os->tsamps[os->cursamp] = __stop_clock(7); + os->cursamp = (++(os->cursamp)%16); + if (e) + os->errors++; + WULOCK(nisopstats); + } +} + +/* + * NIS Version 3 (NIS+) dispatch function. + */ + +void +nis_prog_svc(rqstp, transp) + struct svc_req *rqstp; + register SVCXPRT *transp; +{ + union { + ns_request nis_lookup_svc_arg; + ns_request nis_add_svc_arg; + ns_request nis_modify_svc_arg; + ns_request nis_remove_svc_arg; + ib_request nis_iblist_svc_arg; + ib_request nis_ibadd_svc_arg; + ib_request nis_ibmodify_svc_arg; + ib_request nis_ibremove_svc_arg; + ib_request nis_ibfirst_svc_arg; + ib_request nis_ibnext_svc_arg; + fd_args nis_finddirectory_svc_arg; + nis_taglist nis_status_svc_arg; + dump_args nis_dumplog_svc_arg; + dump_args nis_dump_svc_arg; + netobj nis_callback_svc_arg; + nis_name nis_cptime_svc_arg; + nis_name nis_checkpoint_svc_arg; + ping_args nis_ping_svc_arg; + nis_taglist nis_servstate_svc_arg; + nis_name nis_mkdir_svc_arg; + nis_name nis_rmdir_svc_arg; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + ulong_t starttime; + nis_name *nameaddr = NULL; + uint_t docheck = NIS_SVCARG_NOCHECK; + struct netconfig *nconfp; + char *ipaddrp; + + mark_activity(); + + _rpcsvcdirty = 1; + + start_stat(rqstp->rq_proc); + + if (verbose) { + if ((nconfp = __rpcfd_to_nconf(rqstp->rq_xprt->xp_fd, + rqstp->rq_xprt->xp_type)) != NULL) { + if ((ipaddrp = taddr2uaddr(nconfp, + svc_getrpccaller(rqstp->rq_xprt))) != NULL) { + syslog(LOG_DEBUG, "nis_prog_svc: " + "Request %d from %s", + rqstp->rq_proc, ipaddrp); + free(ipaddrp); + } + freenetconfigent(nconfp); + } + } + + switch (rqstp->rq_proc) { + case NULLPROC: + (void) svc_sendreply(transp, xdr_void, (char *)NULL); + _rpcsvcdirty = 0; + stop_stat(rqstp->rq_proc, 0); + return; + + case NIS_LOOKUP: + xdr_argument = xdr_ns_request; + xdr_result = xdr_nis_result; + local = (char *(*)()) nis_lookup_svc; + nameaddr = &argument.nis_lookup_svc_arg.ns_name; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_ADD: + xdr_argument = xdr_ns_request; + xdr_result = xdr_nis_result; + local = (char *(*)()) nis_add_svc; + nameaddr = &argument.nis_add_svc_arg.ns_name; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_MODIFY: + xdr_argument = xdr_ns_request; + xdr_result = xdr_nis_result; + local = (char *(*)()) nis_modify_svc; + nameaddr = &argument.nis_modify_svc_arg.ns_name; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_REMOVE: + xdr_argument = xdr_ns_request; + xdr_result = xdr_nis_result; + local = (char *(*)()) nis_remove_svc; + nameaddr = &argument.nis_remove_svc_arg.ns_name; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_IBLIST: + xdr_argument = xdr_ib_request; + xdr_result = xdr_nis_result; + local = (char *(*)()) nis_iblist_svc; + nameaddr = &argument.nis_iblist_svc_arg.ibr_name; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_IBADD: + xdr_argument = xdr_ib_request; + xdr_result = xdr_nis_result; + local = (char *(*)()) nis_ibadd_svc; + nameaddr = &argument.nis_ibadd_svc_arg.ibr_name; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_IBMODIFY: + xdr_argument = xdr_ib_request; + xdr_result = xdr_nis_result; + local = (char *(*)()) nis_ibmodify_svc; + nameaddr = &argument.nis_ibmodify_svc_arg.ibr_name; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_IBREMOVE: + xdr_argument = xdr_ib_request; + xdr_result = xdr_nis_result; + local = (char *(*)()) nis_ibremove_svc; + nameaddr = &argument.nis_ibremove_svc_arg.ibr_name; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_IBFIRST: + xdr_argument = xdr_ib_request; + xdr_result = xdr_nis_result; + local = (char *(*)()) nis_ibfirst_svc; + nameaddr = &argument.nis_ibfirst_svc_arg.ibr_name; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_IBNEXT: + xdr_argument = xdr_ib_request; + xdr_result = xdr_nis_result; + local = (char *(*)()) nis_ibnext_svc; + nameaddr = &argument.nis_ibnext_svc_arg.ibr_name; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_FINDDIRECTORY: + xdr_argument = xdr_fd_args; + xdr_result = xdr_fd_result; + local = (char *(*)()) nis_finddirectory_svc; + nameaddr = &argument.nis_finddirectory_svc_arg.dir_name; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_STATUS: + xdr_argument = xdr_nis_taglist; + xdr_result = xdr_nis_taglist; + local = (char *(*)()) nis_status_svc; + break; + + case NIS_DUMPLOG: + xdr_argument = xdr_dump_args; + xdr_result = xdr_log_result; + local = (char *(*)()) nis_dumplog_svc; + nameaddr = &argument.nis_dumplog_svc_arg.da_dir; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_DUMP: + xdr_argument = xdr_dump_args; + xdr_result = xdr_log_result; + local = (char *(*)()) nis_dump_svc; + nameaddr = &argument.nis_dump_svc_arg.da_dir; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_CALLBACK: + xdr_argument = xdr_netobj; + xdr_result = xdr_bool; + local = (char *(*)()) nis_callback_svc; + break; + + case NIS_CPTIME: + xdr_argument = xdr_nis_name; + xdr_result = xdr_u_long; + local = (char *(*)()) nis_cptime_svc; + nameaddr = &argument.nis_cptime_svc_arg; + /* + * Setup docheck with the NIS_SVCARG_ROOTOBJECT flag so that + * objects called 'root.object' can have their time checked. + */ + docheck = NIS_SVCARG_CHECKALL | NIS_SVCARG_ROOTOBJECT; + break; + + case NIS_CHECKPOINT: + xdr_argument = xdr_nis_name; + xdr_result = xdr_cp_result; + local = (char *(*)()) nis_checkpoint_svc; + nameaddr = &argument.nis_checkpoint_svc_arg; + docheck = NIS_SVCARG_NULLPTR | NIS_SVCARG_TRAILINGDOT | + NIS_SVCARG_MAXLEN | NIS_SVCARG_ROOTOBJECT; + break; + + case NIS_PING: + xdr_argument = xdr_ping_args; + xdr_result = xdr_void; + local = (char *(*)()) nis_ping_svc; + nameaddr = &argument.nis_ping_svc_arg.dir; + /* + * Setup docheck with the NIS_SVCARG_ROOTOBJECT flag so that + * objects called 'root.object' can be 'pinged'. + */ + docheck = NIS_SVCARG_CHECKALL | NIS_SVCARG_ROOTOBJECT; + break; + + case NIS_SERVSTATE: + xdr_argument = xdr_nis_taglist; + xdr_result = xdr_nis_taglist; + local = (char *(*)()) nis_servstate_svc; + break; + + case NIS_MKDIR: + xdr_argument = xdr_nis_name; + xdr_result = xdr_nis_error; + local = (char *(*)()) nis_mkdir_svc; + nameaddr = &argument.nis_mkdir_svc_arg; + docheck = NIS_SVCARG_CHECKALL; + break; + + case NIS_RMDIR: + xdr_argument = xdr_nis_name; + xdr_result = xdr_nis_error; + local = (char *(*)()) nis_rmdir_svc; + nameaddr = &argument.nis_rmdir_svc_arg; + docheck = NIS_SVCARG_CHECKALL; + break; + + default: + svcerr_noproc(transp); + _rpcsvcdirty = 0; + stop_stat(rqstp->rq_proc, 1); + return; + } + (void) memset((char *)&argument, 0, sizeof (argument)); + if (!svc_getargs(transp, xdr_argument, (char *)&argument)) { + svcerr_decode(transp); + _rpcsvcdirty = 0; + stop_stat(rqstp->rq_proc, 1); + return; + } + if (nameaddr == NULL || + legal_object_name(*nameaddr, docheck) == TRUE) { + result = (*local)(&argument, rqstp); + } else { + /* + * Service routine expects an object name argument, but + * the name supplied was bad. + */ + result = (char *)nis_make_error(NIS_BADNAME, 0, 0, 0, 0); + add_xdr_cleanup(xdr_nis_result, result, "nis_svc result"); + if (verbose) + syslog(LOG_INFO, "nis_svc: bad name '%s'", + safe_object_name(*nameaddr)); + } + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, (char *)&argument)) { + syslog(LOG_ERR, "nis_svc: unable to free arguments"); + exit(1); + } + _rpcsvcdirty = 0; + + /* If the DB deferred an object deletion, perform it now */ + { + nisdb_tsd_t *tsd = __nisdb_get_tsd(); + + if (tsd != 0) { + nisdb_obj_del_t *nod, *next; + ns_request nsr; + nis_result *res; + nis_error *err; + + for (nod = tsd->objDelList; nod != 0; nod = next) { + next = nod->next; + + nsr.ns_name = nod->objName; + nsr.ns_object.ns_object_len = 0; + nsr.ns_object.ns_object_val = 0; + + res = nis_remove_svc(&nsr, 0); + if (res == 0) { + logmsg(MSG_NOTIMECHECK, LOG_ERR, + "Deferred nis_remove(\"%s\") => NULL", + nod->objName); + nod->objType = NIS_BOGUS_OBJ; + } else if (res->status != NIS_SUCCESS) { + logmsg(MSG_NOTIMECHECK, LOG_ERR, + "Deferred nis_remove(\"%s\") => %s", + nod->objName, + nis_sperrno(res->status)); + nod->objType = NIS_BOGUS_OBJ; + } + + if (res != 0) + nis_freeresult(res); + + if (nod->objType == NIS_DIRECTORY_OBJ) { + err = nis_rmdir_svc(&nod->objName, 0); + if (err == 0 || *err != NIS_SUCCESS) + logmsg(MSG_NOTIMECHECK, LOG_ERR, + "Deferred nis_rmdir(\"%s\") => %s", + nod->objName, + err != 0 ? nis_sperrno(*err) : + "<NIL>"); + } + + sfree(nod->objName); + sfree(nod); + } + + tsd->objDelList = 0; + } + } + + __nis_thread_cleanup(__nis_get_tsd()); + + stop_stat(rqstp->rq_proc, 0); +} + +/* + * check_object_name() + * + * Return TRUE if the following assertions hold for "name", FALSE otherwise: + * + * - Not a NULL pointer + * + * - Not the empty string + * + * - Does not exceed the maximum allowed length for a nis_name + * + * - Ends with trailing dot. + * + */ +static bool_t +legal_object_name(nis_name name, uint_t docheck) +{ + size_t namelen; + + if (docheck == NIS_SVCARG_NOCHECK) + return (TRUE); + + if (name == NULL) + if ((docheck & NIS_SVCARG_NULLPTR) == NIS_SVCARG_NULLPTR) + return (FALSE); + else + /* NULL pointer allowed and present; no more checks */ + return (TRUE); + + namelen = strlen(name); + + if (namelen == 0) + if ((docheck & NIS_SVCARG_EMPTYSTRING) == + NIS_SVCARG_EMPTYSTRING) + return (FALSE); + else + /* Empty string allowed and present; no more checks */ + return (TRUE); + + if ((docheck & NIS_SVCARG_MAXLEN) == NIS_SVCARG_MAXLEN && + namelen >= NIS_MAXNAMELEN) + return (FALSE); + + + /* + * Check for ROOT_OBJECT (root.object) as a valid name. Only + * usually set for special cases when dealing with pinging & + * checking times on root objects. + */ + + if ((docheck & NIS_SVCARG_ROOTOBJECT) == NIS_SVCARG_ROOTOBJECT && + (strcmp(ROOT_OBJ, name) == 0)) + return (TRUE); + + if ((docheck & NIS_SVCARG_TRAILINGDOT) == NIS_SVCARG_TRAILINGDOT && + name[namelen-1] != '.') + return (FALSE); + + return (TRUE); +} + +/* + * safe_object_name() + * + * Returns a version of the nis_name argument that is safe to pass to + * printf et al. + * + */ +static nis_name +safe_object_name(nis_name name) +{ + static const nis_name nullname = "<NULL pointer>"; + static const nis_name emptyname = "<Empty string>"; + static const nis_name toolongname = "<Too long>"; + size_t namelen; + + if (name == NULL) + return (nullname); + else if ((namelen = strlen(name)) == 0) + return (emptyname); + else if (namelen >= NIS_MAXNAMELEN) + return (toolongname); + else + return (name); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_subr_proc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_subr_proc.c new file mode 100644 index 0000000000..fe37b2de17 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_subr_proc.c @@ -0,0 +1,3347 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * nis_subr_proc.c + * + * This module contains subroutines that are used by the NIS+ service but + * _not_ the NIS+ clients. NIS+ client routines are in the library module + * nis_subr.c + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <syslog.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <time.h> +#include <sys/time.h> +#include <pwd.h> +#include <thread.h> +#include <gssapi/gssapi.h> +#include <rpc/rpc.h> +#include <rpc/clnt.h> +#include <rpc/svc.h> +#include <rpc/auth.h> +#include <rpc/auth_sys.h> +#include <rpc/auth_des.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/nis_callback.h> +#include <rpcsvc/nis_dhext.h> +#include <errno.h> +#include <sys/stat.h> +#include <limits.h> +#include <rpcsvc/nis_db.h> +#include "nis_svc.h" +/* nis_proc.h includes nis_mt.h */ +#include "nis_proc.h" +#include "log.h" +#include "ldap_parse.h" +#include "ldap_util.h" +#include "nisdb_mt.h" + +#define GETSERVER(o, n) (((o)->DI_data.do_servers.do_servers_val) + n) +#define MAXSERVER(o) (o)->DI_data.do_servers.do_servers_len + +extern void __nis_destroy_callback(); +extern nis_result *nis_iblist_svc(), *nis_lookup_svc(); +extern CLIENT *__nis_get_server(directory_obj *, int); +extern fd_result *__fd_res(nis_name, nis_error, directory_obj*); +extern nis_result *__nis_remote_lookup(ib_request *req, u_long flags, + int list_op, void *cbdata, int (*cback)()); +extern fd_result *__nis_finddirectory_remote(nis_bound_directory **binding, + char *dname); + +/* routines for registering server's availability wrt given dirctory */ +extern db_status nis_put_offline(nis_name, bool_t); +extern db_status nis_put_online(nis_name, __nis_defer_t, bool_t); +extern nis_name __nis_local_root(); + +extern FILE *cons; + +fd_result *dup_fdres(fd_result *, fd_result *); +static nis_error apply_update(log_entry *); + +static struct directory_item { + NIS_HASH_ITEM dl_item; /* Generic ITEM tag */ + nis_object *dl_obj; /* Directory object */ + ulong_t dl_expires; /* Expiration time */ + ulong_t dl_serve; /* nonzero if we serve */ + ulong_t *dl_stamps; /* Replica timestamps */ +}; +typedef struct directory_item dirItem_t; + +static NIS_HASH_TABLE *dl = NULL; +DECLRWLOCK(dl); + +/* + * Release cached directory object. Intended for use by the cleanup + * code, executed when the thread is about to return to the RPC dispatch. + */ +void +dirCacheReleaseRO(void *ptr) { + struct directory_item *d = ptr; + + if (d != 0) + (void) __nis_release_item(d, dl, 1); +} + +void +dirCacheReleaseWR(void *ptr) { + struct directory_item *d = ptr; + + if (d != 0) + (void) __nis_release_item(d, dl, -1); +} + +/* + * Called when function is done with the directory_item, but release + * should be deferred until the thread is done with the current RPC + * request, or exits. + */ +void +dirDeferRelease(struct directory_item *d, int wr) { + if (d != 0) { + if (wr) { + add_cleanup(dirCacheReleaseWR, d, "dir obj from cache"); + } else { + add_cleanup(dirCacheReleaseRO, d, "dir obj from cache"); + } + } +} + +/* + * Destroy a directory cache item. + */ +void +destroyDirCacheItem(void *item) { + struct directory_item *d = item; + + if (d != 0) { + if (d->dl_item.name != 0) + free(d->dl_item.name); + if (d->dl_stamps != 0) + free(d->dl_stamps); + if (d->dl_obj != 0) + nis_destroy_object(d->dl_obj); + free(d); + } +} + +/* + * free_dlitem() + * + * This function will remove a directory_item from the cache. The memory + * will be re-used when no thread is referencing it anymore. + */ +static void +free_dlitem(dd) + struct directory_item *dd; +{ + if (dd != 0) + (void) nis_remove_item(dd->dl_item.name, dl); +} + + +/* + * 1. Flush server's local directory cache. + * 2. Flush cachemgr's cache, using one of the following + * a. given directory object + * b. if none given and directory is in local cache, use its dir object. + * c. if directory is not in local cache, look it up from cachemgr, + * if found, delete it. + * 3. Flush the associated table cache. + */ +void +flush_dircache(name, dobj) + nis_name name; + directory_obj *dobj; +{ + struct directory_item *dd; + int cachemgr_updated = 0; + + if (verbose) + syslog(LOG_INFO, "flush_dircache: name '%s'", name); + + /* Flush given directory object from cachemgr. */ + if (dobj) { + __nis_CacheRemoveEntry(dobj); + cachemgr_updated = 1; + } + + /* Update server's local directory cache. */ + if (dl) { + dd = (struct directory_item *)nis_find_item(name, dl); + if (dd) { + if (cachemgr_updated == 0) { + __nis_CacheRemoveEntry(&(dd->dl_obj->DI_data)); + cachemgr_updated = 1; + } + free_dlitem(dd); + __nis_release_item(dd, dl, -1); + } + } + + /* Update cachemgr if have not done so yet. */ + if (cachemgr_updated == 0) { + directory_obj dobj2; + if (__nis_CacheSearch(name, &dobj2) == NIS_SUCCESS && + nis_dir_cmp(name, dobj2.do_name) == SAME_NAME) { + __nis_CacheRemoveEntry(&dobj2); + xdr_free((xdrproc_t)xdr_directory_obj, (char *)&dobj2); + } + } + + /* + * Since we cache the table for this dir object, + * we will flush that away as well. + */ + flush_tablecache(name); +} + +void +flush_dircache_refresh(name) + nis_name name; +{ + struct ticks t; + nis_object *obj; + + if (verbose) + syslog(LOG_INFO, "flush_dircache_refresh: name '%s'", name); + + flush_dircache(name, (directory_obj *) NULL); /* Flush it first */ + /* + * Refresh it from the master. + * XXX: We may have some scalability problems + * if all servers go to the master at the same time. Hopefully + * directory objects would not be updated very frequently, so it + * should not be such a big problem. + */ + (void) __directory_object(name, &t, NO_CACHE, &obj); +} + +/* + * Flush all cached directory objects from dircache and cachemgr. + */ +void +flush_dircache_all() +{ + struct directory_item *dd, *nxt; + + if (verbose) + syslog(LOG_INFO, "flush_dircache_all"); + + while ((dd = __nis_pop_item_mt(dl)) != 0) { + /* + * Since a destructor is in effect for this list, + * we have nothing to do; 'dd' will be freed when + * it's no longer in use. + */ + } +} + +void +flush_local_dircache(name) + nis_name name; +{ + struct directory_item *dd; + + if (verbose) + syslog(LOG_INFO, "flush_local_dircache: name '%s'", name); + if (dl) { + dd = (struct directory_item *)nis_find_item(name, dl); + if (dd) { + free_dlitem(dd); + __nis_release_item(dd, dl, -1); + } + } +} + +/* + * Flushes the local TABLE cache. Called when the database changes. + */ +void +flush_tablecache(name) + nis_name name; +{ + struct table_item *ti; + + if (verbose) + syslog(LOG_INFO, "flush_tablecache: name '%s'", name); + if (table_cache) { + ti = (struct table_item *)nis_remove_item(name, table_cache); + /* + * We've established an item destructor for the table_cache, + * so the item will be destroyed automatically when it's + * no longer referenced. + */ + } +} + +/* + * Flushes all TABLE caches. + */ +void +flush_tablecache_all() +{ + struct table_item *ti; + + if (verbose) + syslog(LOG_INFO, "flush_tablecache_all"); + if (table_cache) { + while (ti = (struct table_item *)nis_pop_item(table_cache)) { + /* + * We've established an item destructor for the + * table_cache, so the item will be destroyed + * automatically when it's no longer referenced. + */ + } + } +} + +/* + * Flushes a specific item from the group cache. + */ +void +flush_groupcache(name) + nis_name name; +{ + if (verbose) + syslog(LOG_INFO, "flush_groupcache: name '%s'", name); + + __nis_flush_group_exp_name(name); +} + +/* + * Flushes all group caches. + */ +void +flush_groupcache_all() +{ + if (verbose) + syslog(LOG_INFO, "flush_groupcache_all"); + + nis_flushgroups(); +} + +extern enum clnt_stat __nis_cast_proc(); + +/* + * A convenient wrapper around __nis_cast_proc(). + */ +bool_t +nis_mcast_tags(dobj, tags) + directory_obj *dobj; + nis_taglist *tags; +{ + int i; + int nsrv; + nis_server *srv; + + if (dobj == NULL) + return (FALSE); + srv = dobj->do_servers.do_servers_val; + nsrv = dobj->do_servers.do_servers_len; + for (i = 0; i < nsrv; i++) { + __nis_send_msg(&srv[i], NIS_SERVSTATE, xdr_nis_taglist, tags); + } + return (1); +} + +/* + * We need this function, since the root object is stored as a directory + * obj structure and not a nis_object. directory_obj strucs don't have the + * domain name. + * + * Note: Synchronization of access to 'tbl' is the responsibility of the + * caller. + */ +void +add_pingitem_with_name(buf, dir, ptime, tbl) + char *buf; + nis_object *dir; + u_long ptime; + NIS_HASH_TABLE *tbl; +{ + ping_item *pp; + + pp = (ping_item *)nis_find_item(buf, tbl); + if (pp) { + if (verbose) + syslog(LOG_INFO, "add_pingitem: updated %s", buf); + switch (ldapConfig.updateBatching) { + case upd_none: + /* + * We're not accumulating updates, so we only allow + * the mtime to go backwards. + */ + if (ptime < pp->mtime) { + pp->mtime = ptime; + setPingWakeup(pp->mtime + + updateBatchingTimeout()); + } + break; + case bounded_accumulate: + /* Leave pp->mtime unchanged */ + break; + case accumulate: + default: + if (ptime != pp->mtime) { + pp->mtime = ptime; + setPingWakeup(pp->mtime + + updateBatchingTimeout()); + } + break; + } + /* + * Reset update time and backoff delta, since it appears + * that we can talk to the master. + */ + pp->utime = 0; + pp->delta = INIT_UPD_LIST_TIME_INCR; + __nis_release_item(pp, tbl, -1); + return; + } + + pp = (ping_item *)XCALLOC(1, sizeof (ping_item)); + if (! pp) { + if (verbose) + syslog(LOG_ERR, + "add_pingitem: Couldn't add '%s' to pinglist (no memory)", + buf); + return; + } + pp->item.name = (nis_name) XSTRDUP(buf); + if (pp->item.name == NULL) { + if (verbose) + syslog(LOG_ERR, + "add_pingitem: Couldn't add '%s' to pinglist (no memory)", + buf); + XFREE(pp); + return; + } + pp->mtime = ptime; + if (dir) + pp->obj = nis_clone_object(dir, NULL); + else + pp->obj = NULL; + /* Update time (utime) == 0 means resync ASAP */ + pp->utime = 0; + pp->delta = INIT_UPD_LIST_TIME_INCR; + (void) __nis_insert_item_mt(pp, tbl, 0); + setPingWakeup(ptime + updateBatchingTimeout()); + if (verbose) + syslog(LOG_INFO, "add_pingitem: added %s", buf); +} + +void +add_pingitem(dir, ptime, tbl) + nis_object *dir; + u_long ptime; + NIS_HASH_TABLE *tbl; +{ + char buf[1024]; + + if (dir == NULL) + return; + + sprintf(buf, "%s.%s", dir->zo_name, dir->zo_domain); + add_pingitem_with_name(buf, dir, ptime, tbl); +} + +/* + * Only to be called from update_dl_item() via nis_scan_table() + * + * If the master server of the directory in the hash entry matches the + * master that isn't responding reset the expiry time to some point in + * the future. This is only applied to directories due to expire before + * the new expiry time. + */ + +struct reset_dl_expires_args { + nis_name master; + u_long expiry; +}; + +static bool_t +reset_dl_expires(NIS_HASH_ITEM *nhi, void *funcarg) +{ + struct directory_item *dd = (struct directory_item *)nhi; + struct reset_dl_expires_args *args = funcarg; + + if (strcmp(GETSERVER(dd->dl_obj, 0)->name, args->master) == 0) { + if (dd->dl_expires < args->expiry) { + dd->dl_expires = args->expiry; + if (verbose) + syslog(LOG_DEBUG, "reset_dl_expires: reset " + "expiry for directory %s to %d", + dd->dl_item.name, args->expiry); + } else { + if (verbose) + syslog(LOG_DEBUG, "reset_dl_expires: skipping " + "directory %s, expiry %d is later", + dd->dl_item.name, dd->dl_expires); + } + } else { + if (verbose) + syslog(LOG_DEBUG, "reset_dl_expires: skipping " + "directory %s as master (%s) doesn't match", + dd->dl_item.name, GETSERVER(dd->dl_obj, 0)->name); + } + /* Return false as true would terminate the scan */ + return (FALSE); +} + +/* + * Updates directory item with given information. + * If directory requires update, add it to 'upd_list'. + * If directory is root directory, check whether root object requires + * update. + */ +static void +update_dl_item(nis_name name, + struct directory_item *dd, + nis_object* d_obj, + struct timeval *tv, + enum name_pos p) +{ + nis_name rootname; + nis_server *master; + int retry_time = 1800; /* Backoff time for a dead master */ + struct reset_dl_expires_args funcarg; + + dd->dl_expires = tv->tv_sec + d_obj->zo_ttl; + dd->dl_obj = d_obj; + + if ((dd->dl_serve = nis_isserving(d_obj)) == 0) + return; + + /* + * Add it to the list of directories served by this + * server if not already listed. + */ + nis_server_control(SERVING_LIST, DIR_ADD, name); + + if (dd->dl_serve == 1) /* master */ + dd->dl_stamps[0] = last_update(name); + else { /* replica */ + + ulong_t timestamp; + + /* + * schedule update for directory contents but don't + * try too hard. + */ + master = GETSERVER(d_obj, 0); + timestamp = nis_cptime_msg(master, name, FALSE, FALSE); + /* + * If timestamp == 0, (i.e., failed to retrieve a timestamp + * from the master), force the directory object to expire 30 + * minutes later so that we can check the master again. In + * the meantime, we will use the expired directory cache. + * + * We use nis_scan_table_mt() to reset the expiry time on + * all cached directories from this master server. + */ + if (timestamp == 0) { + if (verbose) + syslog(LOG_WARNING, "update_dl_item: " + "unable to get timestamp for %s " + "from master %s", + name, master->name); + funcarg.master = master->name; + funcarg.expiry = tv->tv_sec + retry_time; + if (verbose) + syslog(LOG_INFO, "update_dl_item: " + "reseting expiry to %d", funcarg.expiry); + /* Update the hash list */ + nis_scan_table(dl, reset_dl_expires, &funcarg); + /* + * The current entry may not be on the hash + * list yet so we explicitly reset it here. + */ + dd->dl_expires = funcarg.expiry; + return; + } + /* + * We have a valid timestamp from the master. Check and + * update upd_list accordingly. + */ + dd->dl_stamps[0] = timestamp; + if (!readonly && last_update(name) < timestamp) { + add_pingitem(dd->dl_obj, timestamp, &upd_list); + } + + /* If root object, schedule update for object itself too */ + if (!readonly && p == SAME_NAME && + ((rootname = __nis_local_root()) != 0) && + nis_dir_cmp(name, rootname) == SAME_NAME && + last_update(ROOT_OBJ) < d_obj->zo_oid.mtime) { + add_pingitem_with_name(ROOT_OBJ, + d_obj, + d_obj->zo_oid.mtime, + &upd_list); + } + } +} + + +/* + * These statistics are used to calculate the effectiveness of the + * directory object cache. You can retrieve them with a TAG_DIRCACHE + * in the stats call. + */ +int dircachecall = 0, + dircachehit = 0, + dircachemiss = 0; + +nis_error +__directory_object(name, ticks, is_master, obj) + nis_name name; + struct ticks *ticks; + int is_master; /* Need we be the master ? */ + nis_object **obj; +{ + return (__directory_object_msg(name, ticks, is_master, obj, 1)); +} + +#define INCDIRSTAT(stat) WLOCK(dircachestats); \ + stat ++; \ + WULOCK(dircachestats); + +/* + * __directory_object() + * This function is used to search for directory objects that describe + * directories that this server serves. When called it will either + * return a directory object which describes a directory that is served + * or NULL if the directory is not served by this server. + * It takes three parameters, name, ticks and is_master. Name is + * presumed to be the NIS+ name of the directory we're looking for, ticks + * points to a ticks structure that we can fill in with some statistics + * and is_master indicates that not only should we serve the directory + * but also we must be the master server for that directory. + */ +nis_error +__directory_object_msg(name, ticks, is_master, obj, msg) + nis_name name; + struct ticks *ticks; + int is_master; /* Need we be the master ? */ + nis_object **obj; + int msg; +{ + register nis_object *d_obj = NULL; + register struct directory_item *dd; + nis_db_result *dbres = NULL; + nis_result *zres = NULL; + int lookup_flags = 0; + struct timeval tv; + enum name_pos p; + int serve; + + /* + * A sanity check, if nis_name_of() returns NULL then either the + * name passed is identical to the local directory (in which case + * we can't possibly serve it unless we are a root server of a + * local root), or the name isn't in our tree at all (shares + * no common path). In which case we can't possibly serve it. + */ + *obj = NULL; + + if (name == NULL) + return (NIS_BADNAME); /* illegal name */ + + p = nis_dir_cmp(name, __nis_rpc_domain()); + if ((p == HIGHER_NAME) || (p == NOT_SEQUENTIAL) || (p == BAD_NAME)) + return (NIS_BADNAME); + + /* + * XXX intermediate workaround, until the next source + * crank. The is_master "boolean" gets expanded here to + * be a set of bit flags. In particular, the new flag + * "NO_CACHE" is used to identify when the function calling + * this function wants it to retrieve a new copy of the + * directory object from the cache. + */ + if ((is_master & NO_CACHE) != 0) { + is_master = ((is_master & TRUE) != 0); + /* get latest copy of directory object */ + lookup_flags = MASTER_ONLY+NO_CACHE; + } + + WLOCK(dl); + if (! dl) { + dl = (NIS_HASH_TABLE *)calloc(1, sizeof (NIS_HASH_TABLE)); + if (!dl) { + syslog(LOG_ERR, "__directory_object: out of memory"); + WULOCK(dl); + return (NIS_NOMEMORY); + } + __nis_init_hash_table(dl, destroyDirCacheItem); + } + WULOCK(dl); + + /* some statistics stuff ... */ + memset((char *)ticks, 0, sizeof (struct ticks)); + INCDIRSTAT(dircachecall); + if (verbose) + syslog(LOG_INFO, "Looking for directory object %s", name); + + /* + * Now we check the cache to see if we have it cached locally. + */ + (void) gettimeofday(&tv, NULL); + dd = (struct directory_item *)__nis_find_item_mt(name, dl, 1, 0); + + /* + * If we found it in the cache, and we're asking for the + * "no cache" version, then we flush the cache and + * cause it to be re-looked up. + */ + if (dd && (lookup_flags & NO_CACHE)) { + free_dlitem(dd); + (void) __nis_release_item(dd, dl, 1); + dd = NULL; + } + + if (dd) { + if (verbose) + syslog(LOG_INFO, "__directory_object: Cache hit."); + INCDIRSTAT(dircachehit); + /* + * Found it in the cache. First we check to see if it + * has expired, and then we check to see if we are the + * master server. (we must serve it, otherwise it wouldn't + * be in our cache!) + * + * Check the expiration date on the object + */ + if ((tv.tv_sec < dd->dl_expires) && !(is_master)) { + if (dd->dl_serve) { + *obj = dd->dl_obj; + dirDeferRelease(dd, 0); + return (NIS_SUCCESS); /* Here it is ... */ + } else { + /* Should never happen */ + *obj = NULL; + (void) __nis_release_item(dd, dl, 1); + return (NIS_NOT_ME); + } + } else if ((tv.tv_sec < dd->dl_expires) && is_master) { + if (dd->dl_serve == 1) { + *obj = dd->dl_obj; + dirDeferRelease(dd, 0); + return (NIS_SUCCESS); /* Here it is ... */ + } else { + (void) __nis_release_item(dd, dl, 1); + return (NIS_NOTMASTER); /* Not master */ + } + } + /* Else it is expired so we're refreshing it. */ + } + if (! dd) { + if (verbose) + syslog(LOG_INFO, "__directory_object: Cache miss."); + INCDIRSTAT(dircachemiss); + } + + /* + * At this point we either don't have it in our cache (dd == NULL) + * or it is expired (dd != NULL) so we have to go looking for it + * from either inside (our database) or outside (our parent). + * First, we check to see if we are a root server and they want + * the root directory. + */ + + dbres = db_lookup(name); + ticks->dticks += dbres->ticks; + if (dbres->status == NIS_SUCCESS) { + d_obj = nis_clone_object(dbres->obj, NULL); + } else if (dbres->status == NIS_SYSTEMERROR) { + syslog(LOG_ERR, + "__directory_object: internal database error."); + } else if (dbres->status != NIS_NOTFOUND) { + /* + * (db result-> no such table ) + * Ask the "parent" of "name" since this should not + * recurse to the same server, we don't + * worry about screwing up the current + * server clock (clock(2)). But we will + * get ticks back from the other server + * so we return those in the ticks structure. + */ + zres = nis_lookup(name, lookup_flags); + if (zres->status == NIS_SUCCESS) { + d_obj = nis_clone_object( + zres->objects.objects_val, 0); + ticks->zticks += zres->zticks; + ticks->dticks += zres->dticks; + ticks->cticks += zres->cticks; + ticks->aticks += zres->aticks; + } else if (zres->status == NIS_NOTFOUND) { + if (dd) { + /* It no longer exists */ + free_dlitem(dd); + (void) __nis_release_item(dd, dl, 1); + dd = NULL; + } + } else { + if (msg) + syslog(LOG_WARNING, + "__directory_object: Failed to lookup %s, status %s", + name, nis_sperrno(zres->status)); + } + nis_freeresult(zres); + } else if (dd) { + /* It no longer exists */ + free_dlitem(dd); + (void) __nis_release_item(dd, dl, 1); + dd = NULL; + } + + /* + * At this point, if d_obj is null then we couldn't find + * it. We check to see if we have an expired cached version + * and if so, log a warning and go ahead and use it. If + * we don't have a cached version we just return NOTFOUND. + */ + if (! d_obj) { + if (dd) { + syslog(LOG_WARNING, + "__directory_object: Using expired directory object for %s", name); + if ((!(is_master) && (dd->dl_serve)) || + (is_master && (dd->dl_serve == 1))) { + *obj = dd->dl_obj; + dirDeferRelease(dd, 0); + return (NIS_S_SUCCESS); + } else { + *obj = NULL; + (void) __nis_release_item(dd, dl, 1); + return (NIS_NOT_ME); + } + } else + return (NIS_NOTFOUND); + } + + /* If we found one, and it isn't a directory abort */ + if (__type_of(d_obj) != NIS_DIRECTORY_OBJ) { + if (msg) + syslog(LOG_WARNING, "Object %s isn't a directory.", + name); + /* If the cache had one toss it out */ + if (dd) { + free_dlitem(dd); + (void) __nis_release_item(dd, dl, 1); + } + nis_destroy_object(d_obj); + return (NIS_BADOBJECT); + } + + + if (dd) { + /* + * We could re-acquire the item with a write lock, + * and update it in place. However, in order to reduce + * contention with other threads that might be using + * the directory item, we instead remove it from the list, + * and insert a new one with the updated info. + */ + free_dlitem(dd); + (void) __nis_release_item(dd, dl, 1); + dd = 0; + } + + dd = calloc(1, sizeof (*dd)); + if (dd) { + dd->dl_item.name = (nis_name) strdup(name); + dd->dl_stamps = (ulong_t *)calloc(MAXSERVER(d_obj), + sizeof (ulong_t)); + update_dl_item(name, dd, d_obj, &tv, p); + if (!__nis_insert_item_mt(dd, dl, 1)) { + /* + * Some other thread got there before us. + * Look in the cache for a new copy. + */ + destroyDirCacheItem(dd); + dd = __nis_find_item_mt(name, dl, 1, 0); + if (dd != 0) { + d_obj = dd->dl_obj; + } else { + /* + * It's disappeared. Inform the + * caller. + */ + return (NIS_NOTFOUND); + } + } + } + + /* + * Ok we found the directory object, do we really serve it ? + * Check to see if this server is supposed to serve this + * domain. (Must be master to manipulate the name space) + */ + if (dd) { + serve = dd->dl_serve; + } else { + serve = nis_isserving(d_obj); + } + + /* Check the master requirement */ + if (is_master) { + if (serve != 1) { + if (dd) + (void) __nis_release_item(dd, dl, 1); + else + nis_destroy_object(d_obj); + return (NIS_NOTMASTER); + } else { + *obj = d_obj; + dirDeferRelease(dd, 0); + return (NIS_SUCCESS); + } + } + + if (serve) { + *obj = d_obj; + dirDeferRelease(dd, 0); + return (NIS_SUCCESS); + } + + if (dd) + (void) __nis_release_item(dd, dl, 1); + else + nis_destroy_object(d_obj); + + return (NIS_NOT_ME); +} + +/* + * Returns the NIS+ principal name of the person making the request + * XXX This is set up to use Secure RPC only at the moment, it should + * be possible for any authentication scheme to be incorporated if it + * has a "full name" that we can return as the principal name. + */ +static const nis_name nobody = "nobody"; + +static NIS_HASH_TABLE credtbl = NIS_HASH_TABLE_MT_INIT; + +struct creditem { + NIS_HASH_ITEM item; + char pname[1024]; + time_t cred_expires; +}; + +static void +add_cred_item(char *netname, char *pname, time_t cred_expires) +{ + struct creditem *foo, *old; + + old = (struct creditem *)nis_find_item(netname, &credtbl); + if (old) { + __nis_release_item((NIS_HASH_ITEM *)old, &credtbl, -1); + return; + } + + foo = (struct creditem *)calloc(1, sizeof (struct creditem)); + foo->cred_expires = cred_expires; + foo->item.name = strdup(netname); + strcpy(foo->pname, pname); + __nis_insert_item_mt(foo, &credtbl, 0); +} + +static int +find_cred_item(char *netname, char *pname) +{ + struct creditem *old; + + old = (struct creditem *)nis_find_item(netname, &credtbl); + if (! old || (old->cred_expires < time(0))) { + struct creditem *toremove; + + if (toremove = + (struct creditem *)nis_remove_item(netname, + &credtbl)) { + free(toremove->item.name); + free(toremove); + } + return (0); + } + strcpy(pname, old->pname); + __nis_release_item((NIS_HASH_ITEM *)old, &credtbl, -1); + return (1); +} + +/* + * Check security conf file for classic des compat entry. + * + * Return TRUE if entry found or if no valid mech entries + * exist, else return FALSE. + */ +static bool_t +auth_des_permitted(void) +{ + mechanism_t **mechs; + + if (mechs = __nis_get_mechanisms(FALSE)) { + mechanism_t **mpp; + + for (mpp = mechs; *mpp; mpp++) { + mechanism_t *mp = *mpp; + + if (AUTH_DES_COMPAT_CHK(mp)) { + __nis_release_mechanisms(mechs); + return (TRUE); + } + } + __nis_release_mechanisms(mechs); + return (FALSE); + } else + return (TRUE); +} + +/* + * Check conf file list of mech entries to see if this raw cred mechanism + * type is permitted. Stop processing if the "des" compat entry is reached. + * + * Return TRUE on validation, else FALSE. + */ +static bool_t +mech_is_permitted(rpc_gss_rawcred_t *rcred) +{ + mechanism_t **mechs; + + if (mechs = __nis_get_mechanisms(FALSE)) { + mechanism_t **mpp; + + for (mpp = mechs; *mpp; mpp++) { + mechanism_t *mp = *mpp; + + if (AUTH_DES_COMPAT_CHK(mp)) + break; + + if (mp->mechname && rcred->mechanism) { + if (strcasecmp(mp->mechname, + rcred->mechanism) == 0) { + __nis_release_mechanisms(mechs); + return (TRUE); + } + } + } + __nis_release_mechanisms(mechs); + } + + return (FALSE); +} + +static char * +flavor2str(int flavor) +{ + switch (flavor) { + case AUTH_SYS: + return ("SYS"); + case AUTH_DES: + return ("DES"); + case RPCSEC_GSS: + return ("GSS"); + default: + return ("unknown"); + } +} + +/* + * Given a RPC netname, return the NIS+ principal. + */ +void +nis_getprincipal( + char *name, /* NIS+ principal (out) */ + struct svc_req *req) /* service request (in) */ +{ + struct authsys_parms *au; + struct authdes_cred *ad; + char *rmtdomain; + char srch[2048]; /* search criteria */ + nis_result *res; + int message; + caddr_t auth; + enum_t flavor; + char netname[MAXNETNAMELEN+1] = {0}; + char auth_type[MECH_MAXATNAME+1] = {0}; + + message = verbose || auth_verbose; + strcpy(name, nobody); /* default is "nobody" */ + + if (req) { + flavor = req->rq_cred.oa_flavor; + auth = req->rq_clntcred; + } else { + syslog(LOG_ERR, + "RPC request is NULL: returning '%s'", nobody); + return; + } + + if (flavor == AUTH_NONE) { + if (message) { + syslog(LOG_INFO, + "nis_getprincipal: flavor = NONE: returning '%s'", nobody); + } + return; + } else if (flavor == AUTH_SYS) { /* XXX ifdef this for 4.1 */ + if (secure_level > 1) { + if (message) { + syslog(LOG_INFO, + "nis_getprincipal: flavor = SYS: returning '%s'", + nobody); + } + return; + } + au = (struct authsys_parms *)(auth); + rmtdomain = nis_domain_of(au->aup_machname); + if (au->aup_uid == 0) { + sprintf(name, "%s", au->aup_machname); + if (! rmtdomain) + strcat(name, __nis_rpc_domain()); + if (name[strlen(name) - 1] != '.') + strcat(name, "."); + if (message) { + syslog(LOG_INFO, + "nis_getprincipal: flavor = SYS: returning '%s'", name); + } + return; + } + sprintf(srch, + "[auth_name=\"%d\", auth_type=LOCAL], cred.org_dir.%s", + au->aup_uid, (*rmtdomain == '.') ? + (char *)nis_local_directory() : rmtdomain); + if (srch[strlen(srch) - 1] != '.') { + strcat(srch, "."); + } + } else if (flavor == AUTH_DES) { + + if (secure_level > 1 && ! auth_des_permitted()) { + if (message) { + syslog(LOG_INFO, + "nis_getprincipal: AUTH_DES not permitted: returning '%s'", + nobody); + } + return; + } + + ad = (struct authdes_cred *)(auth); + if (find_cred_item(ad->adc_fullname.name, name)) { + if (message) + syslog(LOG_INFO, + "nis_getprincipal: flavor = DES: returning from cache '%s'", + name); + return; + } + rmtdomain = strchr(ad->adc_fullname.name, '@'); + if (rmtdomain) { + rmtdomain++; + sprintf(srch, + "[auth_name=%s, auth_type=DES], cred.org_dir.%s", + ad->adc_fullname.name, rmtdomain); + if (srch[strlen(srch) - 1] != '.') { + strcat(srch, "."); + (void) strncpy(netname, ad->adc_fullname.name, + sizeof (netname)); + netname[sizeof (netname) - 1] = '\0'; + } + } else { + if (auth_verbose) { + syslog(LOG_INFO, + "nis_getprincipal: flavor = DES: returning '%s'", + nobody); + } + return; + } + } else if (flavor == RPCSEC_GSS) { + rpc_gss_rawcred_t *rcred; + void *cookie; + + if (! rpc_gss_getcred(req, &rcred, NULL, &cookie)) { + if (message) { + syslog(LOG_WARNING, + "nis_getprincipal: GSS getcred failure: returning '%s'", + nobody); + } + return; + } + + if (secure_level > 1 && ! mech_is_permitted(rcred)) { + if (message) + syslog(LOG_INFO, +"nis_getprincipal: RPC GSS mechanism '%s' not permitted: returning nobody", + rcred->mechanism ? rcred->mechanism + : "NULL"); + return; + } + + if (__nis_gssprin2netname(rcred->client_principal, + netname) < 0) { + syslog(LOG_ERR, +"nis_getprincipal: can't extract netname from RPC GSS cred: returning nobody"); + return; + } + + if (find_cred_item(netname, name)) { + if (message) + syslog(LOG_INFO, + "nis_getprincipal: flavor = GSS: returning '%s' from cache", + name); + return; + } + rmtdomain = strchr(netname, '@'); + if (rmtdomain) { + char alias[MECH_MAXALIASNAME+1] = { 0 }; + + rmtdomain++; + if (! __nis_mechname2alias(rcred->mechanism, alias, + sizeof (alias))) { + syslog(LOG_ERR, + "nis_getprincipal: GSS mechname '%s' not found: returning nobody", + rcred->mechanism); + return; + } + + if (alias[0] != '\0') { + (void) __nis_mechalias2authtype(alias, + auth_type, + sizeof (auth_type)); + + (void) snprintf(srch, NIS_MAXNAMELEN, + "[auth_name=%s, auth_type=%s], cred.org_dir.%s", + netname, auth_type, rmtdomain); + + if (srch[strlen(srch) - 1] != '.') { + strcat(srch, "."); + } + } else { + syslog(LOG_ERR, +"nis_getprincipal: no alias was found for mechname '%s': returning 'nobody'", + rcred->mechanism); + return; + } + } else { + if (auth_verbose) { + syslog(LOG_INFO, + "nis_getprincipal: flavor = GSS: returning '%s'", + nobody); + } + return; + } + } else { + syslog(LOG_WARNING, + "nis_getprincipal: flavor = %d(unknown): returning '%s'", + flavor, nobody); + return; + } + + if (message) + if (flavor == AUTH_DES || flavor == RPCSEC_GSS) + syslog(LOG_INFO, + "nis_getprincipal: calling list with name '%s' and type '%s'", + netname, + flavor == AUTH_DES ? "DES" : auth_type); + else /* AUTH_SYS */ + syslog(LOG_INFO, + "nis_getprincipal: calling list with uid (LOCAL) '%d'", + au->aup_uid); + + res = nis_list(srch, NO_AUTHINFO+USE_DGRAM+FOLLOW_LINKS, NULL, NULL); + if (res->status != NIS_SUCCESS) { + if (message) + syslog(LOG_INFO, + "nis_getprincipal: error doing nis_list: %s", + nis_sperrno(res->status)); + } else { + if (strlcpy(name, ENTRY_VAL(res->objects.objects_val, 0), 1024) + >= 1024) { + strcpy(name, nobody); /* default is "nobody" */ + syslog(LOG_ERR, + "nis_getprincipal: buffer overflow, returning '%s'", nobody); + nis_freeresult(res); + return; + } + if (flavor == AUTH_DES || flavor == RPCSEC_GSS) { + if (message) + syslog(LOG_INFO, + "nis_getprincipal: caching '%s'/'%s'", + netname, name); + add_cred_item(netname, name, + res->objects.objects_val[0].zo_ttl+time(0)); + } + } + + nis_freeresult(res); + if (message) + syslog(LOG_INFO, + "nis_getprincipal: flavor = %s: returning : '%s'", + flavor2str(flavor), name); +} + +/* + * __can_do() + * This function returns true if the given principal has the right to + * do the requested function on the given object. It could be a define + * if that would save time. At the moment it is a function. + * NOTE: It recursively calls NIS by doing the lookup on the group if + * the conditional gets that far. + * + * N.B. If the principal passed is 'null' then we're recursing and don't + * need to check it. (we always let ourselves look at the data) + */ +int +__can_do(right, mask, obj, pr) + unsigned long right; /* The Access right we desire */ + unsigned long mask; /* The mask for World/Group/Owner */ + nis_object *obj; /* A pointer to the object */ + nis_name pr; /* Principal making the request */ +{ + if ((secure_level == 0) || (*pr == 0)) + return (1); + + return (NIS_NOBODY(mask, right) || + (NIS_WORLD(mask, right) && + (strcmp(pr, "nobody") != 0)) || + (NIS_OWNER(mask, right) && + (nis_dir_cmp(pr, obj->zo_owner) == SAME_NAME)) || + (NIS_GROUP(mask, right) && + (strlen(obj->zo_group) > (size_t)(1)) && + __do_ismember(pr, obj, nis_lookup))); +} + +/* BEGIN CSTYLED */ +/* + * Get an xdr buffer + */ +u_char * +__get_xdr_buf(size) + int size; /* Size in bytes of the buffer */ +{ +#ifdef local_buf +#undef local_buf +#endif +#define local_buf (__nis_get_tsd()->local_buf__get_xdr_buf) + + return ((u_char *)nis_get_static_storage(&local_buf, 1, size)); +} +/* END CSTYLED */ + +/* + * Get a string buffer + */ +char * +__get_string_buf(size) + int size; /* Size in bytes of the buffer */ +{ +#ifdef local_buf +#undef local_buf +#endif +#define local_buf (__nis_get_tsd()->local_buf__get_string_buf) + + return ((char *)nis_get_static_storage(&local_buf, 1, size)); +} + +/* + * get an array of entry columns + */ +entry_col * +__get_entry_col(n) + int n; /* required array size */ +{ +#ifdef local_buf +#undef local_buf +#endif +#define local_buf (__nis_get_tsd()->local_buf__get_entry_col) + + return ((entry_col *)nis_get_static_storage(&local_buf, + sizeof (entry_col), n)); +} + +/* + * get an array of table columns + */ +table_col * +__get_table_col(n) + int n; /* required array size */ +{ +#ifdef local_buf +#undef local_buf +#endif +#define local_buf (__nis_get_tsd()->local_buf__get_table_col) + + return ((table_col *)nis_get_static_storage(&local_buf, + sizeof (table_col), n)); +} + +/* + * get an array of nis attributes + */ +nis_attr * +__get_attrs(n) + int n; /* required array size */ +{ +#ifdef local_buf +#undef local_buf +#endif +#define local_buf (__nis_get_tsd()->local_buf__get_attrs) + + return ((nis_attr *)nis_get_static_storage(&local_buf, + sizeof (nis_attr), n)); +} + +/* + * Stability functions. These functions check to see if a transaction + * has been propogated to all replicas. + * + * nis_isstable() + * This function checks to see if an update has been propogated to all + * of the replicates for the given domain. If so, the log code will be + * free to delete it, otherwise we will continue to hold it until the + * replicate picks up the change. If we can't figure out if it is stable + * or not we return 0 and hold onto it. + */ +int +nis_isstable(le, first) + log_entry *le; + int first; +{ + int i; + struct directory_item *dd; + nis_object *dobj; + struct ticks t; + nis_name dirname; + nis_error err; + bool_t ret; + + + if (le->le_type == UPD_STAMP) + dirname = le->le_name; + else + dirname = nis_domain_of(le->le_name); + + /* + * get object for the domain, if this is the first time we're + * "visitin" this domain then get a fresh copy (NO_CACHE) + */ + if (first) + err = __directory_object(dirname, &t, NO_CACHE+1, &dobj); + else + err = __directory_object(dirname, &t, 1, &dobj); + + /* + * If we aren't the master any more, or the directory doesn't exist + * anymore we can toss this update. + */ + if ((err == NIS_NOTMASTER) || (err == NIS_NOTFOUND) || + (err == NIS_BADNAME)) + return (TRUE); + + /* + * If we encountered a problem trying to get the directory object + * we hold on to the update. + */ + if ((err != NIS_SUCCESS) && (err != NIS_S_SUCCESS)) + return (FALSE); + + /* + * If we look at the object and there is only one server then + * the update has by definition propogated. + */ + if (MAXSERVER(dobj) == 1) + return (TRUE); + + /* + * Otherwise we go ahead and check to see if it is stable. + * In the single-threaded case, the find_item will always work + * if the __directory_object() call worked. Not necessarily true + * in MT case, where other threads may do work in between + * __directory_object() and nis_find_item(). + */ + dd = __nis_find_item_mt(dirname, dl, 1, 0); + if (!dd) + return (TRUE); + ret = TRUE; + for (i = 1; i < MAXSERVER(dobj); i++) { + if (dd->dl_stamps[i] == 0) { + /* Fetch the time from the replica */ + dd->dl_stamps[i] = nis_cptime_msg(GETSERVER(dobj, i), + dd->dl_item.name, + FALSE, TRUE); + + if (! dd->dl_stamps[i]) { + dd->dl_stamps[i] = 1; + } + } + + if (dd->dl_stamps[i] < le->le_time) + ret = FALSE; + } + __nis_release_item((NIS_HASH_ITEM *)dd, dl, 1); + return (ret); +} + +/* + * add_fenceposts() + * + * This function will put "fenceposts" into the log for each directory we + * serve. This makes the "lastupdate()" function faster and is essential + * for directories that have been stable long enough that there are no + * delta's left. + */ +void +add_fenceposts() +{ + int i; + struct directory_item *dd; + + LOCK_LIST(dl, "add_fenceposts(dl)"); + for (dd = (struct directory_item *)dl->first; dd; + dd = (struct directory_item *)(dd->dl_item.nxt_item)) { + for (i = 0; i < MAXSERVER(dd->dl_obj); i++) { + if (nis_dir_cmp(GETSERVER(dd->dl_obj, i)->name, + nis_local_host()) == SAME_NAME) { + if (dd->dl_stamps[i] > + last_update(dd->dl_item.name)) { + make_stamp(dd->dl_item.name, + dd->dl_stamps[i]); + break; + } + } + } + } + ULOCK_LIST(dl, "add_fenceposts(dl)"); +} + +/* + * apply_update() + * + * This function applies a log entry that we've received from the master + * for a given domain to the log and the database. + */ +static nis_error +apply_update(le) + log_entry *le; +{ + nis_error status; + char *msg; + + if (cons) { + fprintf(cons, "update type %d, name %s.%s\n", le->le_type, + le->le_object.zo_name, le->le_object.zo_domain); + } + + switch (le->le_type) { + case ADD_NAME : + status = __db_add(le->le_name, &(le->le_object), 0); + msg = "Failed to add object "; + break; + case MOD_NAME_NEW : + status = __db_add(le->le_name, &(le->le_object), 1); + msg = "Failed to modify object "; + break; + + case REM_NAME : + status = __db_remove(le->le_name, &(le->le_object)); + msg = "Failed to remove object "; + break; + + /* For old modified objects, just add this to the log */ + case MOD_NAME_OLD : + status = NIS_SUCCESS; + break; + + case ADD_IBASE : + status = __db_addib_nosync(le->le_name, + le->le_attrs.le_attrs_len, + le->le_attrs.le_attrs_val, + &(le->le_object)); + msg = "Failed to add entry "; + break; + + case REM_IBASE : + status = __db_remib_nosync(le->le_name, + le->le_attrs.le_attrs_len, + le->le_attrs.le_attrs_val); + msg = "Failed to remove entry "; + break; + /* ignore these as they are both NOPs */ + case UPD_STAMP : + case LOG_NOP : + status = NIS_SUCCESS; + break; + + default : + status = NIS_SYSTEMERROR; + msg = "Illegal transaction type on "; + break; + } + if (status != NIS_SUCCESS) { + syslog(LOG_ERR, "apply_update : %s %s NIS+ STATUS : %s", + msg, __make_name(le), nis_sperrno(status)); + if (cons) + fprintf(cons, "apply_update : %s %s\n", + msg, __make_name(le)); + } + + return (status); +} + +extern table_obj tbl_prototype; + +/* + * clear_directory() + * + * This function will restores a directory to "virgin" status. This is + * accomplished by deleting any databases (tables) that are contained in + * that directory, then destroying and recreating the database for the + * directory. + */ +int +clear_directory(name) + nis_name name; +{ + nis_db_list_result *tables; + int i, n; + char namebuf[NIS_MAXNAMELEN+1]; + nis_error status; + + tables = db_list_flags(name, 0, NULL, FN_NOMANGLE+FN_NORAGS); + if (tables != NULL && tables->status == NIS_SUCCESS) { + for (i = 0; i < tables->numo; i++) { + n = snprintf(namebuf, sizeof (namebuf), "%s.%s", + tables->objs[i].o->zo_name, + tables->objs[i].o->zo_domain); + if (n < 1 || n > sizeof (namebuf)) { + syslog(LOG_WARNING, + "clear_directory: buffer (%d) insufficient for \"%s.%s\" (%d)", + sizeof (namebuf), + tables->objs[i].o->zo_name, + tables->objs[i].o->zo_domain, + strlen(tables->objs[i].o->zo_name)+ + strlen(tables->objs[i].o->zo_domain)+ + 2 /* dot and NUL */); + continue; + } + if (__type_of(tables->objs[i].o) == NIS_TABLE_OBJ) { + if ((status = db_destroy(namebuf)) != + NIS_SUCCESS) + syslog(LOG_WARNING, + "clear_directory: Could not destroy table \"%s\": %s.", + namebuf, nis_sperrno(status)); + } else { + /* Not a table, so just remove the object */ + status = __db_remove(namebuf, + tables->objs[i].o); + if (status != NIS_SUCCESS) + syslog(LOG_WARNING, + "clear_directory: Could not remove object \"%s\": %s", + namebuf, nis_sperrno(status)); + } + } + } else if (tables == NULL || tables->status != NIS_NOTFOUND) { + syslog(LOG_WARNING, + "clear_directory: could not get object list for \"%s\": %s", + name, (tables != NULL) ? nis_sperrno(tables->status) : + "<nil>"); + free_db_list(tables); /* (tables == NULL) OK */ + return (0); + } + /* + * else (tables->status == NIS_NOTFOUND), which is fine by us; + * someone already did our work. + */ + + free_db_list(tables); /* (tables == NULL) OK */ + if ((status = db_destroy(name)) != NIS_SUCCESS) { + syslog(LOG_WARNING, + "Could not destroy table %s: %s.", + name, nis_sperrno(status)); + /* XXX recover? */ + } + + /* Make a clean version. */ + if ((status = db_create(name, &tbl_prototype)) != NIS_SUCCESS) { + syslog(LOG_WARNING, + "Could not create table %s: %s.", + name, nis_sperrno(status)); + /* XXX recover? */ + } + + if (verbose) + syslog(LOG_INFO, "directory '%s' cleared.", name); + return (1); +} + +#define repl_stats (__nis_get_tsd()->repl_stats) + +/* + * nis_makename() + * + * This function prints out a nice name for a objects. + */ +void +nis_makename(obj, tobj, str) + nis_object *obj; + nis_object *tobj; + char *str; +{ + int i; + table_col *tcols; + entry_col *ecols; + int ncols; + + if (tobj == NULL) { + sprintf(str, "%s.%s", obj->zo_name, obj->zo_domain); + return; + } + + strcpy(str, "["); + tcols = tobj->TA_data.ta_cols.ta_cols_val; + ncols = tobj->TA_data.ta_cols.ta_cols_len; + ecols = obj->EN_data.en_cols.en_cols_val; + + for (i = 0; i < ncols; i++) { + if (tcols[i].tc_flags & TA_SEARCHABLE) { + strcat(str, tcols[i].tc_name); + strcat(str, " = \""); + if (ecols[i].ENLEN) + strncat(str, ecols[i].ENVAL, ecols[i].ENLEN); + strcat(str, "\", "); + } + } + + str[strlen(str) - 2] = '\0'; + strcat(str, "],"); + strcat(str, obj->zo_name); + strcat(str, "."); + if (strlen(obj->zo_domain)) { + strcat(str, obj->zo_domain); + } +} + +/* + * During full resyncs, repl_objs caches the table objects, so that + * we don't have to look them up every time we add an entry to + * the table. + */ +static NIS_HASH_TABLE repl_objs = NIS_HASH_TABLE_MT_INIT; + +/* This is the callback for nis_dump (Full Resyncs) */ +int +update_directory(name, obj, x) + nis_name name; + nis_object *obj; + void *x; +{ + nis_db_result *dbres; + nis_error status; + nis_object *tobj; + char namebuf[NIS_MAXNAMELEN+1]; + int i, na, mc; + entry_col *ec; + table_col *tc; + nis_attr *attr_list; + ping_item *pp; + int release_pp = 0; + + if (obj->zo_oid.mtime > repl_stats.utime) { + repl_stats.utime = obj->zo_oid.mtime; + } + + /* + * Note : + * Once entered this if block returns to the caller. + */ + if (__type_of(obj) == NIS_ENTRY_OBJ) { + pp = (ping_item *)nis_find_item(name, &repl_objs); + if (! pp) { + dbres = db_lookup_deferred(name, FALSE); + repl_stats.ticks += dbres->ticks; + if (dbres->status == NIS_SUCCESS) + add_pingitem(dbres->obj, 1, &repl_objs); + tobj = dbres->obj; + } else { + release_pp = 1; + tobj = pp->obj; + } + + if (tobj) { + if (verbose) { + nis_makename(obj, tobj, namebuf); + syslog(LOG_INFO, "update_dir: %s", namebuf); + } + /* Build a fully specified name from the entry */ + mc = tobj->TA_data.ta_cols.ta_cols_len; + tc = tobj->TA_data.ta_cols.ta_cols_val; + ec = obj->EN_data.en_cols.en_cols_val; + attr_list = __get_attrs(mc); + if (attr_list == NULL) { + syslog(LOG_WARNING, + "update_directory: out of memory resync aborted."); + repl_stats.errors++; + if (release_pp) + __nis_release_item( + (NIS_HASH_ITEM *)pp, &repl_objs, -1); + return (1); + } + for (i = 0, na = 0; i < mc; i++) { + if ((tc[i].tc_flags & TA_SEARCHABLE) != 0) { + attr_list[na].zattr_ndx = tc[i].tc_name; + attr_list[na].ZAVAL = ec[i].ENVAL; + attr_list[na].ZALEN = ec[i].ENLEN; + na++; + } + } + status = __db_addib_nolog(name, na, attr_list, obj); + if (status == NIS_SUCCESS) { + repl_stats.successes++; + if ((repl_stats.successes % 1000) == 0) + syslog(LOG_INFO, + "update_directory : %d objects, still running.", + repl_stats.successes); + if (release_pp) + __nis_release_item( + (NIS_HASH_ITEM *)pp, &repl_objs, -1); + return (0); + } else { + nis_makename(obj, tobj, namebuf); + syslog(LOG_ERR, + "replica_update (update_directory): adding %s returned %s", + namebuf, nis_sperrno(status)); + repl_stats.errors++; + if (release_pp) + __nis_release_item( + (NIS_HASH_ITEM *)pp, &repl_objs, -1); + return (1); + } + } else { + syslog(LOG_ERR, + "replica_update (update_directory): adding entry to %s returned %s", + name, dbres->status); + repl_stats.errors++; + if (release_pp) + __nis_release_item( + (NIS_HASH_ITEM *)pp, &repl_objs, -1); + return (1); + } + } + + /* + * At this point it isn't an ENTRY object so we're not adding + * it to a table. + */ + if (verbose) { + nis_makename(obj, NULL, namebuf); + syslog(LOG_INFO, "update_dir: %s", namebuf); + } + status = __db_add(name, obj, 0); + if (status == NIS_SUCCESS) { + repl_stats.successes++; + if (release_pp) + __nis_release_item((NIS_HASH_ITEM *)pp, &repl_objs, + -1); + return (0); + } else { + /* + * xxx sometimes clear_directory doesn't successfully + * clear out tables (bug), this tries again before + * giving up on the table object. + */ + if (__type_of(obj) == NIS_TABLE_OBJ) { + if ((status = db_destroy(name)) == NIS_SUCCESS) { + status = __db_add(name, obj, 0); + if (status == NIS_SUCCESS) { + repl_stats.successes++; + return (0); + } + } + } + repl_stats.errors++; + if (release_pp) + __nis_release_item((NIS_HASH_ITEM *)pp, &repl_objs, + -1); + return (1); + } +} + + +/* + * Make sure the directory exists, if it doesn't then we've + * missed a create somewhere or we were just added as a replica + * of the directory. + */ +int +verify_table_exists(nis_name name) +{ + nis_error status; + + status = db_find_table(name); + switch (status) { + case NIS_NOSUCHTABLE: + if ((status = db_create(name, &tbl_prototype)) != + NIS_SUCCESS) { + syslog(LOG_ERR, + "verify_table_exists: cannot create table for %s:%s.", + name, nis_sperrno(status)); + return (0); + } + break; + case NIS_SUCCESS: + break; + default: + syslog(LOG_ERR, + "verify_table_exists: unexpected database error for %s: %s.", + name, nis_sperrno(status)); + return (0); + } + + return (1); +} + +/* + * Update root object (creating it if not there before) or remove root object + * depending on whether we're still listed as a root replica. + * Affects root_server flag. + * Returns 1 if successful; 0 otherwise. + */ +int +root_replica_update() +{ + nis_object *obj; + nis_result *res = 0; + nis_name root_dir = __nis_rpc_domain(); + int readok; + char *myself = "root_replica_update"; + + /* If we're reading the root dir from LDAP, do nothing */ + if (getObjMapping(root_dir, 0, 1, &readok, 0) != 0 && readok) { + logmsg(MSG_NOTIMECHECK, +#ifdef NIS_LDAP_DEBUG + LOG_WARNING, +#else + LOG_INFO, +#endif /* NIS_LDAP_DEBUG */ + "%s: Mapping root dir object from LDAP", myself); + return (1); + } + + res = nis_lookup(root_dir, MASTER_ONLY); + if (res->status != NIS_SUCCESS) { + syslog(LOG_WARNING, +"root_replica_update: update failed '%s': could not fetch object from master.", + root_dir); + nis_freeresult(res); + return (0); /* failed, try again later. */ + } + + obj = res->objects.objects_val; + if (we_serve(&(obj->DI_data), 0)) { + if (verbose) + syslog(LOG_INFO, + "root_replica_update: updating '%s'", + root_dir); + if (!update_root_object(root_dir, obj)) { + nis_freeresult(res); + return (0); /* failed, try again later. */ + } + } else { + if (verbose) + syslog(LOG_INFO, + "root_replica_update: removing '%s'", + root_dir); + remove_root_object(root_dir, obj); + } + nis_freeresult(res); + return (1); +} + +/* + * Return 1 if all objects in the directory 'dirname' are read mapped + * from LDAP, 0 otherwise. For table objects, the definition of "all + * objects" includes the table object itself, as well as entried in + * the table. + * + * Most of the code to retrieve a directory listing and traverse same + * was borrowed from clear_directory(). + */ +static int +isAllMapped(nis_name dirname) { + nis_db_list_result *objs; + int i, n, ok; + char namebuf[NIS_MAXNAMELEN+1]; + nis_error status; + char *myself = "isAllMapped"; + + objs = db_list_flags(dirname, 0, 0, FN_NOMANGLE+FN_NORAGS); + if (objs == 0 || objs->status != NIS_SUCCESS) { + logmsg(MSG_NOTIMECHECK, +#ifdef NIS_LDAP_DEBUG + LOG_WARNING, +#else + LOG_INFO, +#endif /* NIS_LDAP_DEBUG */ + "%s: Unable to get directory list for \"%s\": %s", + myself, NIL(dirname), + objs == 0 ? "<NULL>" : nis_sperrno(objs->status)); + return (0); + } + + for (ok = 1, i = 0; ok && i < objs->numo; i++) { + int readok; + + n = snprintf(namebuf, sizeof (namebuf), "%s.%s", + objs->objs[i].o->zo_name, objs->objs[i].o->zo_domain); + if (n < 1 || n > sizeof (namebuf)) { + logmsg(MSG_NOTIMECHECK, +#ifdef NIS_LDAP_DEBUG + LOG_WARNING, +#else + LOG_INFO, +#endif /* NIS_LDAP_DEBUG */ + "%s: Buffer size (%d) insufficient for \"%s.%s\" (need %d)", + myself, sizeof (namebuf), + objs->objs[i].o->zo_name, + objs->objs[i].o->zo_domain, + strlen(objs->objs[i].o->zo_name) + + strlen(objs->objs[i].o->zo_domain) + 2); + ok = 0; + continue; + } + + if (getObjMapping(namebuf, 0, 1, &readok, 0) == 0 || + readok == 0) { + logmsg(MSG_NOTIMECHECK, +#ifdef NIS_LDAP_DEBUG + LOG_WARNING, +#else + LOG_INFO, +#endif /* NIS_LDAP_DEBUG */ + "%s: No read mapping for obj \"%s\"", + myself, namebuf); + ok = 0; + continue; + } + + if (__type_of(objs->objs[i].o) == NIS_TABLE_OBJ && + (getObjMapping(namebuf, 0, 0, &readok, 0) == 0 || + readok == 0)) { + logmsg(MSG_NOTIMECHECK, +#ifdef NIS_LDAP_DEBUG + LOG_WARNING, +#else + LOG_INFO, +#endif /* NIS_LDAP_DEBUG */ + "%s: No read mapping for entries in \"%s\"", + myself, namebuf); + ok = 0; + continue; + } + } + + free_db_list(objs); + + return (ok); +} + +/* + * Returns 1 if the log_entry:s 'l1' and 'l2' pertain to the same + * entry object, 0 otherwise. + * + * Since this function works on log entries alone, it may not + * correctly handle cases where different attribute selector lists + * resolve to the same entry. For example, it's quite possible that + * '[key1=abc]tab.org_dir' and '[key2=def]tab.org_dir' unambiguously + * specify the same entry, but we wouldn't know without looking + * in the DB (and even that wouldn't work if one of the operations + * is an ADD, in which case the entry migh tnot exist yet). + */ +static int +sameEntryObj(log_entry *l1, log_entry *l2) { + int i; + + if (l1 == 0 || l2 == 0) + return (0); + + /* Must both be entry objects */ + if (l1->le_object.zo_data.zo_type != l2->le_object.zo_data.zo_type) + return (0); + + if (l1->le_object.zo_data.zo_type != NIS_ENTRY_OBJ) + return (0); + + /* Same table name */ + if (l1->le_name == 0 || l2->le_name == 0 || + strcmp(l1->le_name, l2->le_name) != 0) + return (0); + + /* Same number of entry selector attributes */ + if (l1->le_attrs.le_attrs_len != l2->le_attrs.le_attrs_len) + return (0); + + /* + * Check the selector attribute names/values. For simplicity, + * we require that they're in the same order. + */ + for (i = 0; i < l1->le_attrs.le_attrs_len; i++) { + if (l1->le_attrs.le_attrs_val[i].zattr_ndx == 0 || + l2->le_attrs.le_attrs_val[i].zattr_ndx == 0 || + strcmp(l1->le_attrs.le_attrs_val[i].zattr_ndx, + l2->le_attrs.le_attrs_val[i].zattr_ndx) != 0) + return (0); + if (l1->le_attrs.le_attrs_val[i].zattr_val.zattr_val_len != + l2->le_attrs.le_attrs_val[i].zattr_val.zattr_val_len) + return (0); + if (memcmp(l1->le_attrs.le_attrs_val[i].zattr_val.zattr_val_val, + l2->le_attrs.le_attrs_val[i].zattr_val.zattr_val_val, + l1->le_attrs.le_attrs_val[i].zattr_val.zattr_val_len) != + 0) + return (0); + } + + return (1); +} + +/* define buffer size for ctime_r() */ +#define CTBUFSIZ 26 + +/* + * replica_update(); + * + * This function is used by the replicas to keep themselves up to date + * with the master server. When called, it iteratively removes the names + * of directories that have changed from it's list. + * + * Note: replica_update() is called either from main() before we go MT, + * of from check_updaters() with an exclusive lock on the 'upd_list'. + * Thus, there's no need to enforce exclusion in replica_update() + * itself. + */ +int +replica_update(upd) + ping_item *upd; +{ + nis_name name; /* Directory name */ + nis_server *srv; /* Endpoint for the server */ + log_result *lres; + log_entry *le; + log_upd *curr_upd = 0; + char ctbuf[CTBUFSIZ]; + ulong_t ttime; /* timestamp retrieved from last_update() */ + extern void __nis_freelogresult(log_result *); + + /* timestamp used to write back into log when full resync is done */ + ulong_t xttime; + int i, xid, nument, lstat; + ping_item *pp, *nxt; + db_status dbStatus = DB_SUCCESS; + __nisdb_retry_t dumpRetry = ldapConfig.dumpErrorTimeout; + /* If a full dump fails, is it unsafe to return (i.e., try again) ? */ + bool_t dumpReturnUnsafe = FALSE; + char *myself = "replica_update"; + + if (verbose) + syslog(LOG_INFO, "replica_update:"); + + memset((char *)(&repl_stats), 0, sizeof (repl_stats)); + + if (cons) + fprintf(cons, "replica_update: %s\n", upd->item.name); + name = upd->item.name; + + /* check to see if we're replicating the root object */ + if (root_object_p(name)) { + return (root_replica_update()); + } + + srv = GETSERVER(upd->obj, 0); /* master server for directory */ + + if (nis_dir_cmp(srv->name, nis_local_host()) == SAME_NAME) { + syslog(LOG_WARNING, +"host %s thinks that it is the replica for %s, but it is the master!", + nis_local_host(), name); + return (1); + } + + if (verbose) + syslog(LOG_INFO, "replica_update: updating '%s'", name); + + if (verify_table_exists(name) == 0) { + return (0); /* failed, try again later. */ + } + + /* + * If every object in the directory, including table entries, are + * mapped from LDAP, we just get the update time from the master, + * and write an UPDATE time stamp to trans.log. + */ + if (isAllMapped(name)) { + ttime = nis_cptime_msg(srv, name, FALSE, TRUE); + if (ttime != 0L && ttime != ULONG_MAX) { + make_stamp(name, ttime); + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "%s: All objects in \"%s\" mapped from LDAP; " + "setting update time to %s", + myself, name, ctime_r((time_t *)&ttime, ctbuf, + CTBUFSIZ)); + return (1); + } else { + logmsg(MSG_NOTIMECHECK, LOG_WARNING, + "%s: Unable to get update time for \"%s\" from \"%s\"", + myself, name, srv->name); + /* Fall through to normal resync */ + } + } + + /* + * We now intend do things that might want to update the trans.log. + * While the actual transactions are protected by begin_transaction()/ + * end_transaction(), we don't want to have some other thread changing + * the trans.log under our feet from this point on, so we acquire + * a write lock on the trans.log. + */ + lstat = lockTransLog(myself, 1, 0); + if (lstat != 0) + return (0); + + ttime = last_update(name); + /* + * If ttime is non-zero, then get the delta from the master using + * nis_dumplog(). + * If ttime is zero, then go directly to the full resync section. + * There is no need to even try to get the delta from the master. + */ + if (ttime != 0L && ttime != ULONG_MAX) { + /* get the delta from transaction log */ + if (cons) + fprintf(cons, + "replica_update: dumping master's log.\n"); + syslog(LOG_INFO, "replica_update: delta update of %s", name); + lres = nis_dumplog(srv, name, ttime); + if (lres == NULL) { + unlockTransLog(myself, 1); + return (0); + } + if ((lres->lr_status != NIS_SUCCESS) && cons) + fprintf(cons, "replica_update: error result was %s\n", + nis_sperrno(lres->lr_status)); + if (lres->lr_status == NIS_SYSTEMERROR) { + syslog(LOG_WARNING, + "replica_update: Couldn't contact '%s' serving '%s' for an update.", + srv->name, name); + __nis_freelogresult(lres); + unlockTransLog(myself, 1); + return (0); + } + + if (lres->lr_status == NIS_DUMPLATER) { + syslog(LOG_INFO, + "replica_update: master server is busy, will try later."); + __nis_freelogresult(lres); + unlockTransLog(myself, 1); + return (0); + } + + if (lres->lr_status == NIS_SUCCESS) { + int skipped = 0; + + if (verbose) + syslog(LOG_INFO, + "replica_update: %d updates from '%s'", + lres->lr_entries.lr_entries_len, srv->name); + if (cons) + fprintf(cons, + "replica_update: %d updates from '%s'\n", + lres->lr_entries.lr_entries_len, srv->name); + nument = lres->lr_entries.lr_entries_len; + if (nument == 0) { + syslog(LOG_WARNING, + "replica_update: master server returned NIS_SUCCESS, but 0 updates!"); + __nis_freelogresult(lres); + unlockTransLog(myself, 1); + return (1); + } + + le = lres->lr_entries.lr_entries_val; + + /* + * If there are objects in the directory that are mapped + * from LDAP, we intend to skip those during the resync. + * This means that the time stamp of the last update in + * trans.log after the resync isn't necessarily the same + * as the last update known to the master. Hence, we + * save the latter in 'xttime'. + */ + xttime = le[nument-1].le_time; + + /* invalidate this directory in case of crash */ + make_stamp(name, ULONG_MAX); + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "%s: Temporarily setting update time for " + "\"%s\" to 0x%lx", + myself, name, ULONG_MAX); + xid = begin_transaction(nis_local_principal()); + begin_update(); + dbStatus = nis_put_offline(name, FALSE); + if (dbStatus != DB_SUCCESS) { + syslog(LOG_ERR, + "replica_update: offline DB error %d for \"%s\"", + dbStatus, name); + } + for (i = 0; i < nument; i++) { + int shouldskip, readok, io, + doingModify = 0; + nis_error nerr; + /* + * Is the object mapped from LDAP ? If so, + * skip the update, but consider the + * application of the change a success. + */ + io = 0; + switch (le[i].le_object.zo_data.zo_type) { + case NIS_DIRECTORY_OBJ: + case NIS_GROUP_OBJ: + case NIS_TABLE_OBJ: + case NIS_LINK_OBJ: + io = 1; + /* Fall through to entry obj */ + case NIS_ENTRY_OBJ: + shouldskip = + (getObjMapping(le[i].le_name, 0, + io, &readok, 0) != 0 && + readok != 0); + break; + default: + shouldskip = 0; + break; + } + if (shouldskip) { + logmsg(MSG_NOTIMECHECK, +#ifdef NIS_LDAP_DEBUG + LOG_WARNING, +#else + LOG_INFO, +#endif /* NIS_LDAP_DEBUG */ + "%s: skipping LDAP-mapped entry for \"%s\"", + myself, NIL(le[i].le_name)); + skipped++; + repl_stats.successes++; + continue; + } + + /* + * If the current operation is an entry + * removal, and the next one is an add + * of the same entry, tell the DB that + * we're in a modify, so that it can + * merge the remove/add into a single + * LDAP update. + */ + if (le[i].le_type == REM_IBASE && + i < nument-1 && + le[i+1].le_type == ADD_IBASE && + sameEntryObj(&le[i], &le[i+1])) { + __nisdb_get_tsd()->doingModify = 1; + doingModify = 1; + } + + nerr = apply_update(&(le[i])); + + /* + * If we had informed the DB that this was + * part of a modify, and we've done the + * add (or the log update failed), turn + * off the modify indicaton. + */ + if (doingModify && + (le[i].le_type == ADD_IBASE || + nerr != NIS_SUCCESS)) { + __nisdb_get_tsd()->doingModify = 0; + doingModify = 0; + } + + if (nerr != NIS_SUCCESS) { + if (cons) + fprintf(cons, + "Failed to apply update.\n"); + syslog(LOG_ERR, + "replica_update: Unable to apply update."); + repl_stats.errors++; + break; + } + if (cons) + fprintf(cons, + "Add update #%d to trans.log\n", i+1); + if (add_update_nosync(&(le[i]), + (void **)&curr_upd) == 0) { + repl_stats.errors++; + break; + } else { + if (cons) + fprintf(cons, + "Added update to trans.log.\n"); + repl_stats.successes++; + } + } + if (repl_stats.errors == 0) { + if (cons) + fprintf(cons, + "Successfully updated.\n"); + end_update(); + dbStatus = nis_put_online(name, d_commit, + FALSE); + if (dbStatus != DB_SUCCESS) { + syslog(LOG_ERR, + "replica_update: online DB error %d for \"%s\"", + dbStatus, name); + } + nis_db_sync_log(); /* *.log */ + sync_log(); /* trans.log */ + end_transaction(xid); + if (curr_upd != 0) + ttime = curr_upd->lu_time; + else + ttime = 0; + if (!ttime && !skipped) + /* + * This should never happen, otherwise + * this can force the replica to go + * into full resync. + */ + syslog(LOG_INFO, + "replica_update: WARNING: last_update(%s) returned 0!", + name); + /* + * If we skipped updates because the object + * was mapped from LDAP, 'ttime' may not be + * valid, so we use the time from the master + * that we saved in 'xttime'. + */ + make_stamp(name, skipped ? xttime : ttime); + __nis_freelogresult(lres); + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "%s: Setting update time for \"%s\" to %s", + myself, name, + ctime_r(skipped ? (time_t *)&xttime : + (time_t *)&ttime, + ctbuf, CTBUFSIZ)); + unlockTransLog(myself, 1); + return (1); + } else { + if (cons) + fprintf(cons, "Aborted the update.\n"); + abort_transaction(xid); + dbStatus = nis_put_online(name, d_rollback, + FALSE); + if (dbStatus != DB_SUCCESS) { + syslog(LOG_ERR, + "replica_update: online DB error %d for \"%s\"", + dbStatus, name); + } + nis_db_sync_log(); /* *.log */ + sync_log(); /* trans.log */ + __nis_freelogresult(lres); + unlockTransLog(myself, 1); + return (0); + } + } + + if (lres->lr_status != NIS_RESYNC) { + syslog(LOG_WARNING, + "replica_update: nis_dumplog failed: srv='%s', dir='%s', err='%s'", + srv->name, name, nis_sperrno(lres->lr_status)); + if (cons) + fprintf(cons, + "replica_update: nis_dumplog failed: srv='%s', dir='%s', err='%s'\n", + srv->name, name, nis_sperrno(lres->lr_status)); + __nis_freelogresult(lres); + unlockTransLog(myself, 1); + return (0); + } + + __nis_freelogresult(lres); + } /* end of delta update */ + + /* + * Our log and the masters are sufficently out of date that we + * need to completely resync. + */ + if (verbose) { + if (cons) + fprintf(cons, "replica_update: Full dump required.\n"); + syslog(LOG_INFO, "replica_update: Full dump required."); + } + + /* + * The following code handles full dump (resync) from the + * master. Full dump only happens if the local timestamp + * returned from last_update(): + * 1) is 0 (zero), or + * 2) cannot be found in on the master. + */ + syslog(LOG_INFO, "replica_update: Full dump of %s", name); + + dbStatus = nis_put_offline(name, TRUE); + if (dbStatus != DB_SUCCESS) { + syslog(LOG_ERR, + "replica_update: offline DB error %d for \"%s\"", + dbStatus, name); + /* Haven't actually done anything, so return failure */ + unlockTransLog(myself, 1); + return (0); + } + + /* clear_directory(name); */ /* we might serve stale data */ + /* + * Note we *don't* transact this because if we fail we'll simply + * restart from the beginning. However, we hang on to the trans.log + * lock in order to prevent other threads from performing updates. + */ +try_again: + __nis_init_hash_table(&repl_objs, 0); + __nis_destroy_callback(); + /* + * The case where all objects in the directory were mapped from LDAP + * was already handled above. If we get here, it may be that _some_ + * of the objects are mapped from LDAP, but we also know that we're + * out-of-sync with the master for at least one object. + * + * The usual reason that we get to this point is that this is a + * newly created replica, and we'd probably like to build up its + * disk database ASAP. For this reason, and in the interest of + * simplicity (don't want to make libnsl, where nis_dump() lives, + * aware of LDAP mapping), we let the full dump get everything + * from the master, whether or not it's mapped from LDAP. + */ + lres = nis_dump(srv, name, update_directory); + if (lres) { + syslog(LOG_INFO, "replica_update: nis_dump result %s", + nis_sperrno(lres->lr_status)); + syslog(LOG_INFO, "replica_update: %d updates, %d errors.", + repl_stats.successes, repl_stats.errors); + } + /* + * Free up the cache of table objects + */ + LOCK_LIST(&repl_objs, "replica_update(repl_objs)"); + for (pp = (ping_item *)(repl_objs.first); pp; pp = nxt) { + nxt = (ping_item *)(pp->item.nxt_item); + if (!checkpoint_table(pp->item.name)) { + /* + * If lres indicates success, mark the error, + * but continue so that repl_objs will be + * cleaned up. The status in lres will + * be checked below. + */ + if (lres->lr_status == NIS_CBRESULTS) + lres->lr_status = NIS_FAIL; + } + (void) nis_remove_item(pp->item.name, &repl_objs); + XFREE(pp->item.name); + nis_destroy_object(pp->obj); + XFREE(pp); + } + ULOCK_LIST(&repl_objs, "replica_update(repl_objs)"); + + if (lres == NULL || lres->lr_status == NIS_DUMPLATER) { + if (lres) + syslog(LOG_INFO, + "replica_update: master server busy, rescheduling the resync."); + if (ttime != 0L) { + ttime = 0; + make_stamp(name, ttime); + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "%s: Setting update time for \"%s\" to 0x%lx", + myself, name, ttime); + } + /* + * Note, we are not addressing the case when several replicas + * are initialized simultaneously i.e several nismkdir -s + * are executed before a nisping. + */ + dbStatus = nis_put_online(name, d_rollback, TRUE); + if (dbStatus != DB_SUCCESS) { + syslog(LOG_ERR, + "replica_update: online DB error %d for \"%s\"", + dbStatus, name); + } + __nis_freelogresult(lres); + + if (__nis_retry_sleep(&dumpRetry, + dumpReturnUnsafe || (dbStatus != DB_SUCCESS))) { + syslog(LOG_INFO, + "replica_update: trying a full resync again"); + goto try_again; + } else { + /* + * Returning zero makes sure that this directory is not + * deleted from the upd_list. We'll try again after our + * poll times out. + */ + unlockTransLog(myself, 1); + return (0); + } + } + + if (lres->lr_status == NIS_RPCERROR) { /* callback _setup_ failed */ + if (ttime != 0L) { + ttime = 0; + make_stamp(name, ttime); + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "%s: Setting update time for \"%s\" to 0x%lx", + myself, name, ttime); + } + /* + * Put back online, since the directory would not have been + * cleared. + */ + dbStatus = nis_put_online(name, d_rollback, TRUE); + if (dbStatus != DB_SUCCESS) { + syslog(LOG_ERR, + "replica_update: online DB error %d for \"%s\"", + dbStatus, name); + } + __nis_freelogresult(lres); + + if (__nis_retry_sleep(&dumpRetry, + dumpReturnUnsafe || (dbStatus != DB_SUCCESS))) { + syslog(LOG_INFO, + "replica_update: trying a full resync again"); + goto try_again; + } else { + unlockTransLog(myself, 1); + return (0); /* don't delete from upd_list */ + } + + } else if (lres->lr_status == NIS_NOTFOUND) { /* No longer on master */ + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "%s: nis_dump failed: srv='%s', dir='%s', " + "No longer served by master", myself, srv->name, name); + if (ttime != 0L) { + ttime = 0; + make_stamp(name, ttime); + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "%s: Setting update time for \"%s\" to 0x%lx", + myself, name, ttime); + } + /* + * Put back online, nothing more we can do + */ + dbStatus = nis_put_online(name, d_rollback, TRUE); + if (dbStatus != DB_SUCCESS) { + logmsg(MSG_NOTIMECHECK, LOG_ERR, + "replica_update: online DB error %d for \"%s\"", + myself, dbStatus, name); + } + __nis_freelogresult(lres); + unlockTransLog(myself, 1); + return (1); /* do delete from upd_list */ + + } else if (lres->lr_status != NIS_CBRESULTS) { /* CB itself failed */ + syslog(LOG_WARNING, + "replica_update: nis_dump failed: srv='%s', dir='%s', err='%s'", + srv->name, name, nis_sperrno(lres->lr_status)); + if (ttime != 0L) { + ttime = 0; + make_stamp(name, ttime); + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "%s: Setting update time for \"%s\" to 0x%lx", + myself, name, ttime); + } + __nis_freelogresult(lres); + + if (ldapConfig.dumpError == rollback) { + dbStatus = nis_put_online(name, d_rollback, TRUE); + if (dbStatus != DB_SUCCESS) { + syslog(LOG_ERR, + "replica_update: online DB error %d for \"%s\"", + dbStatus, name); + } + } + + dumpReturnUnsafe = TRUE; + + /* + * Wait before retrying, abort dump if we've used up the + * allowed number of attempts. However, if rollback failed, + * we disregard 'dumpRetry.attempts' and always continue + * trying (since the data may not only be stale, but + * inconsistent or corrupted). + */ + if (!__nis_retry_sleep(&dumpRetry, + (dbStatus != DB_SUCCESS))) { + syslog(LOG_WARNING, + "replica_update: Used %d dump attempts for \"%s\", serving stale data", + ldapConfig.dumpErrorTimeout.attempts, name); + unlockTransLog(myself, 1); + return (0); + } + + syslog(LOG_INFO, "replica_update: trying a full resync again."); + + if (ldapConfig.dumpError == rollback && + dbStatus == DB_SUCCESS) { + dbStatus = nis_put_offline(name, TRUE); + if (dbStatus != DB_SUCCESS) { + syslog(LOG_ERR, + "replica_update: offline DB error %d for \"%s\"", + dbStatus, name); + } + } + + goto try_again; + } else if (repl_stats.errors) { + syslog(LOG_WARNING, + "replica_update: errors during resync : srv='%s', dir='%s'", + srv->name, name); + xttime = 0; + __nis_freelogresult(lres); + if (ttime != 0L) { + ttime = 0; + make_stamp(name, ttime); + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "%s: Setting update time for \"%s\" to 0x%lx", + myself, name, ttime); + } + + if (ldapConfig.dumpError == rollback) { + dbStatus = nis_put_online(name, d_rollback, TRUE); + if (dbStatus != DB_SUCCESS) { + syslog(LOG_ERR, + "replica_update: online DB error %d for \"%s\"", + dbStatus, name); + } + } + + dumpReturnUnsafe = TRUE; + + /* + * Wait before retrying, abort dump if we've used up the + * allowed number of attempts. However, if rollback failed, + * we disregard 'dumpRetry.attempts' and always continue + * trying (since the data may not only be stale, but + * inconsistent or corrupted). + */ + if (!__nis_retry_sleep(&dumpRetry, + (dbStatus != DB_SUCCESS))) { + syslog(LOG_WARNING, + "replica_update: Used %d dump attempts for \"%s\", serving stale data", + ldapConfig.dumpErrorTimeout.attempts, name); + unlockTransLog(myself, 1); + return (0); + } + + syslog(LOG_INFO, "replica_update: trying a full resync again."); + + if (ldapConfig.dumpError == rollback && + dbStatus == DB_SUCCESS) { + dbStatus = nis_put_offline(name, TRUE); + if (dbStatus != DB_SUCCESS) { + syslog(LOG_ERR, + "replica_update: offline DB error %d for \"%s\"", + dbStatus, name); + } + } + + goto try_again; + } + /* + * Mark the now resync'd directory as stable. + * This is done by comparing the latest timestamp of + * all the objects we've seen with the latest timestamp + * from the master. This is required because the master + * stamps "remove" operations but they won't show up + * on a full resync. + */ + if (lres->lr_entries.lr_entries_len) { + xttime = lres->lr_entries.lr_entries_val[0].le_time; + } else { + syslog(LOG_INFO, +"replica_update: downrev version of NIS+ service serving dir %s as master.", + name); + xttime = nis_cptime_msg(srv, name, FALSE, TRUE); + xttime = (xttime < repl_stats.utime) ? + repl_stats.utime : xttime; + } + if (cons) + fprintf(cons, "Latest update was %s", + ctime_r((time_t *)&repl_stats.utime, ctbuf, + CTBUFSIZ)); + if (verbose) + syslog(LOG_INFO, + "replica_update: directory %s updated", name); + + /* Add it to the list of directories that it serves */ + nis_server_control(SERVING_LIST, DIR_ADD, name); + __nis_freelogresult(lres); + dbStatus = nis_put_online(name, d_commit, TRUE); + if (dbStatus != NIS_SUCCESS) { + syslog(LOG_ERR, + "replica_update: online DB error %d for \"%s\"", + dbStatus, name); + } + + /* + * The full resync is now complete. We need to put the + * current timestamp for the directory in the local + * transaction log. + * NOTE: variable "xttime" contains the valid timestamp + * for the directory. It should never be 0. However, + * if we do get this, we'll have to assume that there + * were some problem and we should force another resync. + */ + if ((xttime != 0L) || ((xttime == 0L) && (ttime != 0L))) { + /* + * Always stamp with valid timestamp. + * We only stamp with timestamp of 0 if there + * isn't already a timestamp of 0 in the + * transaction log. + */ + make_stamp(name, xttime); + logmsg(MSG_NOTIMECHECK, LOG_INFO, + "%s: Setting update time for \"%s\" after " + "full resync to %s", + myself, name, + ctime_r((time_t *)&xttime, ctbuf, CTBUFSIZ)); + } + if (xttime == 0L) + syslog(LOG_DEBUG, + "replica_update: timestamp=0 after full resync completed!"); + unlockTransLog(myself, 1); + return (1); +} + +static void +ping_replicas_thread(t_pung) + ping_item *t_pung; +{ + + if (verbose) + syslog(LOG_INFO, "ping_replicas: Pinging %s", + t_pung->item.name); + + nis_ping(t_pung->item.name, t_pung->mtime, t_pung->obj); + + if (t_pung->obj) + nis_destroy_object(t_pung->obj); + + if (verbose) + syslog(LOG_INFO, "ping_replicas_thread: directory %s time %ld", + t_pung->item.name, t_pung->mtime); + + XFREE(t_pung->item.name); + XFREE(t_pung); + + thr_exit(0); /* thread simply exits */ +} + + +/* + * ping_replicas() + * + * This function will send a "ping" to the replicas for a given directory + * indicating that they should be ready to get updates to the database. + * + * It creates a thread to send the ping. + * NOTE: The main thread destroys the ping_item "pung" when + * ping_replicas() returns. To avoid problems, the + * ping_item is first cloned. The cloned ping_item + * must be destroyed in the thread. + * + * Once the thr_create is successful the operation is presumed to complete. + * + */ +int +ping_replicas(pung) + ping_item *pung; +{ + thread_t tid; + sigset_t new_mask, orig_mask; + int error; + + if (sigemptyset(&new_mask) != 0) { + if (verbose) + syslog(LOG_ERR, + "ping_replicas: Error (%d) zeroing mask for %s", + errno, pung->item.name); + return (FALSE); + } + if (thr_sigsetmask(SIG_SETMASK, &new_mask, &orig_mask) != 0) { + if (verbose) + syslog(LOG_ERR, + "ping_replicas: Error (%d) setting mask for %s", + errno, pung->item.name); + return (FALSE); + } + error = thr_create(NULL, 0, + (void *(*)(void *))ping_replicas_thread, (void *)pung, + THR_DETACHED, &tid); + if (thr_sigsetmask(SIG_SETMASK, &orig_mask, NULL) != 0) { + if (verbose) + syslog(LOG_ERR, + "ping_replicas: Error (%d) restoring mask for %s", + errno, pung->item.name); + } + if (error) { + if (verbose) + syslog(LOG_ERR, + "ping_replicas: Error (%d) creating ping thread for %s", + error, pung->item.name); + return (FALSE); + } + if (tid) { + if (verbose) + syslog(LOG_INFO, + "ping_replicas: Created ping thread %d for %s", + tid, pung->item.name); + return (TRUE); + } else + return (FALSE); +} + +/* + * Recursion Safe versions of the lookup internals. + * + * These functions are the internal functions that are used by the + * lookup and list functions in the library. When the server is linked + * against libnsl, these functions replace those in the library and make + * it safe for use to call nis_lookup() or nis_list(). + */ + +/* + * nis_local_lookup() + * + * Lookup the requested information by calling the services listsvc or + * lookup_svc entry points. + */ +nis_result * +__nis_local_lookup(req, flags, list_op, cbdata, cback) + ib_request *req; /* name parameters */ + u_long flags; /* user flags */ + int list_op; /* list semantics */ + void *cbdata; /* Callback data */ + int (*cback)(); /* Callback (for list calls) */ +{ + ns_request nsr; + nis_result *res; + nis_result *local_res; + int i; + + if (verbose) + syslog(LOG_INFO, "nis_local_lookup: making local call."); + + res = XCALLOC(1, sizeof (nis_result)); + if (res == NULL) + return (NULL); + + /* + * Depending on name or list_op either + * list it or look it up in the namespace. + */ + if ((req->ibr_srch.ibr_srch_len > 0) || (list_op)) { + if (verbose) + syslog(LOG_INFO, "local LIST"); + local_res = nis_iblist_svc(req, NULL); + } else { + char *name = req->ibr_name; + size_t namelen = 0; + + if (name) + namelen = strlen(name); + + /* sanity check on object name */ + if (namelen == 0 || name[namelen-1] != '.') { + if (verbose) + syslog(LOG_INFO, + "nis_local_lookup: bad name '%s'", + namelen ? name : "<NULL or empty>"); + res->status = NIS_BADNAME; + return (res); + } + + if (verbose) + syslog(LOG_INFO, "local LOOKUP"); + nsr.ns_name = req->ibr_name; + nsr.ns_object.ns_object_len = 0; + nsr.ns_object.ns_object_val = NULL; + local_res = nis_lookup_svc(&nsr, NULL); + } + + /* + * Now duplicate the result for the client so that the + * client can call nis_freeresult() with impunity. + */ + *res = *local_res; + if (res->objects.objects_len) { + res->objects.objects_val = (nis_object *) + calloc(res->objects.objects_len, + sizeof (nis_object)); + for (i = 0; i < res->objects.objects_len; i++) { + (void) nis_clone_object( + local_res->objects.objects_val+i, + res->objects.objects_val+i); + } + } + if (res->cookie.n_len) { + res->cookie.n_bytes = (char *) + malloc(res->cookie.n_len); + memcpy(res->cookie.n_bytes, local_res->cookie.n_bytes, + local_res->cookie.n_len); + } + return (res); +} + +/* + * we_serve() + * + * return TRUE if an RPC call to this directory might call us + * back. + */ +int +we_serve(srv, flags) + directory_obj *srv; + u_long flags; +{ + nis_server *servers; + int ns, i; + + if (flags & MASTER_ONLY) + ns = 1; + else + ns = srv->do_servers.do_servers_len; + servers = srv->do_servers.do_servers_val; + /* + * XXX NB: if the server name doesn't match but the + * address does, then we'll miss this check and recurse + * anyway. Sigh. + */ + for (i = 0; i < ns; i++) + if (nis_dir_cmp(servers[i].name, nis_local_host()) + == SAME_NAME) + return (1); + return (0); +} + +/* + * nis_core_lookup() + * + * The bones of the lookup and list function, this function binds to, then + * calls the appropriate NIS+ server for the given name. If the HARD_LOOKUP + * flag is set it is ignored because the server cannot afford to block. + * + * NB: This function now follows links if flags is set up correctly. THis + * localized this policy to this function and eliminated about 4 + * implementations of the same code in other modules. + */ +nis_result * +__nis_core_lookup(req, flags, list_op, cbdata, cback) + ib_request *req; /* name parameters */ + u_long flags; /* user flags */ + int list_op; /* list semantics */ + void *cbdata; /* Callback data */ + int (*cback)(); /* Callback (for list calls) */ +{ + int parent_first; + nis_error err; + nis_result *res = NULL; + directory_obj *dir; + nis_bound_directory *binding; + + if (verbose) + syslog(LOG_INFO, "__nis_core_lookup: (safe) called on %s", + req->ibr_name); + /* + * AS THE SERVICE, we don't support callbacks + */ + if (cbdata || cback) { + return (nis_make_error(NIS_NOCALLBACK, 0, 0, 0, 0)); + } + + if (list_op && req->ibr_srch.ibr_srch_len == 0) + parent_first = 0; + else + parent_first = 1; + + flags &= ~HARD_LOOKUP; /* server doesn't do HARD_LOOKUP */ + err = nis_bind_dir(req->ibr_name, parent_first, &binding, flags); + if (err != NIS_SUCCESS) { + return (nis_make_error(err, 0, 0, 0, 0)); + } + + dir = &binding->dobj; + + /* + * special handling for root replicas. + * + * If the following conditions are met : + * 1) the directory we're going to query == our domain name + * 2) we allegedly serve it. + * 3) we don't have the "root_server" flag set + * Then we are a root_replica and we need to shunt this + * request to the master root server who will read the + * root object and return it to us. + */ + if ((nis_dir_cmp(dir->do_name, __nis_rpc_domain()) == SAME_NAME) && + ! root_server && we_serve(dir, flags)) { + flags |= MASTER_ONLY; + if (we_serve(dir, flags)) { + nis_free_binding(binding); + return (nis_make_error(NIS_FAIL, 0, 0, 0, 0)); + } + } + + /* + * As a server, we may serve the indicated directory, if we + * do then we *DON'T* do an RPC, rather we just call our + * selves. Note this *can* recurse. + */ + if (we_serve(dir, flags)) { + res = __nis_local_lookup(req, flags, list_op, cbdata, cback); + if (res && res->status == NIS_NOSUCHTABLE && + !we_serve(dir, MASTER_ONLY)) { + /* + * We supposedly serve the directory, but we don't + * have a database for it (or the table), yet. + * Try the master for now, until we get the + * directory back in sync. Of course, we shouldn't + * do this if we are the master. + */ + nis_freeresult(res); + res = __nis_remote_lookup(req, flags | MASTER_ONLY, + list_op, cbdata, cback); + } + } else { + res = __nis_remote_lookup(req, flags, list_op, cbdata, cback); + } + + nis_free_binding(binding); + + return (res); +} + +/* + * __nis_finddirectory() + * + * This function will ask one of the servers of the given directory + * where some unknown directory "name" is. This function is called + * from within the __nis_CacheBind() code so is generally not needed by + * the client. If the directory cannot be found it returns NULL. + */ +fd_result * +__nis_finddirectory(binding, name) + nis_bound_directory **binding; + nis_name name; +{ + directory_obj *slist; + fd_result *res; + fd_result *svc_res; + fd_args argp; + int i, ns; + nis_server *srvlist; + enum name_pos p; + + slist = &(*binding)->dobj; + if (verbose) + syslog(LOG_INFO, "nis_finddirectory (safe) : called."); + memset((char *)&argp, 0, sizeof (argp)); + res = (fd_result *) XCALLOC(1, sizeof (fd_result)); + argp.dir_name = name; + argp.requester = nis_local_host(); + p = nis_dir_cmp(name, __nis_rpc_domain()); + + if (root_server && p == HIGHER_NAME) { + res->status = NIS_NOTFOUND; + return (res); + } + + ns = slist->do_servers.do_servers_len; + /* + * special case the root replica servers. + */ + if (we_serve(slist, 0) && (p != LOWER_NAME) && ! root_server) + ns = 1; /* effectively "master only" */ + + srvlist = slist->do_servers.do_servers_val; + + /* + * If this server is on the list and we have a database for it, + * just call the svc routine + */ + for (i = 0; i < ns; i++) { + if (nis_dir_cmp(srvlist[i].name, argp.requester) == SAME_NAME) { + /* Make sure we've got a database for it. */ + nis_error tab_status = db_find_table(slist->do_name); + if (tab_status == NIS_NOSUCHTABLE) { + if (we_serve(slist, MASTER_ONLY)) + tab_status = NIS_SYSTEMERROR; + else + tab_status = NIS_NOT_ME; + } + + switch (tab_status) { + case NIS_SUCCESS: + svc_res = nis_finddirectory_svc(&argp, NULL); + /* + * Note, we "clone" the find directory result + * because the cache client code will free it. + */ + if (svc_res && dup_fdres(svc_res, res)) { + return (res); + } + return (NULL); + + case NIS_NOT_ME: + /* not master */ + free((void *)res); + goto try_srvlist; + + default: + /* _fd_res puts returned obj on rags list */ + svc_res = __fd_res(argp.requester, tab_status, + NULL); + /* clone fd_result for cache to free */ + if (svc_res && dup_fdres(svc_res, res)) + return (res); + return (NULL); + } + } + } + + /* Make an RPC call to locate the directory. */ + +try_srvlist: + res = __nis_finddirectory_remote(binding, name); + return (res); +} + +int +__nis_host_is_server(nis_server *srv, int nsrv) +{ + int i; + char *name = nis_local_host(); + + for (i = 0; i < nsrv; i++) { + if (nis_dir_cmp(srv[i].name, name) == SAME_NAME) + return (1); + } + return (0); +} + +fd_result * +dup_fdres(o, n) + fd_result *o; /* The result to duplicate */ + fd_result *n; /* Where to put the data */ +{ + +#ifdef DEBUG + if (verbose) + syslog(LOG_INFO, "dup_fdres: cloning the FD result."); +#endif + + *n = *o; + + if (o->source) + n->source = strdup(o->source); + + if (o->dir_data.dir_data_val) { + n->dir_data.dir_data_val = (char *) + malloc(o->dir_data.dir_data_len); + if (! n->dir_data.dir_data_val) { + syslog(LOG_ERR, "dup_fdres: Out of memory!"); + return (NULL); + } + memcpy(n->dir_data.dir_data_val, o->dir_data.dir_data_val, + o->dir_data.dir_data_len); + } + if (o->signature.signature_val) { + n->signature.signature_val = (char *) + malloc(o->signature.signature_len); + if (! n->signature.signature_val) { + syslog(LOG_ERR, "dup_fdres: Out of memory!"); + if (n->dir_data.dir_data_val) + free(n->dir_data.dir_data_val); + return (NULL); + } + memcpy(n->signature.signature_val, o->signature.signature_val, + o->signature.signature_len); + } + return (n); +} + +DECLRWLOCK(root_object); + +int +update_root_object(nis_name root_dir, nis_object *d_obj) +{ + WLOCK(root_object); + if (nis_write_obj(nis_data(ROOT_OBJ), d_obj)) { + writeColdStartFile(&(d_obj->DI_data)); + root_server = 1; + WULOCK(root_object); + __nis_CacheRestart(); + flush_local_dircache(root_dir); + return (1); + } else { + WULOCK(root_object); + return (0); + } +} + +nis_object* +get_root_object() +{ + /* + * XXX Maybe this can be cached and updated whenever + * update_root_object is called instead of being reread ??? + */ + nis_object* d_obj; + + RLOCK(root_object); + d_obj = nis_read_obj(nis_data(ROOT_OBJ)); + RULOCK(root_object); + return (d_obj); +} + +int +root_object_p(nis_name name) +{ + return (strcmp(name, ROOT_OBJ) == 0); +} + +int +remove_root_object(nis_name root_dir, nis_object* d_obj) +{ + WLOCK(root_object); + unlink(nis_data(ROOT_OBJ)); + writeColdStartFile(&(d_obj->DI_data)); + root_server = 0; + WULOCK(root_object); + __nis_CacheRestart(); + flush_local_dircache(root_dir); + return (1); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_thread_funcs.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_thread_funcs.c new file mode 100644 index 0000000000..1b42c20009 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_thread_funcs.c @@ -0,0 +1,619 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <syslog.h> +#include <errno.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <dirent.h> +#include <string.h> +#include <rpcsvc/nis.h> +#include "nis_proc.h" +#include "nis_svc.h" + +uint_t next_refresh; /* next time to re-load dot file */ + +/* Track last time we started processing a request */ +static struct timeval last_activity = {0, 0}; + +/* Max interval between checking number of open fd:s */ +#define FD_CHECK_DEF (5 * 60) /* Five minutes */ + +/* + * Purge since now minus delta depending on number of open fd:s, in seconds. + * + * The array values were chosen to do the following: + * + * Behave like the old code (7200 seconds purge delta) for a very small + * number of open fd:s + * + * Give reasonably smooth purge behavior for realistic fd growth rates, + * as observed at customer site. + * + * Become aggressive about purging at high numbers of open fd:s, to + * reduce the risk of running out of fd:s. + */ +static int purge_deltas[] = { + 7200, 6900, 6600, 6300, 6000, 5400, 4800, 4200, + 3600, 3000, 1800, 1200, 600, 300, 75, 60 +}; + + +/* ARGSUSED1 */ +static int +do_count(void *countp, int fd) { + (*(int *)countp)++; + return (0); +} + +static long +fd_open_count(void) { + int count = 0; + + (void) fdwalk(do_count, &count); + return (count); +} + +/* servloop_sleep and servloop_wake are protected by the same mutex */ +static pthread_cond_t servloop_sleep = PTHREAD_COND_INITIALIZER; +static time_t servloop_wake = 0; +static DECLMUTEXLOCK(servloop_sleep); + +int +msleep(ulong_t sleeptime) { + struct timespec ts; + struct timeval tv; + int ret; + + (void) gettimeofday(&tv, 0); + ts.tv_sec = tv.tv_sec + sleeptime/1000; + ts.tv_nsec = 1000*(tv.tv_usec+1000*(sleeptime%1000)); + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ts.tv_sec += 1; + } + + MUTEXLOCK(servloop_sleep, "servloop:msleep lock"); + if (servloop_wake != 0) { + /* If we're past the servloop_wake time, reset and return */ + if (servloop_wake <= tv.tv_sec) { + servloop_wake = 0; + MUTEXUNLOCK(servloop_sleep, "servloop:msleep unlock"); + return (0); + } + /* servloop_wake may limit our sleep */ + if (servloop_wake <= ts.tv_sec) { + ts.tv_sec = servloop_wake; + ts.tv_nsec = 0; + } + } + ret = pthread_cond_timedwait(&servloop_sleep, + &servloop_sleep_pmutex, &ts); + /* Reset servloop_wake if necessary */ + if (servloop_wake != 0) { + (void) gettimeofday(&tv, 0); + if (servloop_wake <= tv.tv_sec) + servloop_wake = 0; + } + MUTEXUNLOCK(servloop_sleep, "servloop:msleep unlock"); + + return (ret); +} + +void +wakeup_servloop(void) { + (void) pthread_cond_signal(&servloop_sleep); +} + +/* + * Return the update batching timeout. + */ +time_t +updateBatchingTimeout(void) { + + switch (ldapConfig.updateBatching) { + case upd_none: + return (0); + case accumulate: + case bounded_accumulate: + return (ldapConfig.updateBatchingTimeout.timeout); + default: + return (DIR_IDLE_TIME); + } + /* NOTREACHED */ +} + +/* + * Establish the time when the servloop next should wake up to check + * if replicas should be pinged. If we change the wake up time, we + * always wake up the servloop so that it will take the new wakeup time + * into account when it goes back to sleep. + */ +void +setPingWakeup(time_t when) { + int doWakeup = 0; + + MUTEXLOCK(servloop_sleep, "servloop:set_ping_wakeup lock"); + if (when < servloop_wake || servloop_wake == 0) { + servloop_wake = when; + doWakeup = 1; + } + MUTEXUNLOCK(servloop_sleep, "servloop:set_ping_wakeup unlock"); + + if (doWakeup) + wakeup_servloop(); +} + +void +mark_activity(void) { + + static DECLMUTEXLOCK(last_activity); + + MUTEXLOCK(last_activity, "mark_activity(last_activity)"); + (void) gettimeofday(&last_activity, 0); + MUTEXUNLOCK(last_activity, "mark_activity(last_activity)"); +} + +void * +servloop(void *varg) { + + long dtbsize; + long last_purge = 0; + long fd_check = 0; + long fd_check_interval = FD_CHECK_DEF; + long purge_delta = purge_deltas[ + (sizeof (purge_deltas)/sizeof (purge_deltas[0])) - 1]; + long prev_since; + long cur_open_fd; + struct rlimit rl; + struct timeval now; + ulong_t sleeptime = 120*1000; + + /* + * get the maximum number of file descriptors for poll + */ + getrlimit(RLIMIT_NOFILE, &rl); + dtbsize = rl.rlim_cur; + + gettimeofday(&now, NULL); + last_purge = prev_since = now.tv_sec; + + for (;;) { + + /* + * Check open fd:s if more than 'fd_check_interval' seconds + * since last time. + * + * XXX: The somewhat complicated code below sets the + * purge_delta depending on the number of open fd:s, + * using linear interpolation in the purge_deltas[] + * array. + */ + gettimeofday(&now, NULL); + if (now.tv_sec - fd_check >= fd_check_interval) { + long index; + int deltas = (sizeof (purge_deltas)/ + sizeof (purge_deltas[0])); + cur_open_fd = fd_open_count(); + fd_check = now.tv_sec; + index = (cur_open_fd-1) * deltas / dtbsize; + if (index < 0) { + purge_delta = purge_deltas[0]; + } else if (index < deltas-1) { + /* Interpolate */ + purge_delta = purge_deltas[index] + + ((cur_open_fd - index*(dtbsize/deltas)) * + (purge_deltas[index+1]-purge_deltas[index])) / + (dtbsize/deltas); + } else { + purge_delta = purge_deltas[deltas-1]; + } + fd_check_interval = purge_delta; + if (fd_check_interval > FD_CHECK_DEF) + fd_check_interval = FD_CHECK_DEF; + /* + * Run a purge on virtual circuits unused since now + * minus purge_delta seconds, if + * + * The new purge cutoff would be more recent than + * the one used in the last purge, and + * + * We have a fairly large number of fd:s open + * (more than 128, if deltas=16 and dtbsize=1024), + * + * or + * + * More than 'purge_delta' seconds have passed + * since the last purge. + */ + if (now.tv_sec - purge_delta > prev_since && + (index > 1 || now.tv_sec - last_purge >= purge_delta)) { + __svc_nisplus_purge_since( + now.tv_sec-purge_delta); + prev_since = now.tv_sec - purge_delta; + last_purge = now.tv_sec; + } + } + + gettimeofday(&now, NULL); + if (now.tv_sec > next_refresh) { + next_refresh = __nis_serverRefreshCache(); + syslog(LOG_DEBUG, "nis_main: next_refresh %u", + next_refresh); + } + + /* Perform deferred frees */ + __nis_thread_cleanup(__nis_get_tsd()); + + (void) msleep(sleeptime); + if ((last_activity.tv_sec - now.tv_sec) >= sleeptime/1000) { + /* + * No activity while we slept. Remove tables from + * standby mode and go back to sleep. + */ + (void) db_standby(0); + } + check_updaters(); + check_pingers(); + /* the force_checkpoint flag is set by -F or ping -C */ + if (force_checkpoint) { + force_checkpoint = FALSE; + /* + * Give the local databases a chance to + * checkpoint their data. + */ + if (verbose) + syslog(LOG_INFO, + "Service Checkpoint..."); + checkpoint_db(); + if (checkpoint_log()) { + if (verbose) + syslog(LOG_INFO, + "checkpoint succeeded."); + need_checkpoint = FALSE; + } else if (verbose) + syslog(LOG_INFO, "checkpoint failed."); + } + } + /* Not reached */ +} + +#define NO_ENTRY_OBJS 96 + +void * +callback_thread(void *varg) { + + callback_thread_arg_t *arg = (callback_thread_arg_t *)varg; + netobj cookie; + int cbres, queued; + table_col *tc; + nis_object *cbarray[NO_ENTRY_OBJS]; + nis_object *d_obj; + enum clnt_stat status = RPC_SUCCESS; + struct timeval tv = {3600, 0}; + char tblbuf[NIS_MAXNAMELEN * 2]; + char *table; + nis_error result; + int i; + pthread_t myself = pthread_self(); + + (void) nis_add_callback_id(myself, arg->pname); + + arg->cbarg.entries.entries_val = &(cbarray[0]); + cookie = arg->fnr->cookie; + cbres = 0; + queued = 0; + if (__type_of(arg->ib_obj) == NIS_TABLE_OBJ) + tc = arg->ib_obj->TA_data.ta_cols.ta_cols_val; + else + tc = tbl_prototype.ta_cols.ta_cols_val; + + while ((arg->fnr->status == NIS_SUCCESS) && (!cbres)) { + if (arg->nm && multival_filter(arg->ib_obj, + arg->nm, arg->a + arg->na, arg->fnr->obj)) { + nis_destroy_object(arg->fnr->obj); + } else if (arg->all_read || + __can_do(NIS_READ_ACC, arg->fnr->obj->zo_access, + arg->fnr->obj, arg->pname)) { + cbarray[queued] = arg->fnr->obj; + queued++; + } else { + d_obj = nis_censor_object_attr(arg->fnr->obj, tc, + arg->pname, arg->na + arg->nm, arg->a); + if (d_obj) { + cbarray[queued] = d_obj; + queued++; + } + nis_destroy_object(arg->fnr->obj); + } + /* + * the object is either already assigned to + * cbarray or destroyed. + */ + arg->fnr->obj = 0; + if (queued == NO_ENTRY_OBJS) { + arg->cbarg.entries.entries_len = NO_ENTRY_OBJS; + status = clnt_call(arg->cback, CBPROC_RECEIVE, + xdr_cback_data, (char *)&arg->cbarg, + xdr_bool, (char *)&cbres, tv); + if (verbose) + syslog(LOG_INFO, + "list: sent entry, status = %s", + clnt_sperrno(status)); + for (i = 0; i < NO_ENTRY_OBJS; i++) { + nis_destroy_object(cbarray[i]); + cbarray[i] = NULL; + } + queued = 0; + if ((status != RPC_SUCCESS) || cbres) { + if ((cbres == 0) && verbose) { + syslog(LOG_ERR, + "nis_list_svc: callback to %s returned %s", + arg->cbhostname, clnt_sperrno(status)); + } + break; + } + } + XFREE(arg->fnr); + table = internal_table_name(arg->ibr_name, tblbuf); + arg->fnr = db_nextib(arg->ibr_name, &cookie, + FN_MANGLE+FN_NORAGS, table); + /* + * Note: that the db_next_entry function will + * free this data. Technically this is an error + * but it is convienient. + */ + cookie = arg->fnr->cookie; + } + if (queued) { + arg->cbarg.entries.entries_len = queued; + status = clnt_call(arg->cback, CBPROC_RECEIVE, + xdr_cback_data, (char *)&arg->cbarg, + xdr_bool, (char *)&cbres, tv); + if ((status != RPC_SUCCESS) && verbose) + syslog(LOG_ERR, + "nis_list_svc: callback to %s returned %s", + arg->cbhostname, clnt_sperrno(status)); + for (i = 0; i < queued; i++) + nis_destroy_object(cbarray[i]); + } + tv.tv_sec = 10; + tv.tv_usec = 0; + if (status != RPC_SUCCESS) { + if (status == RPC_AUTHERROR) { + syslog(LOG_WARNING, + "Authentication ERROR in talking to %s.", + arg->cbhostname); + auth_destroy(arg->cback->cl_auth); + arg->cback->cl_auth = authnone_create(); + result = NIS_CLNTAUTH; + } else { + syslog(LOG_WARNING, + "RPC ERROR in talking to %s.", + arg->cbhostname); + result = NIS_RPCERROR; + } + (void) clnt_call(arg->cback, CBPROC_ERROR, + xdr_nis_error, (char *)&result, + xdr_void, (char *)(0), tv); + } else { + (void) clnt_call(arg->cback, CBPROC_FINISH, + xdr_void, (char *)(0), + xdr_void, (char *)(0), tv); + } + if (arg->fnr->status == NIS_SUCCESS) { + if (arg->fnr->obj) + nis_destroy_object(arg->fnr->obj); + db_flush(arg->ibr_name, &(arg->fnr->cookie)); + } + if (arg->ib_obj != 0) + nis_destroy_object(arg->ib_obj); + XFREE(arg->fnr); + auth_destroy(arg->cback->cl_auth); + clnt_destroy(arg->cback); + /* Our parent thread allocated this, but it's our job to deallocate */ + free(varg); + + nis_delete_callback_id(myself); + + /* + * The periodic thread cleanup (or the TSD destructor, if all else + * fails) will take care of the loose ends + */ + return (0); +} + +/* From nis_xx_proc.c */ +#define CB_BUF_SIZE 128 + +/* + * Perform the actual dump work on behalf of nis_dump_svc(). + */ +void * +dumpsvc_thread(void *varg) { + + dumpsvc_thread_arg_t *arg = varg; + __nis_hash_table_mt tables = NIS_HASH_TABLE_MT_INIT; + __nis_hash_item_mt *item; + int i, queued_objs; + nis_fn_result *fnr; + netobj cookie; + int cbres = 0; + char namebuf[1024]; + int totlobjs = 0; + int skipobjs = 0; + nis_object *cbarray[CB_BUF_SIZE]; + struct timeval tv, go; + long boost = 0; + cback_data cbarg; + enum clnt_stat rstat; + pthread_t myself = pthread_self(); + + if (arg == 0) + return (0); + + (void) nis_add_callback_id(myself, arg->pname); + + syslog(LOG_INFO, "nis_dump_svc[%d]: sending full dump of %s to %s", + pthread_self(), arg->da_dir, arg->pname); + + cbarg.entries.entries_val = cbarray; + nis_insert_name(arg->da_dir, &tables); + queued_objs = 0; + + while ((item = __nis_pop_item_mt(&tables)) != 0) { + strcpy(arg->pname, item->name); + if (verbose) + syslog(LOG_INFO, "nis_dump_svc (child) : Dumping '%s'", + arg->pname); + fnr = db_firstib(arg->pname, 0, NULL, FN_NOMANGLE+FN_NORAGS, + NULL); + cookie = fnr->cookie; + if (fnr->status == NIS_NOSUCHTABLE) { + syslog(LOG_INFO, + "nis_dump_svc (child): Couldn't read table '%s'", + arg->pname); + } else if (fnr->status == NIS_SUCCESS) { + while (! cbres) { + if (__type_of(fnr->obj) == NIS_TABLE_OBJ) { + sprintf(namebuf, "%s.%s", + fnr->obj->zo_name, + fnr->obj->zo_domain); + nis_insert_name(namebuf, &tables); + } + if (fnr->obj->zo_oid.mtime > arg->ttime) { + skipobjs++; + } else { + cbarray[queued_objs++] = fnr->obj; + totlobjs++; + } + /* + * This won't be true should we skip + * recent updates but let's test for + * it anyway. + */ + if (queued_objs == CB_BUF_SIZE) { + cbarg.entries.entries_len = + CB_BUF_SIZE; + tv.tv_sec = 600 > boost ? 600: boost; + tv.tv_usec = 0; + gettimeofday(&go, NULL); + rstat = clnt_call(arg->cback, + CBPROC_RECEIVE, xdr_cback_data, + (char *)&cbarg, xdr_bool, + (char *)&cbres, tv); + gettimeofday(&tv, NULL); + for (i = 0; i < CB_BUF_SIZE; i++) + nis_destroy_object(cbarray[i]); + + if (rstat != RPC_SUCCESS) { + syslog(LOG_ERR, + "nis_dump_svc (child): Callback failed with RPC error %s.", + clnt_sperrno(rstat)); + auth_destroy( + arg->cback->cl_auth); + clnt_destroy(arg->cback); + free(varg); + nis_delete_callback_id(myself); + return (0); + } + queued_objs = 0; + boost = 3 * (tv.tv_sec - go.tv_sec); + } + XFREE(fnr); + fnr = db_nextib(arg->pname, &cookie, + FN_NORAGS, NULL); + cookie = fnr->cookie; + if (fnr->status != NIS_SUCCESS) + break; + } + } + if (fnr->status == NIS_SUCCESS) + XFREE(fnr->cookie.n_bytes); + XFREE(fnr); + if (queued_objs) { + cbarg.entries.entries_len = queued_objs; + if (! cbres) { + tv.tv_sec = 600 > boost ? 600 : boost; + tv.tv_usec = 0; + rstat = clnt_call(arg->cback, CBPROC_RECEIVE, + xdr_cback_data, (char *)&cbarg, + xdr_bool, (char *)&cbres, tv); + if (rstat != RPC_SUCCESS) { + syslog(LOG_ERR, + "nis_dump_svc (child): Callback failed with RPC error %s.", + clnt_sperrno(rstat)); + auth_destroy(arg->cback->cl_auth); + clnt_destroy(arg->cback); + free(varg); + nis_delete_callback_id(myself); + return (0); + } + } + for (i = 0; i < queued_objs; i++) + nis_destroy_object(cbarray[i]); + + queued_objs = 0; + } + + free(item->name); + free(item); + + boost = 0; /* on to next table */ + } + + syslog(LOG_INFO, "nis_dump_svc[%d]: good dump of %s, %d total objects " + "(%d deferred)", pthread_self(), arg->da_dir, totlobjs, skipobjs); + if (verbose) + syslog(LOG_INFO, "nis_dump_svc (child): dump complete."); + + tv.tv_sec = 3; + tv.tv_usec = 0; + rstat = clnt_call(arg->cback, CBPROC_FINISH, xdr_void, 0, xdr_void, 0, + tv); + if (rstat != RPC_SUCCESS && rstat != RPC_TIMEDOUT) { + + /* + * Since the callback function handling this FINISH rpc + * call does not reply, so we basically ignore it if a + * RPC_TIMEDOUT message is returned. Only log other types + * of non-successful messages. + */ + syslog(LOG_WARNING, + "nis_dump_svc[%d]: Finish handshake returned %s", + pthread_self(), clnt_sperrno(rstat)); + } + auth_destroy(arg->cback->cl_auth); + clnt_destroy(arg->cback); + free(varg); + + nis_delete_callback_id(myself); + + return (0); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_xx_proc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_xx_proc.c new file mode 100644 index 0000000000..9c45816c14 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_xx_proc.c @@ -0,0 +1,1936 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1991-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Ported from + * "@(#)nis_xx_proc.c 1.34 91/04/13 Copyr 1990 Sun Micro"; + * + * nis_xx_proc.c + * + * This module contains various routines needed by the NIS version 3 + * service and all of the replicate management routines. + * NB : It provides the routines that the dispatch function in nis_svc.c + * call. That file, nis_svc.c, is automatically generated and reflects the + * interface definition that is described in the nis.x file. When the + * nis.x file changes, you must make sure that any parameters that change + * get reflected in these routines. + * + */ + +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <stdlib.h> +#include <pwd.h> +#include <malloc.h> +#include <rpc/rpc.h> +#include <rpc/key_prot.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/nis_callback.h> +#include <rpc/des_crypt.h> +#include "nis_svc.h" +#include "nis_proc.h" +#include "nis_mt.h" + +/* Statistic variables */ +extern struct ops_stats nisopstats[]; +extern int nislookups; +extern int dircachecall; +extern int dircachemiss; +extern int dircachehit; +extern int __nis_ss_used; + +extern int emulate_yp; +extern int resolv_flag; + +extern nis_object *get_root_object(); + +static void rmdir_update(char *); + +/* + * This is the signature function, it should sign the data in the + * buffer so that when the client cache routines pass it to the + * cache manager it can trust the answer it gets. + * returns 1 on success and 0 if some error. + */ +#ifdef SIGN_RESULTS +static int +__sign_off(requester, res) + nis_name requester; + fd_result *res; +{ + char s_netname[MAXNETNAMELEN+1]; + char pkey[HEXKEYBYTES+1]; + unsigned char *digest; + unsigned int digestlen; + des_block deskey; + keybuf requester_pkey; + int err; + nis_attr qry[3]; + char srch[2048]; + nis_object *d_obj; + nis_name dirname; + struct ticks t; + nis_db_list_result *dbres; + + /* + * no need to sign for these special cases. + * these are used by nisinit, when the principal names haven't + * been set up. + * "multicast" should also be added when that is implemented + */ + + if ((strcmp(requester, "broadcast") == 0) || + (strcmp(requester, "nobody") == 0)) + return (0); + + /* get the netname and the publickey of the requester machine */ + if (!host2netname(s_netname, requester, NULL)) { + if (verbose) + syslog(LOG_ERR, + "__sign_off: host2netname failed for: %s", requester); + return (0); + } + dirname = nis_domain_of(requester); + if (! dirname || (*dirname == '.')) + return (0); + + err = __directory_object(dirname, &t, 0, &d_obj); + pkey[0] = 0; + /* we serve this directory so we "know" the public key. */ + if (d_obj) { + qry[0].zattr_ndx = "auth_name"; + qry[0].zattr_val.zattr_val_len = strlen(s_netname) + 1; + qry[0].zattr_val.zattr_val_val = &s_netname[0]; + qry[1].zattr_ndx = "auth_type"; + qry[1].zattr_val.zattr_val_len = 4; + qry[1].zattr_val.zattr_val_val = "DES"; + sprintf(srch, "cred.org_dir.%s", nis_domain_of(requester)); + dbres = db_list(srch, 2, &qry[0]); + if (dbres->status == NIS_SUCCESS) { + strncpy(pkey, ENTRY_VAL(dbres->objs[0].o, 3), + HEXKEYBYTES); + } + } else { + getpublickey(s_netname, pkey); + } + if (pkey[0] == 0) { + syslog(LOG_ERR, + "__sign_off: Unable to get publickey for: %s", + s_netname); + return (0); + } + /* get the conversation key from the keyserver */ + memset((void *)requester_pkey, 0, sizeof (keybuf)); + memcpy((char *)requester_pkey, pkey, HEXKEYBYTES); + if (key_get_conv(requester_pkey, &deskey) != 0) { + if (verbose) + syslog(LOG_ERR, + "__sign_off: Could not get conversation key for: %s", + requester); + return (0); + } + /* calculate the md5 checksum of XDR'ed data and encrypt it */ + digest = NULL; + __nis_calculate_encrypted_cksum(res->dir_data.dir_data_len, + res->dir_data.dir_data_val, + (char *)&deskey, &digestlen, &digest); + if (!digest) + return (0); + + res->signature.signature_val = (char *)digest; + res->signature.signature_len = digestlen; + return (1); +} +#else +static int +__sign_off(req, res) + nis_name req; + fd_result *res; +{ + return (1); +} +#endif + + +/* + * Return a Find Directory result. + */ +fd_result * +__fd_res(requester, stat, o) + nis_name requester; + nis_error stat; + directory_obj *o; +{ + fd_result *res; + int status, size1; + XDR xdrs; + static fd_result mem_err = {NIS_NOMEMORY, 0}; + + res = (fd_result *)XCALLOC(1, sizeof (fd_result)); + if (! res) { + syslog(LOG_CRIT, + "find_directory_svc: Out of memory, location request aborted."); + return (&mem_err); + } + add_cleanup((void (*)())XFREE, (char *)res, "fd_res: result"); + + res->status = stat; + res->source = nis_local_principal(); + if (res->source == NULL) { + res->status = NIS_NOMEMORY; /* probable cause */ + return (res); + } + /* + * If this is the answer, or "closer" to the + * name they want then just return it. + */ + if (o) { + size1 = xdr_sizeof(xdr_directory_obj, o); + res->dir_data.dir_data_val = (char *)XMALLOC(size1); + if (res->dir_data.dir_data_val) { + add_cleanup((void (*)())XFREE, + (char *)res->dir_data.dir_data_val, + "fd_res: data"); + + xdrmem_create(&xdrs, res->dir_data.dir_data_val, + size1, XDR_ENCODE); + status = xdr_directory_obj(&xdrs, o); + if (!status) + syslog(LOG_ERR, + "Unable to encode resulting directory object."); + res->dir_data.dir_data_len = size1; + res->signature.signature_val = NULL; + res->signature.signature_len = 0; + /* sign the result */ + __sign_off(requester, res); + + } else { + syslog(LOG_CRIT, + "find_directory_svc: Out of memory, location request aborted."); + res->status = NIS_NOMEMORY; + return (res); } + } + return (res); +} + +/* + * nis_finddirectory, this routine is called by clients that are looking for + * the names of machines serving the directory they are interested + * in. It responds with a directory object of the directory that the client + * is looking for or the directory object for a directory that is closer to + * the desired directory than this one is. If the server is the desired + * directory it returns an object containing itself. + * NB: There are three possible results : + * This Directory + * My Parent Directory + * A sub directory. + * + */ + +fd_result * +nis_finddirectory_svc(argp, reqstp) + fd_args *argp; + struct svc_req *reqstp; +{ + directory_obj *dobj; /* The proposed answer */ + char *in, *s, *t; + name_pos p; + int i_serve, i; + struct fd_result *res; + nis_db_result *dbres = NULL; + nis_error stat; + static fd_result mem_err = {NIS_NOMEMORY, 0}; + + if (verbose) + syslog(LOG_INFO, + "FINDDIR_SVC: Location request for directory %s", + argp->dir_name); + + /* + * If we serve this object, it seems a bit silly to look in the + * shared cache. Even if it's there (not at all certain, if it + * isn't our cold start directory), the shared cache copy is probably + * at best up-to-date with the real one. We use __directory_object() + * to check the directory list ('dl') cache, or to retrieve a copy + * from the real database. + */ + { + nis_object *tmpobj; + struct ticks ticks; + nis_error dostatus; + + dostatus = __directory_object_msg(argp->dir_name, &ticks, 0, + &tmpobj, 0); + if (dostatus == NIS_SUCCESS) { + return (__fd_res(argp->requester, NIS_SUCCESS, + &(tmpobj->DI_data))); + } + } + + /* Allocate some memory to hold our directory object */ + dobj = (directory_obj *)XMALLOC(sizeof (*dobj)); + if (! dobj) { + syslog(LOG_CRIT, "find_directory_svc: No memory."); + return (&mem_err); + } + + /* Read the cache first (won't recurse) */ + stat = __nis_CacheSearch(argp->dir_name, dobj); + if (stat != NIS_SUCCESS) { + XFREE(dobj); + syslog(LOG_ERR, "Location cache failure on server:%s", + nis_sperrno(stat)); + return (__fd_res(argp->requester, stat, NULL)); + } + + /* + * The location algorithm works as follows: start on the + * easy cases and then work up to the hard ones. + */ + p = nis_dir_cmp(argp->dir_name, dobj->do_name); + if (p == BAD_NAME) { + xdr_free(xdr_directory_obj, (char *)(dobj)); + XFREE(dobj); + return (__fd_res(argp->requester, NIS_BADNAME, NULL)); + } else if (p == SAME_NAME) { + /* Real easy, the cache returned the answer. */ + add_xdr_cleanup(xdr_directory_obj, (char *)dobj, + "fd_res: dirobj"); + return (__fd_res(argp->requester, NIS_SUCCESS, dobj)); + } + + s = nis_local_host(); + + /* + * Step 1. Next easiest + * If the cache result returned is above my directory, I cannot + * possibly serve it. And if the target name is below the + * cache result then we're one step closer to resolving it + * so just return the cache result. + */ + if ((p == LOWER_NAME) && + (nis_dir_cmp(dobj->do_name, __nis_rpc_domain()) + == HIGHER_NAME)) { + add_xdr_cleanup(xdr_directory_obj, (char *)dobj, + "fd_res: dirobj"); + return (__fd_res(argp->requester, NIS_SUCCESS, dobj)); + } + + /* determine whether I serve this directory */ + for (i = 0; i < dobj->do_servers.do_servers_len; i++) { + t = dobj->do_servers.do_servers_val[i].name; + if (nis_dir_cmp(s, t) == SAME_NAME) + break; + } + i_serve = (i < dobj->do_servers.do_servers_len); + + /* + * Step 2, a bit tougher. + * If the directory being located is either above us in + * the tree or above us and down another branch (a sibling), we + * have two options. If the directory the cache produced was one + * that I serve, we return our parent, otherwise we return the + * cached result. If the name is above us and we are the root + * server, we return an error or our parent object + */ + if ((p == HIGHER_NAME) || (p == NOT_SEQUENTIAL)) { + if (! i_serve) { + /* I don't serve it so it is a valid answer */ + add_xdr_cleanup(xdr_directory_obj, (char *)dobj, + "fd_res: dobj (cache result)"); + return (__fd_res(argp->requester, NIS_SUCCESS, dobj)); + } + } + /* free data in the previous directory object */ + xdr_free(xdr_directory_obj, (char *)(dobj)); + + if (nis_dir_cmp(argp->dir_name, __nis_rpc_domain()) == HIGHER_NAME) { + if (root_server) { + nis_object *obj; + + /* Won't need the directory object anymore */ + XFREE(dobj); + + obj = nis_read_obj(nis_data(PARENT_OBJ)); + if (! obj) + return (__fd_res(argp->requester, + NIS_NOSUCHNAME, NULL)); + + add_xdr_cleanup(xdr_nis_object, (char *)obj, + "fd_res: parent obj (cache result)"); + if (obj->DI_data.do_type == NIS) + return (__fd_res(argp->requester, NIS_SUCCESS, + &(obj->DI_data))); + else + return (__fd_res(argp->requester, NIS_FOREIGNNS, + &(obj->DI_data))); + } else { + stat = __nis_CacheSearch(__nis_rpc_domain(), dobj); + if (stat == NIS_SUCCESS) { + add_xdr_cleanup(xdr_directory_obj, + (char *)dobj, "fd_res: dobj (parent)"); + return (__fd_res(argp->requester, NIS_SUCCESS, + dobj)); + } else { + XFREE(dobj); + return (__fd_res(argp->requester, stat, NULL)); + } + } + } + + + if (nis_dir_cmp(argp->dir_name, __nis_rpc_domain()) == + NOT_SEQUENTIAL) { + if (root_server) { + XFREE(dobj); + return (__fd_res(argp->requester, + NIS_NOSUCHNAME, NULL)); + } + } + + /* + * Step 3, toughest one + * By process of elimination I know the name is below + * some cache entry that got returned. That could be either + * something I serve or the pointer to my parent. In either + * case, I start searching my database from the name passed + * upward toward the root. Four things can happen + * 1) If I find the one we're looking for then great, I'll + * just return it. + * 2) If I never find any thing closer (ie a directory object + * in it's path then my parent can deal with it. + * 3) If I find a directory in the desired directories "path" + * that I don't serve, we pass the request on to those + * servers. + * 4) If I find a directory that I serve above it in the name + * space then the target directory cannot exist. Because + * if it did I would have found it's directory object + * before finding a directory that I serve. + * + * We start using the whole name and selectively pick off + * leaves until we're up to the our local directory. + */ + + in = argp->dir_name; + while (nis_dir_cmp(in, __nis_rpc_domain()) == LOWER_NAME) { + if (verbose) + syslog(LOG_INFO, "Locating %s in the database.", in); + dbres = db_lookup(in); + if (dbres->status == NIS_SUCCESS) { + + /* + * POLICY : You can't link directories with + * LINK objects. + * ANSWER : Because the recursion could become + * infinite. + */ + XFREE(dobj); /* won't be needing this any more */ + if (__type_of(dbres->obj) != NIS_DIRECTORY_OBJ) { + if (verbose) + syslog(LOG_INFO, + "Object %s isn't a directory.", + dbres->obj->zo_name); + res = __fd_res(argp->requester, NIS_BADOBJECT, + NULL); + } else { + /* + * we found a directory object in the "path" + * of the desired object. + */ + if (verbose) + syslog(LOG_INFO, + "Found a directory %s", + dbres->obj->zo_name); + dobj = &(dbres->obj->DI_data); + p = nis_dir_cmp(dobj->do_name, argp->dir_name); + if (p == SAME_NAME) { + res = __fd_res(argp->requester, + NIS_SUCCESS, dobj); + /* + * It isn't the answer (#1) so let's see if we + * serve it. (#3 or #4) + */ + } else { + s = nis_local_host(); + for (i = 0; + i < dobj->do_servers.do_servers_len; + i++) { + t = + dobj->do_servers.do_servers_val[i].name; + if (nis_dir_cmp(s, t) + == SAME_NAME) + break; + } + if (i < dobj->do_servers.do_servers_len) + res = __fd_res(argp->requester, + NIS_NOSUCHNAME, NULL); + else + res = __fd_res(argp->requester, + NIS_SUCCESS, dobj); + } + } + return (res); + } else if (dbres->status == NIS_NOTFOUND) { + XFREE(dobj); + return (__fd_res(argp->requester, NIS_NOSUCHNAME, + NULL)); + } + in = nis_domain_of(in); + } + + if (verbose) { + syslog(LOG_INFO, "Unable to locate directory : %s", + argp->dir_name); + } + + /* Option #2, let my parent deal with it. */ + stat = __nis_CacheSearch(__nis_rpc_domain(), dobj); + if (stat == NIS_SUCCESS) { + add_xdr_cleanup(xdr_directory_obj, (char *)dobj, + "fd_res: dobj (parent)"); + return (__fd_res(argp->requester, NIS_SUCCESS, dobj)); + } + XFREE(dobj); + return (__fd_res(argp->requester, stat, NULL)); +} + +/* + * nis_callback_svc + * + * Check on the state of a child process or thread who is calling back to the + * client. + */ + +bool_t * +nis_callback_svc(argp, reqstp) + netobj *argp; + struct svc_req *reqstp; +{ +#define res (__nis_get_tsd()->nis_callback_svc_res) + pthread_t id; + anonid_t anonid; + char pname[1024], id_pname[1024]; + + if (argp->n_bytes == 0 || argp->n_len != sizeof (anonid_t)) { + syslog(LOG_ERR, "CALLBACK_SVC: bad argument"); + res = FALSE; + return (&res); + } + + /* Get name of principal calling us */ + nis_getprincipal(pname, reqstp); + + /* + * If an unauthenticated user issued a nis_list() for a table on + * which "nobody" has read rights, then the callback rpc.nisd + * will be running on behalf of nobody. Hence, it is not appropriate + * for us to verify authentication; we just check that the + * principal behind the NIS_CALLBACK, and the one who initiated the + * callback, is one and the same. If the initator name is "nobody", + * anything goes. + */ + + /* Obtain pid and principal on whose behalf pid is running */ + anonid = *((anonid_t *)argp->n_bytes); + id_pname[0] = '\0'; + id = nis_get_callback_id(anonid, id_pname, sizeof (id_pname)); + + if (verbose) + syslog(LOG_INFO, + "CALLBACK_SVC: ID = %ld, anon id = %ld, pname = %s, id_pname = %s", + id, anonid, pname, id_pname); + + /* Verify caller principal same as pid principal */ + if (secure_level >= 2 && strcmp(pname, id_pname) != 0 && + strcmp("nobody", id_pname) != 0) { + res = FALSE; + return (&res); + } + + res = (pthread_kill(id, 0) == 0); + + return (&res); +} + +#undef res + + +/* Names of the operations for statistics functions. */ +static char *opnames[] = { + "NULLPROC", /* 0 */ + "LOOKUP", /* 1 */ + "ADD", /* 2 */ + "MODIFY", /* 3 */ + "REMOVE", /* 4 */ + "LIST", /* 5 */ + "ADDENTRY", /* 6 */ + "MODENTRY", /* 7 */ + "REMENTEY", /* 8 */ + "FIRSTENTRY", /* 9 */ + "NEXTENTRY", /* 10 */ + "RSRVD1", /* 11 */ + "FINDDIR", /* 12 */ + "RSRVD2", /* 13 */ + "STATUS", /* 14 */ + "DUMPLOG", /* 15 */ + "DUMP", /* 16 */ + "CALLBACK", /* 17 */ + "CPTIME", /* 18 */ + "CHECKPOINT", /* 19 */ + "PING", /* 20 */ + "SERVSTATE", /* 21 */ + "MKDIR", /* 22 */ + "RMDIR", /* 23 */ + "UPDKEYS" /* 24 */ +}; + +static char * +prtop(p) + int p; +{ + int i; + struct ops_stats *op; + ulong_t avgtime = 0; + static char opbuf[80]; + + if (p < 0 || p >= (sizeof (opnames) / sizeof (opnames[0]))) { + sprintf(opbuf, "<unknown operation %d>", p); + return (opbuf); + } + RLOCK(nisopstats); + op = &(nisopstats[p]); + for (i = 0; (i < 16) && op->tsamps[i]; i++) + avgtime += op->tsamps[i]; + if (i) + avgtime = avgtime / i; + sprintf(opbuf, "\nOP=%s:C=%d:E=%d:T=%d", opnames[p], op->calls, + op->errors, avgtime); + RULOCK(nisopstats); + return (opbuf); +} + +extern struct timeval start_time; + +static void +up_since(s) + char *s; +{ + struct timeval tv; + long days, hrs, mins, secs; + + gettimeofday(&tv, NULL); + secs = tv.tv_sec - start_time.tv_sec; + days = secs / 86400; + hrs = (secs - (days * 86400)) / 3600; + mins = (secs - (days * 86400) - (hrs * 3600)) / 60; + secs = secs % 60; + sprintf(s, "up %dD, %02d:%02d:%02d", days, hrs, mins, secs); +} + +static void +free_taglist(nis_taglist *tl) +{ + nis_freetags(tl->tags.tags_val, tl->tags.tags_len); + free((void *)tl); +} + +#define CHECK_TAG_S_ACC(tag) \ + if (!nis_op_access("NIS_STATUS", tag, 0, 0, \ + reqstp)) { \ + strcpy(tag_data, "<permission denied>"); \ + break; \ + } + +/* + * nis_status, return status on the nis server. + */ +nis_taglist * +nis_status_svc(argp, reqstp) + nis_taglist *argp; + struct svc_req *reqstp; +{ + int i, nt, j; + nis_tag *tlist; + char tag_data[1024]; /* Enough for any tag other than DIR_LIST */ + char *dirs; /* malloc:ed string for DIR_LIST */ + nis_taglist *res; + int grpcachecall; + int grpcachemiss; + int grpcachehit; + extern int __nis_group_cache_stats(int *, int *, int *); + extern unsigned int heap_start; + + if (verbose) + syslog(LOG_INFO, "STATUS_SVC: %d tags passed", + argp->tags.tags_len); + nt = argp->tags.tags_len; + tlist = argp->tags.tags_val; + + /* malloc memory for result */ + res = (nis_taglist *)XCALLOC(1, sizeof (nis_taglist)); + if (res == NULL) { + syslog(LOG_ERR, "nis_status_svc: no memory."); + return (NULL); + } + add_cleanup((void (*)())free_taglist, res, "tag list"); + + res->tags.tags_val = (nis_tag *)XCALLOC(nt, sizeof (nis_tag)); + res->tags.tags_len = nt; + if (res->tags.tags_val == NULL) { + syslog(LOG_ERR, "nis_status_svc: no memory."); + return (NULL); + } + + for (i = 0; i < nt; i++) { + switch (tlist[i].tag_type) { + case TAG_UPTIME : + CHECK_TAG_S_ACC("TAG_UPTIME"); + up_since(tag_data); + break; + case TAG_S_DCACHE : + CHECK_TAG_S_ACC("TAG_S_DCACHE"); + RLOCK(dircachestats); + if (dircachecall) + sprintf(tag_data, + "C=%d:H=%d:M=%d:HR=%2.0f%%", + dircachecall, dircachehit, + dircachemiss, + ((dircachecall-dircachemiss)*(float)100)/ + dircachecall); + else + sprintf(tag_data, + "C=0:H=0:M=0:HR=100%%"); + RULOCK(dircachestats); + break; + case TAG_S_STORAGE : + CHECK_TAG_S_ACC("TAG_S_STORAGE"); + sprintf(tag_data, "%d", __nis_ss_used); + break; + case TAG_S_GCACHE : + CHECK_TAG_S_ACC("TAG_S_GCACHE"); + if (__nis_group_cache_stats(&grpcachecall, + &grpcachehit, &grpcachemiss) && + grpcachecall != 0) + sprintf(tag_data, + "C=%d:H=%d:M=%d:HR=%2.0f%%", + grpcachecall, grpcachehit, + grpcachemiss, + ((grpcachecall-grpcachemiss)*(float)100)/ + grpcachecall); + else + sprintf(tag_data, + "C=0:H=0:M=0:HR=100%%"); + break; + case TAG_OPSTATS : + CHECK_TAG_S_ACC("TAG_OPSTATS"); + if ((*(tlist[i].tag_val) == '\0') || + (strcmp(tlist[i].tag_val, "all") == 0)) { + tag_data[0] = '\0'; + for (j = 0; j < 24; j++) { + if (! nisopstats[j].calls) + continue; + strcat(tag_data, prtop(j)); + } + } else { + j = atoi(tlist[i].tag_val); + sprintf(tag_data, "%s", prtop(j)); + } + break; + case TAG_HEAP : + CHECK_TAG_S_ACC("TAG_HEAP"); + sprintf(tag_data, "%u", + (unsigned int) sbrk(0) - heap_start); + break; + case TAG_DIRLIST: + CHECK_TAG_S_ACC("TAG_DIRLIST"); + if (nis_server_control(SERVING_LIST, + DIR_GETLIST, &dirs) == 0) { + dirs = NULL; + } + break; + case TAG_NISCOMPAT: + CHECK_TAG_S_ACC("TAG_NISCOMPAT"); + if (emulate_yp) + strcpy(tag_data, "ON"); + else + strcpy(tag_data, "OFF"); + break; + case TAG_DNSFORWARDING: + CHECK_TAG_S_ACC("TAG_DNSFORWARDING"); + if (resolv_flag) + strcpy(tag_data, "ON"); + else + strcpy(tag_data, "OFF"); + break; + case TAG_SECURITY_LEVEL: + CHECK_TAG_S_ACC("TAG_SECURITY_LEVEL"); + sprintf(tag_data, "%d", secure_level); + break; + case TAG_ROOTSERVER: + CHECK_TAG_S_ACC("TAG_ROOTSERVER"); + if (root_server) + strcpy(tag_data, "ON"); + else + strcpy(tag_data, "OFF"); + break; + default : + strcpy(tag_data, "<Unknown Statistic>"); + break; + } + if (tlist[i].tag_type == TAG_DIRLIST) + res->tags.tags_val[i].tag_val = dirs; + else + res->tags.tags_val[i].tag_val = strdup(tag_data); + if (res->tags.tags_val[i].tag_val == NULL) { + syslog(LOG_ERR, "nis_status_svc: no memory."); + return (NULL); + } + res->tags.tags_val[i].tag_type = tlist[i].tag_type; + } + return (res); +} + +/* + * nis_cptime() + * + * This function will return the timestamp of the last stable transaction + * from the server. All of the timestamps that are maintained in a cluster + * are generated by the master so there is no time skew. This function + * is used by the master to ping replicas and figure out whether it can + * truncate the log or not. + */ +uint_t * +nis_cptime_svc(argp, reqstp) + nis_name *argp; + struct svc_req *reqstp; +{ +#define res (__nis_get_tsd()->nis_cptime_svc_res) + + if (verbose) + syslog(LOG_INFO, "CPTIME_SVC: '%s'", *argp); + + if (!nis_op_access("NIS_CPTIME", 0, *argp, 0, reqstp)) { + if (verbose) + syslog(LOG_INFO, + "CPTIME_SVC: authorization error on \"%s\"", *argp); + res = 0; + return (&res); + } + + res = last_update(*argp); + return (&res); +} + +#undef res + +/* + * nis_checkpoint() + * + * This function sets up a checkpoint to be done, as you might want to do + * if the log were getting too full. The parameter is the name of the + * directory you want to checkpoint. In addition to checkpointing the + * NIS+ log, it checkpoints and local database logs that may need this. + * Note that the actual checkpointing is done later, in the + * main server loop, after the server has forked a read-only child. + * Should not fork here because we're in the middle of an RPC. + * + */ +cp_result * +nis_checkpoint_svc(argp, reqstp) + nis_name *argp; + struct svc_req *reqstp; +{ + cp_result *res; + nis_error stat; + struct ticks t; + nis_object *dobj; + /* xxx static should be const */ + static cp_result mem_err = {NIS_NOMEMORY, 0, 0}; + + if (verbose) + syslog(LOG_INFO, "CHECKPOINT_SVC: '%s'", *argp); + + res = (cp_result *)XCALLOC(1, sizeof (cp_result)); + if (! res) + return (&mem_err); + add_cleanup((void (*)())XFREE, (char *)res, "chkpnt result"); + + if (!nis_op_access("NIS_CHECKPOINT", 0, *argp, 0, reqstp)) { + if (verbose) + syslog(LOG_INFO, + "CHECKPOINT_SVC: authorization error on \"%s\"", + *argp); + res->cp_status = NIS_PERMISSION; + return (res); + } + + __start_clock(0); + + /* + * Maintain '*argp' in a list of directories to be checkpointed. + * and perform do_checkpoint_dir(*argp) over list when checkpointing + * eventually occurs. + * If argument is null string, checkpoint all. + */ + if (*argp == 0 || **argp == 0) { + clear_checkpoint_list(); /* delete remembered items */ + checkpoint_all = 1; + } else { + /* Otherwise, specified directory. make sure we serve it. */ + stat = __directory_object(*argp, &t, 0, &dobj); + if (stat != NIS_SUCCESS) { + res->cp_status = stat; + res->cp_zticks = __stop_clock(0); + return (res); + } + + /* Only add to list if we are not going to cp entire db */ + if (!checkpoint_all) + add_pingitem_with_name(*argp, 0, 0, &checkpoint_list); + } + + /* + * Set status to SUCCESS here to indicate that directory + * object is valid and will be checkpointed. + */ + + res->cp_status = NIS_SUCCESS; + res->cp_zticks = __stop_clock(0); + force_checkpoint = 1; + + /* + * The non-MT code will try to checkpoint immediately, so in order + * to preserve that behavior, we wake up the servloop() thread. + */ + wakeup_servloop(); + + return (res); +} + +/* + * nis_dumplog, dump the log from the indicated timestamp on. + */ +log_result * +nis_dumplog_svc(argp, reqstp) + dump_args *argp; + struct svc_req *reqstp; +{ + log_result *tmp; + char pname[1024]; + nis_object *dobj; + nis_result *lres; + nis_server *srvs; + int ns; + int i; + + + + tmp = (log_result *)XCALLOC(1, sizeof (log_result)); + add_xdr_cleanup(xdr_log_result, (char *)tmp, "dumplog result"); + + if (argp->da_time == 0) { + if (verbose) + syslog(LOG_INFO, + "nis_dumplog_svc: replica asking for time 0. RESYNC."); + tmp->lr_status = NIS_RESYNC; + return (tmp); + } + + /* + * Now we verify that a valid replica is actually asking for this data. + * NB: We look up the object from the master server to pickup the + * latest copy of the object. This lets us see new replicas as + * they are added. + */ + lres = nis_lookup(argp->da_dir, MASTER_ONLY); + if (lres->status != NIS_SUCCESS) { + if (verbose) + syslog(LOG_INFO, + "nis_dump_svc: No directory object, error %s.", + nis_sperrno(lres->status)); + tmp->lr_status = lres->status; + nis_freeresult(lres); + return (tmp); + } + + /* + * Make sure it is a directory they want dumped. + */ + dobj = lres->objects.objects_val; + if (__type_of(dobj) != NIS_DIRECTORY_OBJ) { + syslog(LOG_ERR, + "nis_dump_svc: request to dump %s.%s which isn't a directory!", + dobj->zo_name, dobj->zo_domain); + tmp->lr_status = NIS_BADOBJECT; + nis_freeresult(lres); + return (tmp); + } + + /* + * Make sure that we are the master server for that directory. + */ + ns = dobj->DI_data.do_servers.do_servers_len; + srvs = dobj->DI_data.do_servers.do_servers_val; + if (nis_dir_cmp(srvs[0].name, nis_local_host()) != SAME_NAME) { + nis_freeresult(lres); + tmp->lr_status = NIS_NOTMASTER; + return (tmp); + } + + /* + * Check to see that a valid replica is asking for the dump + */ + nis_getprincipal(pname, reqstp); + if (secure_level) { + for (i = 1; (i < ns) && + (nis_dir_cmp(pname, srvs[i].name) != SAME_NAME); i++) + ; + + if (i == ns) { + if (verbose) + syslog(LOG_INFO, + "nis_dump_svc: invalid replica '%s'", pname); + nis_freeresult(lres); + tmp->lr_status = NIS_PERMISSION; + return (tmp); + } + } + + if (verbose) + syslog(LOG_INFO, "DUMPLOG_SVC : dumping '%s' to host '%s'", + argp->da_dir, pname); + + /* + * Pass the whole directory object so that entries_since() + * can add it to the ping list should it not return all + * entries this time. + */ + entries_since(dobj, argp->da_time, tmp); + + /* don't need this anymore */ + nis_freeresult(lres); + + if (verbose) + syslog(LOG_INFO, + "nis_dumplog_svc: returning status of '%s', and %d deltas.", + nis_sperrno(tmp->lr_status), + tmp->lr_entries.lr_entries_len); + return (tmp); +} + +/* + * nis_dump_svc, dump the entire contents of the named directory. + */ +#define CB_BUF_SIZE 128 + +log_result * +nis_dump_svc(argp, reqstp) + dump_args *argp; + struct svc_req *reqstp; +{ + log_result *res; + CLIENT *cback = NULL; + char pname[1024]; + dumpsvc_thread_arg_t *mtdumparg; + pthread_t tid; + pthread_attr_t attr; + int stat; + int i; + nis_server *srvs; + int ns; + log_entry *le = NULL; + nis_object *dobj; + ulong_t ttime; + nis_result *lres; + nis_server cbsrv; + struct netbuf *rpc_origin; + endpoint *org_endpoint, *alt_endpoint, epbuf; + char uaddrbuf[256], *uaddr = uaddrbuf; + + + if (verbose) + syslog(LOG_INFO, "DUMP_SVC : Dump directory '%s'", + argp->da_dir); + res = (log_result *) XCALLOC(1, sizeof (log_result)); + add_cleanup((void (*)())XFREE, (char *)res, "dump result"); + + /* + * Start the series of checks that need to be met before dumping + * can begin. + */ + if (argp->da_cbhost.da_cbhost_len != 1) { + if (verbose) + syslog(LOG_INFO, "nis_dump_svc: missing callback."); + res->lr_status = NIS_CBERROR; + return (res); + } + + /* + * XXX Do we need to limit the number of dumping threads ? + * No, for now, we just rely on the serialization implicit + * in the use of the __nis_callback_lock mutex in nis_dump() + * (in libnsl). + */ + + /* + * Now we verify that a valid replica is actually asking for this data. + * NB: We look up the object from the master server to pickup the + * latest copy of the object. This lets us see new replicas as + * they are added. + */ + lres = nis_lookup(argp->da_dir, MASTER_ONLY); + if (lres->status != NIS_SUCCESS) { + if (verbose) + syslog(LOG_INFO, + "nis_dump_svc: No directory object, error %s.", + nis_sperrno(lres->status)); + res->lr_status = lres->status; + nis_freeresult(lres); + return (res); + } + + /* + * Make sure it is a directory they want dumped. + */ + dobj = lres->objects.objects_val; + if (__type_of(dobj) != NIS_DIRECTORY_OBJ) { + syslog(LOG_ERR, + "nis_dump_svc: request to dump %s.%s which isn't a directory!", + dobj->zo_name, dobj->zo_domain); + res->lr_status = NIS_BADOBJECT; + nis_freeresult(lres); + return (res); + } + + /* + * Make sure that we are the master server for that directory. + */ + ns = dobj->DI_data.do_servers.do_servers_len; + srvs = dobj->DI_data.do_servers.do_servers_val; + if (nis_dir_cmp(srvs[0].name, nis_local_host()) != SAME_NAME) { + nis_freeresult(lres); + res->lr_status = NIS_NOTMASTER; + return (res); + } + + /* + * Check to see that a valid replica is asking for the dump + */ + nis_getprincipal(pname, reqstp); + if (secure_level) { + for (i = 1; (i < ns) && + (nis_dir_cmp(pname, srvs[i].name) != SAME_NAME); i++) + ; + + if (i == ns) { + if (verbose) + syslog(LOG_INFO, + "nis_dump_svc: invalid replica '%s'", pname); + res->lr_status = NIS_PERMISSION; + return (res); + } + } + + /* + * The replica supplied an address for the callback service. + * However, we may not be able to reach that address, so + * try the source address of the RPC request first. + * + * Note: The alt_endpoint array (which usually contains just + * a single element) is a copy of of org_endpoint, including + * pointers, except for the uaddr of one element. This uaddr + * contains a merged version of the RPC source address and the + * port number specified by the client in the callback data. + * + * The copying tp 'cbsrv' and 'epbuf' below is done so that we + * can free any allocated memory immediately, and won't have to + * worry about that later. + */ + cbsrv = *argp->da_cbhost.da_cbhost_val; + org_endpoint = cbsrv.ep.ep_val; + rpc_origin = svc_getrpccaller(reqstp->rq_xprt); + if (argp->da_cbhost.da_cbhost_val->ep.ep_len == 1 && + (alt_endpoint = __nis_alt_callback_server(org_endpoint, + argp->da_cbhost.da_cbhost_val->ep.ep_len, + rpc_origin, + &uaddr, sizeof (uaddrbuf))) != 0) { + epbuf = alt_endpoint[0]; + cbsrv.ep.ep_val = &epbuf; + free(alt_endpoint); + } + + /* + * XXX now, do we use the handle that was passed us? or do we use + * the one in the object? + */ + cback = nis_make_rpchandle(argp->da_cbhost.da_cbhost_val, 1, + CB_PROG, 1, ZMH_VC+ZMH_AUTH, 128, 8192); + + if (! cback) { + if (verbose) + syslog(LOG_INFO, + "nis_dump_svc: unable to create callback."); + res->lr_status = NIS_CBERROR; + nis_freeresult(lres); + return (res); + } + + /* don't need this anymore */ + nis_freeresult(lres); + + ttime = last_update(argp->da_dir); + if (ttime == time(0)) { + syslog(LOG_INFO, + "nis_dump_svc: Current updates found, try later"); + res->lr_status = NIS_DUMPLATER; + return (res); + } else if (ttime == 0) + syslog(LOG_INFO, + "nis_dump_svc: directory %s has no update timestamp.", + argp->da_dir); + else { + le = (log_entry *) XCALLOC(1, sizeof (log_entry)); + if (! le) { + syslog(LOG_INFO, "nis_dump_svc: out of memory."); + res->lr_status = NIS_NOMEMORY; + auth_destroy(cback->cl_auth); + clnt_destroy(cback); + return (res); + } + add_cleanup((void (*)())XFREE, (char *)le, "dump logent"); + le->le_princp = XSTRDUP(nis_local_principal()); + add_cleanup((void (*)())XFREE, (char *)le->le_princp, + "dump logent->princp"); + le->le_time = ttime; + le->le_type = UPD_STAMP; + le->le_name = XSTRDUP(argp->da_dir); + add_cleanup((void (*)())XFREE, (char *)le->le_name, + "dump logent->name"); + __type_of(&(le->le_object)) = NIS_NO_OBJ; + le->le_object.zo_name = ""; + le->le_object.zo_owner = ""; + le->le_object.zo_group = ""; + le->le_object.zo_domain = le->le_name; + res->lr_entries.lr_entries_len = 1; + res->lr_entries.lr_entries_val = le; + } + + if ((mtdumparg = calloc(1, sizeof (*mtdumparg))) == 0) { + syslog(LOG_WARNING, + "nis_dump_svc: memory allocation failed for %d bytes", + sizeof (*mtdumparg)); + res->lr_status = NIS_NOMEMORY; + auth_destroy(cback->cl_auth); + clnt_destroy(cback); + return (res); + } + (void) strcpy(mtdumparg->da_dir, argp->da_dir); + (void) strcpy(mtdumparg->pname, pname); + mtdumparg->cback = cback; + mtdumparg->ttime = ttime; + + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if ((stat = pthread_create(&tid, &attr, dumpsvc_thread, mtdumparg)) == + 0) { + if (verbose) + syslog(LOG_INFO, + "nis_dump_svc: created callback thread %d", tid); + res->lr_status = NIS_CBRESULTS; + res->lr_cookie.n_bytes = (char *)malloc(sizeof (anonid_t)); + if (res->lr_cookie.n_bytes != NULL) { + add_cleanup((void (*)())XFREE, res->lr_cookie.n_bytes, + "anonid"); + res->lr_cookie.n_len = sizeof (anonid_t); + memcpy(res->lr_cookie.n_bytes, &tid, + sizeof (anonid_t)); + } else { + res->lr_cookie.n_len = 0; + syslog(LOG_WARNING, + "nis_dump_svc: no memory for callback id cookie"); + } + if (verbose) + syslog(LOG_INFO, + "nis_dump_svc: Parent thread returning"); + } else { + if (verbose) + syslog(LOG_ERR, + "nis_dump_svc: callback thread create failed: %d", + stat); + auth_destroy(cback->cl_auth); + clnt_destroy(cback); + res->lr_status = NIS_TRYAGAIN; + } + (void) pthread_attr_destroy(&attr); + return (res); +} + +/* + * nis_ping() + * + * This function receives a "ping" and schedules an update session. + */ +void * +nis_ping_svc(argp, reqstp) + ping_args *argp; + struct svc_req *reqstp; +{ + static int foo; + struct ticks t; + nis_object *obj; + nis_error err; + + if (verbose) + syslog(LOG_INFO, "PING_SVC: dir = '%s'", argp->dir); + + if (!nis_op_access("NIS_PING", 0, argp->dir, 0, reqstp)) { + if (verbose) + syslog(LOG_INFO, + "PING_SVC: unauthorized ping for \"%s\" ignored", argp->dir); + return (0); + } + + /* Check to see if we're replicating the root object */ + if (root_object_p(argp->dir)) { + /* + * Note that we cannot check whether we have the root object + * because this ping might be asking to add me as replica + */ + err = NIS_SUCCESS; + if (last_update(argp->dir) < argp->stamp) + add_pingitem_with_name(argp->dir, + 0, argp->stamp, &upd_list); + } else { + err = __directory_object(argp->dir, &t, NO_CACHE, &obj); + if ((err == NIS_SUCCESS) && (obj != NULL) && + (last_update(argp->dir) < argp->stamp)) + add_pingitem(obj, argp->stamp, &upd_list); + } + + if ((verbose) && (err != NIS_SUCCESS)) + syslog(LOG_INFO, "PING_SVC: error %s.", nis_sperrno(err)); + else if (verbose) + syslog(LOG_INFO, "PING_SVC: done."); + + /* + * The non-MT code would have tried to execute the resync + * immediately. In order to preserve those semantics, we wake + * up the servloop() thread now. + */ + wakeup_servloop(); + + return (&foo); +} + +#define TAG_VAL(n) argp->tags.tags_val[n].tag_val +#define TAG_TYPE(n) argp->tags.tags_val[n].tag_type +#define CHECK_TAG_ACC(tag) \ + if (!nis_op_access("NIS_SERVSTATE", tag, 0, 0, \ + reqstp)) { \ + free(TAG_VAL(i)); \ + TAG_VAL(i) = strdup("<permission denied>"); \ + break; \ + } + +/* + * This function allows the client to change various "state" + * bits in the server. The primary uses are to turn verbosity + * on and off, and to turn statistics on and off. + */ +nis_taglist * +nis_servstate_svc(argp, reqstp) + nis_taglist *argp; + struct svc_req *reqstp; +{ + int i; + + if (verbose) + syslog(LOG_INFO, "SERVSTATE_SVC: %d tags.", + argp->tags.tags_len); + + if (argp->tags.tags_len > 0) { + for (i = 0; i < argp->tags.tags_len; i++) { + switch (TAG_TYPE(i)) { + case TAG_DEBUG : + CHECK_TAG_ACC("TAG_DEBUG"); + if (strcmp(TAG_VAL(i), "on") == 0) + verbose = 1; + else if (strcmp(TAG_VAL(i), "off") == 0) + verbose = 0; + else { + free(TAG_VAL(i)); + TAG_VAL(i) = strdup("unchanged"); + } + break; + case TAG_DCACHE_ONE: + CHECK_TAG_ACC("TAG_DCACHE_ONE"); + if (TAG_VAL(i) && strlen(TAG_VAL(i))) { + flush_dircache(TAG_VAL(i), NULL); + } else { + free(TAG_VAL(i)); + TAG_VAL(i) = strdup("Name missing"); + } + break; + case TAG_DCACHE_ONE_REFRESH: + CHECK_TAG_ACC("TAG_DCACHE_ONE_REFRESH"); + if (TAG_VAL(i) && strlen(TAG_VAL(i))) { + flush_dircache_refresh(TAG_VAL(i)); + } else { + free(TAG_VAL(i)); + TAG_VAL(i) = strdup("Name missing"); + } + break; + case TAG_DCACHE_ALL: + /* This flushes _all_ directory objects */ + CHECK_TAG_ACC("TAG_DCACHE_ALL"); + flush_dircache_all(); + break; + case TAG_TCACHE_ONE : + CHECK_TAG_ACC("TAG_TCACHE_ONE"); + if (TAG_VAL(i) && strlen(TAG_VAL(i))) { + flush_tablecache(TAG_VAL(i)); + } else { + free(TAG_VAL(i)); + TAG_VAL(i) = strdup("Name missing"); + } + break; + case TAG_TCACHE_ALL: + /* This flushes _all_ table caches */ + CHECK_TAG_ACC("TAG_TCACHE_ALL"); + flush_tablecache_all(); + break; + case TAG_GCACHE_ONE : + CHECK_TAG_ACC("TAG_GCACHE_ONE"); + if (TAG_VAL(i) && strlen(TAG_VAL(i))) { + flush_groupcache(TAG_VAL(i)); + } else { + free(TAG_VAL(i)); + TAG_VAL(i) = strdup("Name missing"); + } + break; + case TAG_GCACHE_ALL : + CHECK_TAG_ACC("TAG_GCACHE_ALL"); + flush_groupcache_all(); + break; + case TAG_HEAP : + CHECK_TAG_ACC("TAG_HEAP"); +#ifdef MEM_DEBUG + xdump(); +#else /* MEM_DEBUG */ + TAG_VAL(i) = strdup("<Not enabled>"); +#endif /* MEM_DEBUG */ + break; + case TAG_READONLY: + /* Disables updates, used for nisbackup(1M) */ + CHECK_TAG_ACC("TAG_READONLY"); + if ((CHILDPROC) || (readonly)) { + TAG_VAL(i) = strdup("Try again"); + break; + } + readonly = 1; + break; + case TAG_READWRITE: + /* Reset to read/write, used by nisbackup(1M) */ + CHECK_TAG_ACC("TAG_READWRITE"); + if ((CHILDPROC) || (readonly == 0)) { + TAG_VAL(i) = strdup("Invalid request"); + break; + } + readonly = 0; + break; + + default : + syslog(LOG_INFO, "Unknown tag %d", + TAG_TYPE(i)); + TAG_VAL(i) = strdup("<Unknown tag>"); + break; + } + } + } + return (argp); +} + +extern table_obj tbl_prototype; + +/* + * nis_mkdir_svc() + * + * This function allows a client to "make" a directory table remotely. + * The purpose of this function is to allow for the addition of NIS+ + * directories by an application rather than by hand. See the companion + * function below, nis_rmdir_svc(). Note: It _depends_ on the fact that + * if the directory object exists, and this server is included as one + * of the valid servers, then it is allowed to create the directory. + * This means that if the addition of the directory object to the namespace + * is managed, the subsequent creation of the table will just follow along. + * It also means that if your namespace is compromised you could create + * a bogus directory. During that time you can run in "max-secure" mode + * which disables both the make and remove directory functions. + */ +nis_error * +nis_mkdir_svc(argp, rqstp) + nis_name *argp; + struct svc_req *rqstp; +{ +#define result (__nis_get_tsd()->nis_mkdir_svc_result) + directory_obj *da; + nis_object *obj; + nis_result *res; + int i; + time_t ttime; + nis_server *srvs; + nis_name s; + name_pos p; + + + /* Go look for the object for this new directory. */ + if (verbose) + syslog(LOG_INFO, "MKDIR_SVC : Creating directory : %s", *argp); + + if (readonly) { + syslog(LOG_INFO, + "nis_mkdir_svc: readonly child called to mkdir, ignored."); + result = NIS_TRYAGAIN; + return (&result); + } + + /* Check NIS_MKDIR access to parent directory */ + if (!nis_op_access("NIS_MKDIR", 0, nis_domain_of(*argp), 0, rqstp)) { + if (verbose) + syslog(LOG_INFO, + "MKDIR_SVC : authorization error on \"%s\"", + nis_domain_of(*argp)); + result = NIS_PERMISSION; + return (&result); + } + + /* + * We see if we can find the directory object for the directory + * that we are "making". If not then we abort. + * NOTE: The nis_lookup should recurse to our local version of + * __nis_core_lookup. + */ + res = nis_lookup(*argp, MASTER_ONLY); + /* Schedule this to be cleaned up when we return */ + add_cleanup(nis_freeresult, (char *)res, "mkdir lookup res"); + result = res->status; + if (result != NIS_SUCCESS) { + if (verbose) + syslog(LOG_WARNING, + "nis_mkdir_svc: could not get the directory object for %s: %s", + *argp, nis_sperrno(result)); + return (&result); + } + + obj = res->objects.objects_val; + /* + * Now we either have an object that is a directory object or + * we don't. + */ + if (__type_of(obj) != NIS_DIRECTORY_OBJ) { + result = NIS_BADOBJECT; + syslog(LOG_ERR, "nis_mkdir_svc: %s is not a directory object", + *argp); + return (&result); + } + + /* + * verify that we serve it. + */ + da = &(obj->DI_data); + s = nis_local_host(); /* optimization */ + srvs = da->do_servers.do_servers_val; + for (i = 0; i < da->do_servers.do_servers_len; i++) { + if (nis_dir_cmp(srvs[i].name, s) == SAME_NAME) + break; + } + + if (i == da->do_servers.do_servers_len) { + syslog(LOG_ERR, + "nis_mkdir_svc: Attempt to add a directory %s which we don't serve!", + *argp); + result = NIS_NOT_ME; + return (&result); + } + + p = nis_dir_cmp(*argp, __nis_rpc_domain()); + if (p != LOWER_NAME) { + syslog(LOG_ERR, + "nis_mkdir_svc: Attempt to create illegal directory %s", + *argp); + result = NIS_BADNAME; + return (&result); + } + + /* + * Now we check to see if we can read anything out of the + * directories database. + */ + result = db_find_table(*argp); + if (result == NIS_NOSUCHTABLE) { + if ((result = db_create(*argp, &tbl_prototype)) != + NIS_SUCCESS) { + syslog(LOG_ERR, "Unable to create table %s: %s.", + *argp, nis_sperrno(result)); + return (&result); + } + /* Give it a stability timestamp. */ + ttime = time(0); + make_stamp(*argp, ttime); + /* Add it to the list of directories that it serves */ + nis_server_control(SERVING_LIST, DIR_ADD, *argp); + } + if (verbose) + syslog(LOG_INFO, "Successful creation."); + + return (&result); +} + +#undef result + +/* + * nis_rmdir_svc() + * + * This is the opposite of mkdir. It will iterate through a directory + * removing all tables in that directory, and then remove the directory + * itself. Beware recursing on yourself, since if this function makes + * a request to this server you will deadlock. + * + * Note, rmdir is a little weird. If we are removing the directory + * completely, (i.e out of the name space) then there won't be a + * directory object to verify it. The easy case is if we are just + * getting dropped of the list of servers that serve it. + */ +nis_error * +nis_rmdir_svc(argp, rqstp) + nis_name *argp; + struct svc_req *rqstp; +{ +#define result (__nis_get_tsd()->nis_rmdir_svc_result) + directory_obj *da; /* Temp pointing to our data */ + int i; /* Loop counter */ + nis_name s; /* Another temporary */ + nis_server *srvs; /* Another temporary */ + nis_result *res; /* Result of nis lookup */ + nis_object *obj; /* object temporary */ + nis_db_result *dbres, /* Result db lookup */ + *rrs; /* From removing entries */ + nis_fn_result *fnr, /* Result of iteration */ + *tlist; + char namebuf[1024]; /* buffer where we cons name */ + nis_error error; + int xid; + int complete_removal = 1; + char *principal = 0; + + /* Go look for the object for this new directory. */ + if (verbose) + syslog(LOG_INFO, "RMDIR_SVC : Removing directory : %s", *argp); + + if (readonly) { + syslog(LOG_INFO, + "nis_rmdir_svc: readonly child called to rmdir, ignored."); + result = NIS_TRYAGAIN; + return (&result); + } + + /* + * If 'rqstp' is NULL, we've been called internally, and supply the + * local principal. + */ + if (rqstp == 0) + principal = nis_local_principal(); + + /* Check NIS_RMDIR access to directory */ + if (!nis_op_access("NIS_RMDIR", 0, *argp, principal, rqstp)) { + if (verbose) + syslog(LOG_INFO, + "RMDIR_SVC : authorization error on \"%s\"", *argp); + result = NIS_PERMISSION; + return (&result); + } + + /* + * First we get the directory object. If it doesn't exist we consider + * that to be an ok error since we could have been its only server etc. + */ + res = nis_lookup(*argp, MASTER_ONLY); + + /* schedule a cleanup of these results */ + result = res->status; + obj = res->objects.objects_val; + + if ((result != NIS_SUCCESS) && (result != NIS_NOTFOUND)) + return (&result); + + /* + * Now determine whether or not the object we lookup up is a + * directory and that we no longer serve it. + */ + if (res->status == NIS_SUCCESS) { + if (__type_of(obj) != NIS_DIRECTORY_OBJ) { + result = NIS_BADOBJECT; + return (&result); + } + + da = &(obj->DI_data); + s = nis_local_host(); /* optimization */ + srvs = da->do_servers.do_servers_val; + for (i = 0; i < da->do_servers.do_servers_len; i++) { + if (nis_dir_cmp(srvs[i].name, s) == SAME_NAME) { + syslog(LOG_ERR, + "nis_rmdir_svc: Attempt to remove directory %s which we still serve!", + *argp); + result = NIS_NOT_ME; + return (&result); + } + } + /* + * A complete directory removal works by first removing the + * directory object from the name space and then doing + * individual 'rmdir' operations on each server. + * + * If directory object still exists, that means we are not + * doing a complete removal and hence cannot be its master. + * + * If directory object does not exist, we act as if we are + * master (because we cannot tell without the directory obj) + * and abort the removal if it contains subdirectories. + * Continuing with the removal in that case would leave + * the subdirectories orphaned and inaccessible. + * + * Note that we cannot tell if it is a replica trying + * to clean up after the directory has already been removed + * earlier, or it is doing a rmdir at the time or the remove. + */ + complete_removal = 0; + } + + /* + * Now, we definitely know that the directory needs to be deleted. + * So we attempt the actual removal of the directory. We wish to remove + * every object and all tables in the directory. To accomplish this + * we need to iterate over the entire contents. This is accomplished + * by calling db_firstib() and if the object returned is a table, we + * delete the table contents. The entire operation is bracketed in + * a transaction to allow us to abort it if something goes wrong. + */ + fnr = db_firstib(*argp, 0, NULL, + FN_NORAGS+FN_NOMANGLE+FN_NOERROR, NULL); + result = NIS_SUCCESS; + if (fnr->status == NIS_NOSUCHTABLE) { + XFREE(fnr); + return (&result); + } else if (fnr->status == NIS_NOTFOUND) { + /* table is empty */ + if ((result = db_destroy(*argp)) != NIS_SUCCESS) { + syslog(LOG_WARNING, + "nis_rmdir_svc: Could not remove directory table %s: %s.", + *argp, nis_sperrno(result)); + result = NIS_S_SUCCESS; + } + make_stamp(*argp, 0); /* Make a tombstone. */ + XFREE(fnr); + rmdir_update(*argp); + return (&result); + } else if (fnr->status != NIS_SUCCESS) { + result = fnr->status; + XFREE(fnr); + return (&result); + } + error = NIS_SUCCESS; + xid = begin_transaction(nis_local_principal()); + if (! xid) { + XFREE(fnr->cookie.n_bytes); + nis_destroy_object(fnr->obj); + XFREE(fnr); + result = NIS_TRYAGAIN; + return (&result); + } + while (fnr->status == NIS_SUCCESS) { + sprintf(namebuf, "%s.%s", fnr->obj->zo_name, + fnr->obj->zo_domain); + /* + * If we are getting rid of a directory with subdirectories, + * abort. + */ + if (__type_of(fnr->obj) == NIS_DIRECTORY_OBJ && + complete_removal) { + error = NIS_NOTEMPTY; + break; + } + if (__type_of(fnr->obj) == NIS_TABLE_OBJ) { + tlist = db_firstib(namebuf, 0, NULL, + FN_NORAGS+FN_NOERROR, NULL); + while (tlist->status == NIS_SUCCESS) { + struct obj_list ol; + + ol.o = tlist->obj; + ol.r = 1; + /* free this since we won't be using it */ + rrs = db_remib(namebuf, 0, NULL, &ol, 1, + fnr->obj, (ulong_t)time(0)); + if (rrs->status != NIS_SUCCESS) { + error = NIS_FAIL; + break; + } + /* + * Note : We call firstib again, rather than + * calling nextib because our removing + * the entry may have screwed up the + * internal database ptrs. db_firstib() + * always works. + */ + XFREE(tlist->cookie.n_bytes); + nis_destroy_object(tlist->obj); + XFREE(tlist); + tlist = db_firstib(namebuf, 0, NULL, + FN_NORAGS+FN_NOERROR, NULL); + } + if (tlist->status == NIS_SUCCESS) { + XFREE(tlist->cookie.n_bytes); + nis_destroy_object(tlist->obj); + } + if (tlist->status != NIS_NOTFOUND) + error = NIS_FAIL; + XFREE(tlist); + } + if (error != NIS_SUCCESS) + break; + /* This remove will also destroy the table */ + dbres = db_remove(namebuf, fnr->obj, (ulong_t)time(0)); + if (dbres->status != NIS_SUCCESS) { + error = dbres->status; + break; + } + /* free this because we aren't going to call nextib */ + XFREE(fnr->cookie.n_bytes); + nis_destroy_object(fnr->obj); + XFREE(fnr); + fnr = db_firstib(*argp, 0, NULL, FN_NORAGS+FN_NOERROR, NULL); + } + if (fnr->status == NIS_SUCCESS) { + XFREE(fnr->cookie.n_bytes); + nis_destroy_object(fnr->obj); + } + if (error != NIS_SUCCESS) { + syslog(LOG_ERR, + "nis_rmdir_svc: Could not remove directory table %s: %s.", + *argp, nis_sperrno(error)); + abort_transaction(xid); + result = error; + } else { + end_transaction(xid); + if ((result = db_destroy(*argp)) != NIS_SUCCESS) { + syslog(LOG_WARNING, + "nis_rmdir_svc: Could not remove directory table %s: %s.", + *argp, nis_sperrno(result)); + result = NIS_S_SUCCESS; + } + make_stamp(*argp, 0); /* Make a tombstone. */ + rmdir_update(*argp); + } + + XFREE(fnr); + return (&result); +} + +#undef result + +/* + * If we serve the parent directory or the directory is + * the root, then do an update of that directory so that + * we see the change to the parent directory or root + * object. If we don't do this, then we will continue + * to see the removed directory in the database until + * the master server pings us. + */ +static +void +rmdir_update(name) + char *name; +{ + nis_server *srv; + nis_object *rootobj = get_root_object(); + nis_object *d_obj = NULL; + nis_result *lres = NULL; + char *parent = nis_domain_of(name); + + if (rootobj && + nis_dir_cmp(rootobj->DI_data.do_name, name) == SAME_NAME) { + parent = name; + d_obj = rootobj; + } else if (parent) { + lres = nis_lookup(parent, MASTER_ONLY); + if (lres && lres->status == NIS_SUCCESS) { + d_obj = lres->objects.objects_val; + if (__type_of(d_obj) != DIRECTORY_OBJ) + d_obj = NULL; + } + } + + /* if we can't get the directory object, we can't do an update */ + if (d_obj == NULL) + goto skip_update; + + /* + * If we are running on the master server, or if we don't + * serve the parent directory, then skip the update. + */ + srv = &d_obj->DI_data.do_servers.do_servers_val[0]; + if (nis_dir_cmp(srv->name, nis_local_host()) == SAME_NAME || + nis_server_control(SERVING_LIST, DIR_SERVED, parent) == 0) + goto skip_update; + + /* + * If we get here, then we are a replica and we need to update + * the parent directory. + */ + if (d_obj == rootobj) { + /* + * Updating root object. + */ + root_replica_update(); + } else { + /* + * Use a very large timestamp to force an + * update to the directory. + */ + add_pingitem_with_name(parent, d_obj, 0xffffffff, &upd_list); + } + +skip_update: + if (rootobj) + nis_destroy_object(rootobj); + if (lres) + nis_freeresult(lres); + + /* Delete name from the list of directories that we serve */ + nis_server_control(SERVING_LIST, DIR_DELETE, name); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nisinit.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nisinit.c new file mode 100644 index 0000000000..e292596653 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nisinit.c @@ -0,0 +1,746 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisinit.c + * + * Copyright (c) 1991-1999 by Sun Microsystems, Inc. + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module contains the nis initialization code. It is designed to + * initialize a client or server. The server can be a master server or + * a slave server (replicate). NOTE: This file defines what the "psuedo" + * tables look like when defined by the server. This means that this + * module and the nis_db.c module must track each other. This module + * gets linked to nis_db.o and the database library. + * + * This is version 3 of this file, most of it has been taken over + * by nismkdir and the service itself. + * This is version 2 of this file, version 1 was a major hack this + * is more structured but still pretty hackish. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/fcntl.h> +#include <string.h> +#include <rpc/rpc.h> +#include <rpc/xdr.h> +#include <netdb.h> +#include <netdir.h> +#include <netconfig.h> +#include <nsswitch.h> +#include <sys/socket.h> +#include <rpcsvc/nis.h> +#include <netinet/in.h> +#include "nis_svc.h" +#include "nis_proc.h" + +#define COLDSTART "/var/nis/NIS_COLD_START" + +extern void __nis_netconfig2ep(struct netconfig *, endpoint *); + +extern int errno; +extern int optind; /* getopt counter */ +extern char *optarg; /* getopt pointer */ + +directory_obj bc_dir; +char valid_dir_obj = 0; +char *local_dir; +char *secure_dir; + +/* + * get_server() + * + * This function constructs a local server description of the current + * server and returns it as a nis_server structure. This is then added + * to the list of servers who serve this directory. + */ +nis_server * +get_server(host) + char *host; +{ +#define INC_SIZE 512 + int myaddr_size = INC_SIZE; + endpoint *myaddr; + static nis_server myself; + char hname[256]; + int num_ep = 0, i; + struct netconfig *nc; + void *nch; + struct nd_hostserv hs; + struct nd_addrlist *addrs; + + myaddr = (endpoint *) calloc(myaddr_size, sizeof (endpoint)); + if (myaddr == NULL) { + fprintf(stderr, "Error: out of memory\n"); + exit(1); + } + gethostname(hname, 256); + hs.h_host = (host) ? host : hname; + hs.h_serv = "rpcbind"; + nch = setnetconfig(); + while (nc = getnetconfig(nch)) { + if (! netdir_getbyname(nc, &hs, &addrs)) { + for (i = 0; i < addrs->n_cnt; i++, num_ep++) { + if (num_ep == myaddr_size) { + myaddr_size += INC_SIZE; + myaddr = (endpoint *) realloc( + (void *)myaddr, + myaddr_size * sizeof (endpoint)); + if (myaddr == NULL) { + fprintf(stderr, + "Error: out of memory\n"); + exit(1); + } + } + myaddr[num_ep].uaddr = + taddr2uaddr(nc, &(addrs->n_addrs[i])); + __nis_netconfig2ep(nc, &(myaddr[num_ep])); + } + netdir_free((char *)addrs, ND_ADDRLIST); + } + } + endnetconfig(nch); + + if (! num_ep) { + fprintf(stderr, + "\nError: Unable to construct an address from name '%s'\n", + hs.h_host); + fprintf(stderr, + "Please check that this hostname exists in /etc/hosts\n"); + exit(1); + } + myself.name = (host) ? strdup(host) : strdup(nis_local_host()); + myself.ep.ep_len = num_ep; + myself.ep.ep_val = &myaddr[0]; + myself.key_type = NIS_PK_NONE; + myself.pkey.n_bytes = NULL; + myself.pkey.n_len = 0; + return (&myself); +} + +/* + * This function initializes a directory object. It is used when + * creating the object for master servers, and for creating a + * prototype coldstart file which is then used to build the + * real coldstart file. It returns false if it couldn't get + * a nis_server structure for the passed hostname. If this + * name is a null pointer it builds the directory object for + * the local host and cannot fail. + */ +void +init_data(dd, host) + directory_obj *dd; /* Directory data. */ + char *host; /* Optional host */ +{ + dd->do_type = NIS; + dd->do_name = strdup(local_dir); + dd->do_servers.do_servers_len = 1; + dd->do_servers.do_servers_val = get_server(host); + dd->do_ttl = 12*60*60; /* this should track the object */ + dd->do_armask.do_armask_len = 0; + dd->do_armask.do_armask_val = NULL; +} + +static +int +decode_dir(res) + fd_result *res; +{ + int stat; + XDR mem; + + if (res->status != NIS_SUCCESS) + return (0); + + memset((char *)&bc_dir, 0, sizeof (bc_dir)); + xdrmem_create(&mem, res->dir_data.dir_data_val, + res->dir_data.dir_data_len, XDR_DECODE); + stat = xdr_directory_obj(&mem, &bc_dir); + if (! stat) { + fprintf(stderr, + "\nError: Couldn't decode returned object.\n"); + return (0); + } + if (nis_dir_cmp(local_dir, bc_dir.do_name) != SAME_NAME) { + xdr_free(xdr_directory_obj, (char *)&bc_dir); + return (0); + } + + valid_dir_obj = 1; + return (1); +} + +/* + * The bc_init_data() function is called by the callback function of + * the RPC broadcast function. + */ +bool_t +bc_init_data(x, haddr, nc) + caddr_t x; + struct netbuf *haddr; /* host that answered : UNUSED */ + struct netconfig *nc; /* its endpoint: UNUSED */ +{ + fd_result *res = (fd_result *) x; + + return (decode_dir(res)); +} + +static +int +check_coldstart(dir) + char *dir; +{ + directory_obj dobj; + + if (!__readColdStartFile(&dobj)) + return (1); + + if (nis_dir_cmp(dobj.do_name, dir) != SAME_NAME) { + return (0); + } + + return (1); +} + +void +usage(s) + char *s; +{ + fprintf(stderr, + "usage: \t%s [-k key_domain] -c -H host|-B|-C coldstart\n", s); + fprintf(stderr, "\t%s -r\n", s); + fprintf(stderr, "\t%s -p Y|D|N parent_domain host ...\n", s); + exit(1); +} + +enum make_op {MAKE_NONE, MAKE_ROOT, MAKE_PARENT, MAKE_CLIENT }; +enum obj_src {SRC_NONE, SRC_BCAST, SRC_MCAST, SRC_CSTART, SRC_HNAME }; + +void +main(argc, argv) + int argc; + char *argv[]; +{ + enum clnt_stat dummy; /* rc for rpc_broadcast() */ + int c; /* Option character */ + enum name_pos pos; + uint32_t exptime; + struct stat s; + char buf[1024]; + char *directory = nis_local_directory(), + *hostname = nis_local_host(), + *coldstart = NULL; + enum make_op op = MAKE_NONE; + enum obj_src src = SRC_NONE; + nstype ns_type = SUNYP; + nis_object d_obj, *n_obj; + int sfd, dfd, i, status, nhosts = 1; + directory_obj *d_data; + nis_server *srvs; + unsigned char a1, a2, a3, a4; + struct hostent *he; + struct in6_addr addr; + nis_result *res; + fd_args fdarg; + fd_result fdres; + int ss; + char *tmpp; + FILE *fw; + struct __nsw_switchconfig *conf; + enum __nsw_parse_err perr; + int print_warn = 0; + int heerr; + sa_family_t af; + + /* + * Make sure that files created by stdio don't + * have extra permission. We allow group and world + * read because the files and directories we create + * need to be world readable. + */ + (void) umask(022); + + memset((char *)(&d_obj), 0, sizeof (nis_object)); + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as superuser.\n"); + exit(1); + } + + local_dir = nis_local_directory(); + + /* + * By default Solaris2.0/SunOS 5.0 is setup as an NIS (YP) client. + * In order to use NIS+, publickeys must be gotten from NIS+ if + * NIS+ is running in secure mode. + */ + conf = __nsw_getconfig("publickey", &perr); + if ((conf == NULL) || (conf->lookups == NULL)) { + fprintf(stderr, + "Warning: There is no publickey entry in %s.\n", + __NSW_CONFIG_FILE); + fprintf(stderr, + "The default publickey policy is \"publickey: nis\".\n"); + print_warn = 1; + } else if ((strcmp(conf->lookups->service_name, "nisplus") != 0) || + (conf->lookups->next != NULL)) { + struct __nsw_lookup *look; + + fprintf(stderr, + "Warning: The publickey entry in %s is \"publickey: ", + __NSW_CONFIG_FILE); + for (look = conf->lookups; look; look = look->next) { + fprintf(stderr, "%s", look->service_name); + if (look->next) + fprintf(stderr, " "); + } + fprintf(stderr, "\"\n"); + print_warn = 1; + } + + if (print_warn == 1) { + fprintf(stderr, + "In order to use NIS+, it should be \"publickey: nisplus\".\n"); + fprintf(stderr, +"For more information, see secure_rpc(3N), publickey(3N), & nisaddcred(1).\n"); + } + + op = MAKE_NONE; + src = SRC_NONE; + while ((c = getopt(argc, argv, "rck:p:C:BMH:")) != -1) { + switch (c) { + case 'k': + secure_dir = optarg; + break; + case 'r' : + if (op != MAKE_NONE) + usage(argv[0]); + op = MAKE_ROOT; + break; + case 'c': + if (op != MAKE_NONE) + usage(argv[0]); + op = MAKE_CLIENT; + break; + case 'p' : + if (op != MAKE_NONE) + usage(argv[0]); + if (*optarg == '-') /* skip '-' character */ + optarg++; + switch (*optarg) { + case 'Y' : + ns_type = SUNYP; + break; + case 'D' : + ns_type = DNS; + break; + case 'N' : + ns_type = NIS; + break; + default : + fprintf(stderr, + "unrecognized name service type, use one of:\n"); + fprintf(stderr, + " Y = Sun YP\n D = Domain Name Service\n"); + fprintf(stderr, + " N = Sun NIS+\n"); + usage(argv[0]); + } + op = MAKE_PARENT; + break; + case 'C' : + if (src != SRC_NONE) + usage(argv[0]); + src = SRC_CSTART; + coldstart = optarg; + break; + case 'H' : + if (src != SRC_NONE) + usage(argv[0]); + src = SRC_HNAME; + hostname = optarg; + break; + case 'B' : + if (src != SRC_NONE) + usage(argv[0]); + src = SRC_BCAST; + break; +#ifdef NIS_MCAST_SUPPORT + case 'M' : + if (src != SRC_NONE) + usage(argv[0]); + src = SRC_MCAST; + break; +#endif + case '?' : + default : + usage(argv[0]); + break; + } + } + + if (op == MAKE_NONE) { + fprintf(stderr, "One of -c, -r or -p must be specified.\n"); + usage(argv[0]); + } + + /* Only making the parent requires extra data (host name) */ + if ((optind < argc) && (op != MAKE_PARENT)) { + fprintf(stderr, + "Error: Extra input at the end of the command.\n"); + usage(argv[0]); + } else if ((optind <= argc) && (op == MAKE_PARENT)) { + if ((argc - optind) < 2) { + fprintf(stderr, +"Error: Make parent (-p) requires parent domain and at least one host.\n"); + usage(argv[0]); + } + directory = argv[optind++]; + nhosts = argc - optind; + } + + /* /var/nis directory should exist. If not, create it. */ + strcpy(buf, nis_data(NULL)); + tmpp = strrchr(buf, '/'); + *tmpp = NULL; + ss = stat(buf, &s); + if (ss == -1 && errno == ENOENT) { + if (mkdir(buf, 0755)) { + perror("mkdir"); + fprintf(stderr, "Error: Unable to create \"%s\"\n", + buf); + exit(1); + } + } else if (ss == -1) { + perror("stat"); + fprintf(stderr, "Error: Unable to stat \"%s\"\n", buf); + exit(1); + } + + /* + * For root and parent operations, the /var/nis directory should + * exist with the correct permissions. + */ + switch (op) { + case MAKE_ROOT: + case MAKE_PARENT: + strcpy(buf, nis_data(NULL)); + ss = stat(buf, &s); + if (ss == -1 && errno == ENOENT) { + if (op == MAKE_PARENT) { + fprintf(stderr, +"Error: Parent object cannot be created before creating a root object.\n"); + exit(1); + } + if (mkdir(buf, 0744)) { + perror("mkdir"); + fprintf(stderr, "Error: Unable to create " + "\"%s\"\n", buf); + exit(1); + } + } else if (ss == -1) { + perror("stat"); + fprintf(stderr, "Error: Unable to stat \"%s\"\n", buf); + exit(1); + } else { + if ((s.st_mode & 0777) != 0744) { + fprintf(stderr, + "Warning: Bad permissions (%o) on the %s directory.\n", + s.st_mode & 0777, buf); + fprintf(stderr, "Should have been 744\n\n"); + } + if (op == MAKE_ROOT) + /* Some data is already in this directory */ + fprintf(stderr, + "Warning: Old data exists under " + "the \"%s\" directory.\n\n", buf); + else { + /* make sure that the root_object is there */ + ss = stat(nis_data(ROOT_OBJ), &s); + if (ss == -1 && errno == ENOENT) { + fprintf(stderr, +"Error: Parent object cannot be created before creating a root object.\n"); + exit(1); + } else if (ss == -1) { + perror("stat"); + fprintf(stderr, + "Error: Unable to stat \"%s\"\n", + nis_data(ROOT_OBJ)); + exit(1); + } + } + } + } + + printf( + "This machine is in the \"%s\" NIS+ domain.\n", local_dir); + switch (op) { + case MAKE_ROOT : + printf("Setting up root server ...\n"); + /* + * Step 0. See if we have the info we need. + */ + + /* + * Step 1. Create Directory object + * Since we are the master server, the d_obj should + * not pre-exist. + */ + d_obj.zo_oid.ctime = time(0); + d_obj.zo_name = strdup(nis_leaf_of(directory)); + d_obj.zo_domain = nis_domain_of(directory); + d_obj.zo_owner = nis_local_principal(); + d_obj.zo_group = nis_local_group(); + /* Make this object readable by nobody */ + d_obj.zo_access = DEFAULT_RIGHTS | (NIS_READ_ACC << 24); + if (*d_obj.zo_group != NULL) + d_obj.zo_access |= ((NIS_READ_ACC + + NIS_MODIFY_ACC + NIS_CREATE_ACC + + NIS_DESTROY_ACC) << 8); + d_obj.zo_ttl = 24 * 60 * 60; + d_obj.zo_data.zo_type = NIS_DIRECTORY_OBJ; + d_data = &(d_obj.DI_data); + init_data(d_data, (char *)NULL); + strcpy(buf, nis_data(ROOT_OBJ)); + status = nis_write_obj(buf, &d_obj); + if (! status) { + fprintf(stderr, + "\nError: Unable to write root object.\n"); + exit(1); + } + /* Write cold start file */ + __nis_CacheRestart(); /* in case cachemgr running */ + unlink(COLDSTART); + writeColdStartFile(d_data); + __nis_CacheRestart(); + + /* Create the serving list file */ + strcpy(buf, nis_data("serving_list")); + fw = fopen(buf, "a+"); + if (fw == NULL) { + fprintf(stderr, + "\nERROR: could not open file \"%s\" for storing directories served.\n", + buf); + exit(1); + } + fprintf(fw, "%s\n", directory); + fclose(fw); + + break; + case MAKE_PARENT : + printf("Setting up parent object ...\n"); + /* + * Step 1. Create Directory object + * Since we are the master server, the d_obj should + * not pre-exist. + */ + strcpy(buf, nis_domain_of(local_dir)); + d_obj.zo_name = strdup(nis_leaf_of(buf)); + d_obj.zo_domain = strdup(nis_domain_of(buf)); + d_obj.zo_owner = strdup(nis_local_principal()); + d_obj.zo_group = ""; + d_obj.zo_access = 0x01010101; /* r---r---r---r--- */ + d_obj.zo_ttl = 7*24*3600; + + d_obj.zo_data.zo_type = NIS_DIRECTORY_OBJ; + d_data = &(d_obj.DI_data); + d_data->do_type = ns_type; + d_data->do_name = directory; + d_data->do_servers.do_servers_len = nhosts; + d_data->do_servers.do_servers_val = (nis_server *) + calloc(nhosts, sizeof (nis_server)); + srvs = d_data->do_servers.do_servers_val; + for (i = 0; i < nhosts; i++) { + extern char *inet_ntoa(struct in_addr); + srvs[i].name = strdup(argv[optind + i]); + srvs[i].key_type = NIS_PK_NONE; + srvs[i].ep.ep_len = 1; + srvs[i].ep.ep_val = (endpoint *) + calloc(1, sizeof (endpoint)); + he = getipnodebyname(srvs[i].name, AF_INET6, + AI_DEFAULT, &heerr); + if (! he) { + fprintf(stderr, + "\nError: Couldn't locate address information for \"%s\".\n", + srvs[i].name); + exit(1); + } + addr = + ((struct sockaddr_in6 *)(he->h_addr_list[0]))->sin6_addr; + if (IN6_IS_ADDR_V4MAPPED(&addr)) { + struct in_addr in4; + af = AF_INET; + IN6_V4MAPPED_TO_INADDR(&addr, &in4); + strcpy(buf, inet_ntoa(in4)); + } else { + af = AF_INET6; + (void) inet_ntop(AF_INET6, &addr, + buf, sizeof (buf)); + } + switch (d_data->do_type) { + case NIS: + case SUNYP: + strcat(buf, ".0.111"); + break; + case DNS: + strcat(buf, ".0.53"); + break; + } + srvs[i].ep.ep_val->uaddr = strdup(buf); + srvs[i].ep.ep_val->family = + (af == AF_INET6) ? NC_INET6 : NC_INET; + if (d_data->do_type != NIS) + srvs[i].ep.ep_val->proto = + (af == AF_INET6) ? "udp6" : "udp"; + else + srvs[i].ep.ep_val->proto = + (af == AF_INET6) ? "tcp6" : "tcp"; + } + d_data->do_ttl = d_obj.zo_ttl; + d_data->do_armask.do_armask_len = 0; + d_data->do_armask.do_armask_val = NULL; + strcpy(buf, nis_data(PARENT_OBJ)); + status = nis_write_obj(buf, &d_obj); + if (! status) { + fprintf(stderr, + "\nError: Unable to write parent object.\n"); + exit(1); + } + break; + case MAKE_CLIENT : + printf("Setting up NIS+ client ...\n"); + if (secure_dir == NULL) { + if (!check_coldstart(local_dir)) { + fprintf(stderr, + "\nError: system domain name doesn't match that stored in \"%s\"\n", + COLDSTART); + fprintf(stderr, + " use the -k option to specify the domain where root's"); + fprintf(stderr, + " key is stored.\n"); + exit(1); + } + } else { + pos = nis_dir_cmp(local_dir, secure_dir); + if (pos != SAME_NAME && pos != LOWER_NAME) { + fprintf(stderr, + "\nError: system domain name must be the same as or lower than the\n"); + fprintf(stderr, + " domain in which root's key is stored.\n"); + exit(1); + } + local_dir = secure_dir; + } + (void) unlink(COLDSTART); /* just in case */ + switch (src) { + case SRC_NONE : + fprintf(stderr, + "\nError: Missing source for client setup.\n"); + usage(argv[0]); + break; + case SRC_HNAME : + d_data = &(d_obj.DI_data); + init_data(d_data, hostname); + writeColdStartFile(d_data); + break; + case SRC_CSTART : + sfd = open(coldstart, O_RDONLY, 0); + if (sfd == -1) { + fprintf(stderr, + "\nError: Can't open file \"%s\" " + "for reading.\n", coldstart); + exit(1); + } + dfd = open(COLDSTART, O_WRONLY+O_CREAT, 0644); + if (dfd == -1) { + fprintf(stderr, + "\nError: Can't open file \"%s\" " + "for writing.\n", COLDSTART); + exit(1); + } + while ((i = read(sfd, buf, 1024)) > 0) + write(dfd, buf, i); + close(dfd); + close(sfd); + break; + case SRC_BCAST : + fdarg.dir_name = local_dir; + fdarg.requester = "broadcast"; + memset((char *)&fdres, 0, sizeof (fdres)); + dummy = rpc_broadcast(NIS_PROG, NIS_VERSION, + NIS_FINDDIRECTORY, + xdr_fd_args, (char *)&fdarg, + xdr_fd_result, (char *)&fdres, + (resultproc_t)bc_init_data, + NULL); + if ((fdres.status == NIS_SUCCESS) && + (dummy == RPC_SUCCESS) && + (valid_dir_obj)) + writeColdStartFile(&bc_dir); + else { + fprintf(stderr, + "\nError: No servers responding, use -H option \n"); + exit(1); + } + break; + case SRC_MCAST : + break; + default : + break; + } + + /* + * At this point there should be something of a + * coldstart file in the /var/nis directory. If + * not then we're hosed. + * + * We switch to a local cache so that there is + * no conflict if nis_cachemgr is still running + * with a copy of an old domain name. + */ + __nis_CacheLocalInit(&exptime); + unlink(COLDSTART); + res = nis_lookup(local_dir, NO_AUTHINFO); + if (res->status != NIS_SUCCESS) { + fprintf(stderr, + "\nError: Could not create a valid NIS+ coldstart file\n"); + nis_perror(res->status, local_dir); + exit(1); + } + n_obj = res->objects.objects_val; + writeColdStartFile(&(n_obj->DI_data)); + __nis_CacheRestart(); + printf("All done.\n"); + exit(0); + default : + printf("\n"); + usage(argv[0]); + } + printf("All done.\n"); + exit(0); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nisldapmaptest.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nisldapmaptest.c new file mode 100644 index 0000000000..f81e534b77 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nisldapmaptest.c @@ -0,0 +1,389 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <strings.h> +#include <malloc.h> + +#include <lber.h> +#include <ldap.h> + +#include <sys/param.h> +#include <rpcsvc/nis.h> +#include "nis_proc.h" +#include "nis_ldap.h" + +#include "ldap_util.h" +#include "ldap_structs.h" +#include "ldap_print.h" +#include "ldap_attr.h" +#include "ldap_nisdbquery.h" +#include "ldap_ruleval.h" +#include "ldap_op.h" +#include "ldap_map.h" +#include "ldap_nisplus.h" + +#include "nis_parse_ldap_conf.h" + + +/* Avoid having to link in nis_cleanup.o */ +void +do_cleanup(cleanup *stuff) { +} + +/* Parser assumes existence of 'verbose' */ +int verbose = 0; +/* ... as well as 'cons' */ +FILE *cons = stdout; + +/* 'justTesting' means we can live with some fudging */ +int justTesting = 0; + +/* 'setColumnsDuringConfig' is OK for us */ +int setColumnsDuringConfig = 1; + + +void +usage(char *prog) { + fprintf(stderr, + "Usage: %s [ -s | -r | -d ] ", prog); + fprintf(stderr, "[ -l | -t table ] [ -v ] "); + fprintf(stderr, "[ -i ] [ -o ] "); + fprintf(stderr, "[ -m conffile ] "); + fprintf(stderr, "[ -x attr=val ... ] "); + fprintf(stderr, "[ col=val ...]\n"); +} + +int +main(int argc, char *argv[]) { + + int ret, i, j, numVals, numEntries = 0, nq = 0; + __nis_table_mapping_t *t; + db_query **q, **qr; + int modify = 0, delete = 0, list = 0, asObj = 0; + char *myself = "main"; + char *table = 0; + int freeTable = 0; + char *ldapConfFile = 0; + char **ldapCLA = 0; + int numLA = 0; + int c, saveVerbose; + __nis_obj_attr_t *attr; + entry_obj **ea = 0; + int numEa; + + while ((c = getopt(argc, argv, "srdlviom:x:t:")) != -1) { + switch (c) { + case 's': + /* Search */ + modify = 0; + delete = 0; + break; + case 'r': + /* Replace/Modify/Add */ + modify = 1; + delete = 0; + break; + case 'd': + /* Delete */ + modify = 0; + delete = 1; + break; + case 'l': + /* List parser structures */ + list = 1; + break; + case 'v': + /* Verbose mode */ + verbose = 1; + break; + case 'i': + /* + * Allow fudging (i.e., guessing) at information + * that can't be retrieved (such as column names). + */ + justTesting = 1; + break; + case 'o': + /* Work on object; affects tables */ + asObj = 1; + break; + case 'm': + /* Config file name */ + ldapConfFile = optarg; + break; + case 'x': + /* Attribute assignment */ + ldapCLA = realloc(ldapCLA, + (numLA + 2) * sizeof (ldapCLA[0])); + if (ldapCLA == 0) { + fprintf(stderr, + "Out of memory. realloc(%d) => NULL\n", + (numLA + 2) * sizeof (ldapCLA[0])); + return (-1); + } + ldapCLA[numLA++] = optarg; + ldapCLA[numLA] = 0; + break; + case 't': + /* NIS+ object */ + table = optarg; + break; + case '?': + default: + usage(argv[0]); + return (-1); + } + } + + /* + * Make /var/nis our CWD, so that config files without a dir + * path get the correct default. + */ +#define OURCWD "/var/nis" + if (chdir(OURCWD) != 0) { + fprintf(stderr, "Failure setting CWD to "); + perror(OURCWD); + } + + /* + * If 'verbose' is on, the parser spits out a listing of the + * mapping config file. However, since nisldapmaptest has an + * option specifically for that purpose, we unset 'verbose' + * while parsing. + */ + saveVerbose = verbose; + verbose = 0; + ret = parseConfig(ldapCLA, ldapConfFile); + verbose = saveVerbose; + if (ret == 1) { + fprintf(stderr, "Mapping inactive\n"); + return (1); + } else if (ret != 0) { + fprintf(stderr, "Parse error for \"%s\" => %d\n", + ldapConfFile ? ldapConfFile : + "/var/nis/NIS+LDAPmapping", + ret); + return (ret); + } + + if (list) { + /* Don't bother locking the table for traversal */ + for (t = (__nis_table_mapping_t *)ldapMappingList.first; + t != 0; + t = (__nis_table_mapping_t *)t->item.nxt_item) { + __nis_table_mapping_t *m; + for (m = t; m != 0; m = m->next) { + printTableMapping(m); + printbuf(); + } + } + } + + if (table != 0) { + int len = 0; + char *objPath = 0; + + table = fullObjName(F, table); + if (table != 0) { + freeTable = 1; + len = strlen(table); + objPath = calloc(1, len + MAXPATHLEN + 1); + } + if (table == 0 || objPath == 0 || + internal_table_name(table, objPath) == 0) { + fprintf(stderr, + "Unable to obtain internal object name for \"%s\"\n", + table); + if (freeTable) + free(table); + return (-1); + } + t = __nis_find_item_mt(objPath, &ldapMappingList, 1, 0); + if (t == 0) { + fprintf(stderr, "No mapping for \"%s\" (%s)\n", + table, NIL(objPath)); + if (objPath != 0) + free(objPath); + if (freeTable) + free(table); + return (-1); + } + if (objPath != 0) + free(objPath); + } else { + if (list) + exit(0); + usage(argv[0]); + exit(-1); + } + + if (asObj || t->objType != NIS_TABLE_OBJ) { + char *op; + nis_object *no = 0; + + /* + * Work on object (even if it's a table); ignore excess + * arguments + */ + if (modify) { + ret = objToLDAP(t, 0, 0, 0); + op = "objToLDAP"; + } else if (delete) { + ret = deleteLDAPobj(t); + op = "deleteLDAPobj"; + } else { + ret = objFromLDAP(t, &no, &ea, &numEa); + op = "objFromLDAP"; + } + if (ret == LDAP_SUCCESS) { + if (no != 0) { + nis_print_object(no); + if (no->zo_data.zo_type == NIS_DIRECTORY_OBJ && + ea != 0) { + p2buf(myself, "Directory entries:\n"); + for (i = 0; i < numEa; i++) { + p2buf(myself, "\t"); + sc2buf(myself, + ea[i]->en_cols.en_cols_val[1].ec_value.ec_value_val, + ea[i]->en_cols.en_cols_val[1].ec_value.ec_value_len); + p2buf(myself, "\n"); + } + } + nis_destroy_object(no); + } + if (ea != 0) + freeEntryObjArray(ea, numEa); + } else { + p2buf(myself, "%s(\"%s\") => %d (%s)\n", + op, t->objName, ret, + ldap_err2string(ret)); + } + printbuf(); + } else if (optind < argc) { + __nis_rule_value_t *rv = 0; + int nv = 0; + __nis_obj_attr_t **attr = 0; + /* + * Non-option arguments specify column names/values in + * "name=value" format. + */ + q = createQuery(argc-optind, &argv[optind], t, + (modify || delete) ? &rv : 0, &nv); + if (q == 0) { + p2buf(myself, "Unable to create LDAP %s parameters\n", + modify ? "modify" : + (delete ? "delete" : "search")); + if (freeTable) + free(table); + exit(-1); + } + if (modify) { + ret = mapToLDAP(t, nv, 0, q, rv, 0, 0); + if (ret != LDAP_SUCCESS) { + p2buf(myself, + "mapToLDAP(<modify>) => %d (%s)\n", + ret, ldap_err2string(ret)); + } + } else if (delete) { + ret = mapToLDAP(t, nv, q, 0, rv, 0, 0); + if (ret != LDAP_SUCCESS) { + p2buf(myself, + "mapToLDAP(<delete>) => %d (%s)\n", + ret, ldap_err2string(ret)); + } + } else { + qr = mapFromLDAP(t, q[0], &nq, 0, &ret, &attr); + if (qr != 0) { + for (i = 0; i < nq; i++) { + printQuery(qr[i], t); + printbuf(); + freeQuery(qr[i]); + if (attr != 0) { + printObjAttr(attr[i]); + } + } + free(qr); + freeObjAttr(attr, nq); + } else if (ret != LDAP_SUCCESS) { + p2buf(myself, + "mapFromLDAP() => %d (%s)\n", + ret, ldap_err2string(ret)); + } + } + for (i = 0; i < nv; i++) + freeQuery(q[i]); + freeRuleValue(rv, nv); + printbuf(); + } else if (!modify && !delete) { + __nis_obj_attr_t **attr = 0; + /* Enumeration */ + qr = mapFromLDAP(t, 0, &nq, 0, &ret, &attr); + if (qr != 0) { + for (i = 0; i < nq; i++) { + printQuery(qr[i], t); + printbuf(); + freeQuery(qr[i]); + if (attr != 0) { + printObjAttr(attr[i]); + } + } + freeObjAttr(attr, nq); + free(qr); + } else if (ret != LDAP_SUCCESS) { + p2buf(myself, + "mapFromLDAP() => %d (%s)\n", + ret, ldap_err2string(ret)); + } + printbuf(); + } else { + usage(argv[0]); + } + + if (freeTable) + free(table); + if (t != 0) + __nis_release_item(t, &ldapMappingList, 1); + + if (numMisaligned > 0) + fprintf(stderr, "numMisaligned = %d\n", numMisaligned); + + return (0); +} + +/* + * As long as we're single-threaded, we need a private version of + * assertExclusive that always returns success. + */ +int +assertExclusive(void *unused) { + return (1); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nislog.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nislog.c new file mode 100644 index 0000000000..3a0f8c125e --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nislog.c @@ -0,0 +1,290 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2001 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nislog.c + * + * This module simply reads the internal format of the log and prints + * it out for the user to stdout. The format of the transaction log + * is an *INTERNAL* interface and will change from time to time. The + * service uses the log specific functions defined in the nis_log.c + * module. They are reproduced here for this test code. + * + */ + +#include <stdio.h> +#include <syslog.h> +#include <values.h> /* MAXINT define */ +#include <sys/fcntl.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <unistd.h> +#include <signal.h> +#include <rpc/rpc.h> +#include <rpc/xdr.h> +#include <rpcsvc/nis.h> +#include "log.h" + +/* + * nislog(1M) doesn't use 'cons', but we need it defined following the + * code rearrangements for the MT rpc.nisd. + */ +FILE *cons = NULL; + +extern unsigned long __maxloglen; + +extern int optind, opterr; +extern char *optarg; +int verbose = 0; +/* + * This macro returns true if the two objects have identical OIDs + */ +#define sameobj(o1, o2) (((o1)->zo_oid.ctime == (o2)->zo_oid.ctime) && \ + ((o1)->zo_oid.mtime == (o2)->zo_oid.mtime)) + +int +abort_transaction(xid) + int xid; +{ + return (0); +} + + +void +print_transaction(cur, num) + log_upd *cur; + int num; +{ + XDR xdrs; + log_entry le; + + printf("@@@@@@@@@@@@@@@@ Transaction @@@@@@@@@@@@@@@@@@\n"); + printf("#%05d, XID : %d\n", num, cur->lu_xid); + printf("Time : %s\n", ctime((time_t *)&(cur->lu_time))); + printf("Directory : %s\n", cur->lu_dirname); + memset((char *)&le, 0, sizeof (le)); + xdrmem_create(&xdrs, (char *)(cur->lu_data), cur->lu_size, XDR_DECODE); + xdr_log_entry(&xdrs, &le); + printf("Entry type : "); + switch (le.le_type) { + case ADD_NAME : + printf("ADD Name\n"); + break; + case REM_NAME : + printf("REMOVE Name\n"); + break; + case ADD_IBASE : + printf("ADD Entry\n"); + break; + case REM_IBASE : + printf("REMOVE Entry\n"); + break; + case MOD_NAME_OLD : + printf("MODIFY (Original Value)\n"); + break; + case MOD_NAME_NEW : + printf("MODIFY (New Value)\n"); + break; + case UPD_STAMP : + printf("UPDATE time stamp.\n"); + break; + default: + printf("Unknown (%d)!\n", le.le_type); + break; + } + printf("Entry timestamp : %s", ctime((time_t *)&(le.le_time))); + printf("Principal : %s\n", le.le_princp); + printf("Object name : %s\n", __make_name(&le)); + printf(".................. Object .....................\n"); + nis_print_object(&le.le_object); + printf("...............................................\n"); + xdr_free(xdr_log_entry, (char *)&le); +} + +static +void +buserr_exit() +{ + printf("Transaction log checkpointed while reading.\n"); + exit(0); +} + +usage(s) + char *s; +{ + if ((strcmp(s, "loghead") == 0) || (strcmp(s, "logtail") == 0)) + fprintf(stderr, "usage: nislog %s [-v] num\n", s); + else + fprintf(stderr, "usage: %s [-h [num] | -t [num] ] [-v] \n", s); + exit(1); +} + +char *directories[128]; + +main(argc, argv) + int argc; + char *argv[]; +{ + log_upd *cur; + char **dir; + int entries = MAXINT; + char *cmd; + int i, c; + struct sigaction sa; + int tail_only = 0; + char logname[NIS_MAXNAMELEN]; + + if (geteuid() != (uid_t)0) { + fprintf(stderr, "nislog must be run as root.\n"); + exit(1); + } + + memset(logname, 0, sizeof (logname)); + printf("NIS Log printing facility.\n"); + opterr = 0; + /* + * if the cmdname is "logtail" or "loghead" change the + * arguments a bit, any other name and we default to + * "nislog" behaviour. + */ + cmd = (char *) strrchr(argv[0], '/'); + if (! cmd) + cmd = argv[0]; + if ((strcmp(cmd, "logtail") == 0) || + (strcmp(cmd, "loghead") == 0)) { + if (strcmp(cmd, "logtail") == 0) + tail_only = 1; + + while ((c = getopt(argc, argv, "v")) != -1) { + switch (c) { + case 'v' : + verbose = 1; + break; + case '?' : + usage(cmd); + break; + } + } + if (optind < argc) { + entries = atoi(argv[optind]); + optind++; + } + i = 0; + while (optind < argc) + directories[i++] = argv[optind++]; + } else { + while ((c = getopt(argc, argv, "h:t:v")) != -1) { + switch (c) { + case 'h' : + entries = atoi(optarg); + break; + case 't' : + tail_only = 1; + entries = atoi(optarg); + break; + case 'v' : + verbose = 1; + break; + case '?' : + usage(cmd); + break; + } + } + i = 0; + while (optind < argc) + directories[i++] = argv[optind++]; + } + + /* + * If we have a long transaction log mmapped and rpc.nisd + * truncates the log before we have hit all of the pages, + * then when we hit a page past the current end of file for + * the first time, we will get a SIGBUS. We simple set a + * signal handler to catch the signal and exit with status 0. + */ + memset((char *)&sa, 0, sizeof (sa)); + sa.sa_handler = buserr_exit; + sigaction(SIGBUS, &sa, NULL); + + sprintf(logname, "%s", LOG_FILE); + if (map_log(logname, FNISLOG)) { + fprintf(stderr, "Unable to map log!\n"); + exit(1); + } + printf("NIS Log dump :\n"); + printf("\tLog state : "); + switch (__nis_log->lh_state) { + + case LOG_STABLE : + printf("STABLE.\n"); + break; + case LOG_RESYNC : + printf("RESYNCING.\n"); + break; + case LOG_UPDATE : + printf("UPDATING.\n"); + break; + case LOG_CHECKPOINT : + printf("CHECKPOINTING.\n"); + break; + } + printf("Number of updates : %d\n", __nis_log->lh_num); + printf("Current XID : %d\n", __nis_log->lh_xid); + printf("Size of Log in bytes : %d\n", + (__nis_log->lh_tail) ? LOG_SIZE(__nis_log) : sizeof (log_hdr)); + printf("*** UPDATES ***\n"); + if (! __nis_log->lh_num) { + printf("--None--\n"); + exit(0); + } + dir = directories; + do { + cur = (tail_only) ? __nis_log->lh_tail : __nis_log->lh_head; + + if (tail_only) { + for (i = 0; cur && (i < entries); cur = cur->lu_prev) { + if ((*dir) && + (nis_dir_cmp(*dir, cur->lu_dirname) + != SAME_NAME)) + continue; + print_transaction(cur, __nis_log->lh_num-i-1); + i++; + } + } else { + for (i = 0; cur && (i < entries); cur = cur->lu_next) { + if ((*dir) && + (nis_dir_cmp((*dir), cur->lu_dirname) + != SAME_NAME)) + continue; + print_transaction(cur, i); + i++; + } + } + dir++; + } while (*dir != NULL); + exit(0); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/req.flg b/usr/src/cmd/rpcsvc/nis/rpc.nisd/req.flg new file mode 100644 index 0000000000..6bf46e9493 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/req.flg @@ -0,0 +1,50 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# + + +echo_file usr/src/lib/libnsl/include/rpcsvc/nis_dhext.h +echo_file usr/src/lib/libnisdb/nisdb_ldap.h +echo_file usr/src/lib/libnisdb/ldap_attr.h +echo_file usr/src/lib/libnisdb/ldap_map.h +echo_file usr/src/lib/libnisdb/ldap_nisdbquery.h +echo_file usr/src/lib/libnisdb/ldap_nisplus.h +echo_file usr/src/lib/libnisdb/ldap_op.h +echo_file usr/src/lib/libnisdb/ldap_parse.h +echo_file usr/src/lib/libnisdb/ldap_print.h +echo_file usr/src/lib/libnisdb/ldap_ruleval.h +echo_file usr/src/lib/libnisdb/ldap_structs.h +echo_file usr/src/lib/libnisdb/ldap_util.h +echo_file usr/src/lib/libnisdb/ldap_xdr.h +echo_file usr/src/lib/libnisdb/nis_hashitem.h +echo_file usr/src/lib/libnisdb/nis_ldap.h +echo_file usr/src/lib/libnisdb/nis_parse_ldap_conf.h +echo_file usr/src/lib/libnisdb/nis_servlist.h +echo_file usr/src/lib/libnisdb/nisdb_ldap.h +echo_file usr/src/lib/libnisdb/nisdb_mt.h +echo_file usr/src/lib/libnisdb/nisdb_rw.h diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv.c new file mode 100644 index 0000000000..5bafc52962 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv.c @@ -0,0 +1,405 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1993-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <rpc/rpc.h> +#include <syslog.h> +#include <signal.h> +#include <string.h> +#include <sys/types.h> +#include <sys/resource.h> +#include <errno.h> +#include <sys/systeminfo.h> +#include <netconfig.h> +#include <netdir.h> +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/nis.h> +#include "resolv_common.h" +#include "nis_proc.h" + +#define YPDNSVERS 2L +#define RESOLV_EXEC_PATH "/usr/sbin/rpc.nisd_resolv" +#define RESOLV_EXEC_ERR "can't exec /usr/sbin/rpc.nisd_resolv: %s\n" + +/* + * Need to protect against inadvertently starting more than one rpc.nis_resolv. + * Thus, apply this lock before checking if rpc.nisd_resolv is running, + * and hold it until setup_resolv() (if called) has completed. + */ +DECLMUTEXLOCK(setup_resolv); + +extern int verbose; + +void +setup_resolv(fwding, child, client, tp_type, prognum) +int *fwding; +int *child; +CLIENT **client; +char *tp_type; +long prognum; /* use transient if this not set */ +{ + enum clnt_stat stat; + struct timeval tv; + char prog_str[15], fd_str[5]; + SVCXPRT *xprt = NULL; + char *tp; + char name[257]; + struct netconfig *nc; + void *h; + + if (!*fwding) + return; + + /* try the specified netid (default ticots), then any loopback */ + tp = (tp_type && *tp_type) ? tp_type : "ticots"; + if (!getconf(tp, &h, &nc)) { /* dont forget endnetconfig() */ + syslog(LOG_ERR, "can't get resolv_clnt netconf %s.\n", tp); + *fwding = FALSE; + return; + } + tp = nc->nc_netid; + + /* + * Startup the resolv server: use transient prognum if prognum + * isn't set. Using transient means we create mapping then + * pass child the fd to use for service. + */ + if (!getprognum(&prognum, &xprt, fd_str, prog_str, YPDNSVERS, tp)) { + syslog(LOG_ERR, "can't create resolv xprt for transient.\n"); + *fwding = FALSE; + endnetconfig(h); + return; + } + switch (*child = vfork()) { + case -1: /* error */ + syslog(LOG_ERR, "can't startup resolv daemon\n"); + endnetconfig(h); + *fwding = FALSE; + return; + case 0: /* child */ + /* + * if using transient we must maintain fd across + * exec cause unset/set on prognum isn't automic. + * + * if using transient we'll just do svc_tli_create + * in child on our bound fd. + */ + execlp(RESOLV_EXEC_PATH, "rpc.nisd_resolv", + "-F", /* forground */ + "-C", fd_str, /* dont close */ + "-p", prog_str, /* prognum */ + "-t", tp, /* tp type */ + NULL); + syslog(LOG_ERR, RESOLV_EXEC_ERR, strerror(errno)); + /* + * vfork(2) says the forked child should call _exit(), + * not exit(), if exec() fails. + */ + _exit(1); + default: /* parent */ + /* close fd, free xprt, but leave mapping */ + if (xprt) svc_destroy(xprt); + + /* let it crank up before we create client */ + sleep(4); + } + if (sysinfo(SI_HOSTNAME, name, sizeof (name)-1) == -1) { + syslog(LOG_ERR, "can't get local hostname.\n"); + (void) kill (*child, SIGINT); + endnetconfig(h); + *fwding = FALSE; + return; + } + if ((*client = clnt_tp_create(HOST_SELF_CONNECT, prognum, + YPDNSVERS, nc)) == NULL) { + syslog(LOG_ERR, "can't create resolv_clnt\n"); + (void) kill (*child, SIGINT); + endnetconfig(h); + *fwding = FALSE; + return; + } + endnetconfig(h); + + /* ping for comfort */ + tv.tv_sec = 10; tv.tv_usec = 0; + if ((stat = clnt_call(*client, 0, xdr_void, 0, + xdr_void, 0, tv)) != RPC_SUCCESS) { + syslog(LOG_ERR, "can't talk with resolv server\n"); + clnt_destroy (*client); + (void) kill (*child, SIGINT); + *fwding = FALSE; + return; + } + + if (verbose) + syslog(LOG_INFO, "finished setup for dns fwding.\n"); +} + +int +getprognum(prognum, xprt, fd_str, prog_str, vers, tp_type) +long *prognum; +SVCXPRT **xprt; +char *fd_str; +char *prog_str; +long vers; +char *tp_type; +{ + static u_long start = 0x40000000; + int fd; + struct netconfig *nc; + struct netbuf *nb; + + /* If prognum specified, use it instead of transient hassel. */ + if (*prognum) { + *xprt = NULL; + sprintf(fd_str, "-1"); /* have child close all fds */ + sprintf(prog_str, "%u", *prognum); + return (TRUE); + } + + /* + * Transient hassel: + * - parent must create mapping since someone else could + * steal the transient prognum before child created it + * - pass the child the fd to use for service + * - close the fd (after exec), free xprt, leave mapping intact + */ + /* tp_type is legit: users choice or a loopback netid */ + if ((nc = getnetconfigent(tp_type)) == NULL) + return (FALSE); + if ((*xprt = svc_tli_create(RPC_ANYFD, nc, NULL, 0, 0)) == NULL) { + freenetconfigent(nc); + return (FALSE); + } + nb = &(*xprt)->xp_ltaddr; + fd = (*xprt)->xp_fd; + while (!rpcb_set(start, vers, nc, nb)) + start++; + freenetconfigent(nc); + + *prognum = start; + sprintf(fd_str, "%u", fd); + sprintf(prog_str, "%u", *prognum); + + return (TRUE); +} + +int +getconf(netid, handle, nconf) +char *netid; +void **handle; +struct netconfig **nconf; +{ + struct netconfig *nc, *save = NULL; + + if ((*handle = setnetconfig()) == NULL) + return (FALSE); + + while (nc = getnetconfig((void*)*handle)) { + /* XXX Shouldn't this be strcmp() == 0 ? */ + if (strcmp(nc->nc_netid, netid) != 0) { + *nconf = nc; + return (TRUE); + } else if (!save && strcmp(nc->nc_protofmly, "loopback") != 0) + save = nc; + } + + if (save) { + *nconf = save; + return (TRUE); + } else { + endnetconfig(*handle); + return (FALSE); + } +} + +int +resolv_req(fwding, client, pid, tp, xprt, req, map) +int *fwding; +CLIENT **client; +int *pid; +char *tp; +SVCXPRT *xprt; +struct ypreq_key *req; +char *map; +{ + enum clnt_stat stat; + struct timeval tv; + struct ypfwdreq_key4 fwd_req4; + struct ypfwdreq_key6 fwd_req6; + struct in6_addr in6; + int byname, byaddr; + int byname6, byaddr6; + struct netbuf *nb; + char *cp; + int i; + sa_family_t caller_af = AF_UNSPEC; + struct sockaddr_in *sin4; + struct sockaddr_in6 *sin6; + int savepid; + + if (!*fwding) + return (FALSE); + + byname = strcmp(map, "hosts.byname") == 0; + byaddr = strcmp(map, "hosts.byaddr") == 0; + byname6 = strcmp(map, "ipnodes.byname") == 0; + byaddr6 = strcmp(map, "ipnodes.byaddr") == 0; + if ((!byname && !byaddr && !byname6 && !byaddr6) || + req->keydat.dsize == 0 || + req->keydat.dptr[0] == '\0' || + !isascii(req->keydat.dptr[0]) || + !isgraph(req->keydat.dptr[0])) { + /* default status is YP_NOKEY */ + return (FALSE); + } + + /* + * In order to tell if we have an IPv4 or IPv6 caller address, + * we must know that nb->buf is a (sockaddr_in *) or a + * (sockaddr_in6 *). Hence, we might as well dispense with the + * conversion to uaddr and parsing of same that this section + * of the code previously involved itself in. + */ + nb = svc_getrpccaller(xprt); + if (nb != 0) + caller_af = ((struct sockaddr_storage *)nb->buf)->ss_family; + + if (caller_af == AF_INET6) { + fwd_req6.map = map; + fwd_req6.keydat = req->keydat; + fwd_req6.xid = svc_getxid(xprt); + sin6 = (struct sockaddr_in6 *)nb->buf; + fwd_req6.addr = (uint32_t *)&in6; + memcpy(fwd_req6.addr, sin6->sin6_addr.s6_addr, + sizeof (in6)); + fwd_req6.port = sin6->sin6_port; + } else if (caller_af == AF_INET) { + fwd_req4.map = map; + fwd_req4.keydat = req->keydat; + fwd_req4.xid = svc_getxid(xprt); + sin4 = (struct sockaddr_in *)nb->buf; + fwd_req4.ip = ntohl(sin4->sin_addr.s_addr); + fwd_req4.port = sin4->sin_port; + } else { + syslog(LOG_ERR, "unknown caller IP address family %d", + caller_af); + return (FALSE); + } + + /* Restart resolver if it died. (possible overkill) */ + + /* + * Since we may end up restarting rpc.nisd_resolv, acquire the + * lock. + */ + MUTEXLOCK(setup_resolv, "resolv_req(setup_resolv)"); + + if (kill(*pid, 0)) { + syslog(LOG_INFO, + "Restarting resolv server: old one (pid %d) died.\n", *pid); + clnt_destroy (*client); + setup_resolv(fwding, pid, client, tp, 0l /* transient p# */); + if (!*fwding) { + MUTEXUNLOCK(setup_resolv, "resolv_req(setup_resolv)"); + syslog(LOG_ERR, + "can't restart resolver: ending resolv service.\n"); + return (FALSE); + } + } + + /* + * Save pid of rpc.nisd_resolv, so that we can tell if another + * thread performed a restart while we weren't holding the lock. + */ + savepid = *pid; + MUTEXUNLOCK(setup_resolv, "resolv_req(setup_resolv)"); + + /* may need to up timeout */ + tv.tv_sec = 10; tv.tv_usec = 0; + if (caller_af == AF_INET6) { + stat = clnt_call(*client, YPDNSPROC6, xdr_ypfwdreq_key6, + (char *) &fwd_req6, xdr_void, 0, tv); + } else { + stat = clnt_call(*client, YPDNSPROC4, xdr_ypfwdreq_key4, + (char *) &fwd_req4, xdr_void, 0, tv); + } + if (stat == RPC_SUCCESS) /* expected */ + return (TRUE); + + else { /* Over kill error recovery */ + MUTEXLOCK(setup_resolv, "resolv_req(setup_resolv)"); + /* + * If the pid of rpc.nisd_resolv has changed, another thread + * has already attempted a restart, and there's little point + * in our doing the same. Just repeat the clnt_call(). + */ + if (*pid == savepid) { + /* + * make one attempt to restart service before turning + * off + */ + syslog(LOG_INFO, + "Restarting resolv server: old one not responding.\n"); + + if (!kill(*pid, 0)) + kill (*pid, SIGINT); /* cleanup old one */ + + clnt_destroy (*client); + setup_resolv(fwding, pid, client, tp, + 0l /* transient p# */); + if (!*fwding) { + MUTEXUNLOCK(setup_resolv, + "resolv_req(setup_resolv)"); + syslog(LOG_ERR, + "can't restart resolver: ending resolv service.\n"); + return (FALSE); + } + } + MUTEXUNLOCK(setup_resolv, "resolv_req(setup_resolv)"); + + if (caller_af == AF_INET6) { + stat = clnt_call(*client, YPDNSPROC6, xdr_ypfwdreq_key6, + (char *) &fwd_req6, xdr_void, 0, tv); + } else { + stat = clnt_call(*client, YPDNSPROC4, xdr_ypfwdreq_key4, + (char *) &fwd_req4, xdr_void, 0, tv); + } + if (stat == RPC_SUCCESS) /* expected */ + return (TRUE); + else { + /* no more restarts */ + clnt_destroy (*client); + *fwding = FALSE; /* turn off fwd'ing */ + syslog(LOG_ERR, + "restarted resolver not responding: ending resolv service.\n"); + return (FALSE); + } + } +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.c new file mode 100644 index 0000000000..71517dae8e --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.c @@ -0,0 +1,74 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1993-1999 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Routines used by rpc.nisd and by rpc.nisd_resolv + */ + +#include <rpc/rpc.h> +#include <netdb.h> +#include <rpcsvc/yp_prot.h> +#include <errno.h> +#include <sys/types.h> +#include "resolv_common.h" + +bool_t +xdr_ypfwdreq_key4(XDR *xdrs, struct ypfwdreq_key4 *ps) +{ + return (xdr_ypmap_wrap_string(xdrs, &ps->map) && + xdr_datum(xdrs, &ps->keydat) && + xdr_u_long(xdrs, &ps->xid) && + xdr_u_long(xdrs, &ps->ip) && + xdr_u_short(xdrs, &ps->port)); +} + + +bool_t +xdr_ypfwdreq_key6(XDR *xdrs, struct ypfwdreq_key6 *ps) +{ + u_int addrsize = sizeof (struct in6_addr)/sizeof (uint32_t); + char **addrp = (caddr_t *)&(ps->addr); + + return (xdr_ypmap_wrap_string(xdrs, &ps->map) && + xdr_datum(xdrs, &ps->keydat) && + xdr_u_long(xdrs, &ps->xid) && + xdr_array(xdrs, addrp, &addrsize, addrsize, + sizeof (uint32_t), xdr_uint32_t) && + xdr_u_short(xdrs, &ps->port)); +} + + +u_long +svc_getxid(xprt) +register SVCXPRT *xprt; +{ + register struct bogus_data *su = getbogus_data(xprt); + if (su == NULL) + return (0); + return (su->su_xid); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.h new file mode 100644 index 0000000000..1e8eb7316b --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.h @@ -0,0 +1,90 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _RESOLV_COMMON_H +#define _RESOLV_COMMON_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> + +/* + * Definitions common to rpc.nisd resolv and rpc.resolv code. + * Stolen from rpcbind and is used to access xprt->xp_p2 fields. + */ + +#define YPDNSPROC4 1L +#define YPDNSPROC6 2L + +#define MAX_UADDR 52 +#define GETCALLER(xprt) svc_getrpccaller(xprt) +#define SETCALLER(xprt, nbufp) xprt->xp_rtaddr.len = nbufp->len; \ + memcpy(xprt->xp_rtaddr.buf, nbufp->buf, nbufp->len); +#define MAX_OPT_WORDS 128 +#define RPC_BUF_MAX 32768 +struct bogus_data { + /* XXX: optbuf should be the first field, used by ti_opts.c code */ + struct netbuf optbuf; /* netbuf for options */ + long opts[MAX_OPT_WORDS]; /* options */ + u_int su_iosz; /* size of send.recv buffer */ + u_long su_xid; /* transaction id */ + XDR su_xdrs; /* XDR handle */ + char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */ + char *su_cache; /* cached data, NULL if none */ + struct t_unitdata su_tudata; /* tu_data for recv */ +}; +#define getbogus_data(xprt) ((struct bogus_data *)(xprt->xp_p2)) + + +struct ypfwdreq_key4 { + char *map; + datum keydat; + unsigned long xid; + unsigned long ip; + unsigned short port; +}; + +struct ypfwdreq_key6 { + char *map; + datum keydat; + unsigned long xid; + uint32_t *addr; + in_port_t port; +}; + +extern ulong_t svc_getxid(SVCXPRT *); +extern bool_t xdr_ypfwdreq_key4(XDR *, struct ypfwdreq_key4 *); +extern bool_t xdr_ypfwdreq_key6(XDR *, struct ypfwdreq_key6 *); + +#ifdef __cplusplus +} +#endif + +#endif /* _RESOLV_COMMON_H */ diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/DNS_FWD b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/DNS_FWD new file mode 100644 index 0000000000..2ae1c97574 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/DNS_FWD @@ -0,0 +1,84 @@ +CDDL HEADER START + +The contents of this file are subject to the terms of the +Common Development and Distribution License, Version 1.0 only +(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 + + RPC.NISD operation on ypproc_match_svc() host match failure + + + |---------------| + | rpc.nisd -YX | |---------| + | | rpc |nisd_fwd | res_search + | --------> | --------------------> | | --------> + | | if no | parms: caller | | + | | match & | xid | | <-------- + | | hosts.xx| map | | response + | | | key | | + |---------------| |---------| + ^ | + | | + clnt_call | caller/xid | + (udp) | map & key | sendreply + | | (udp) + | | + |---------------| | + | yp_match() | <--------------------------| + | hosts | + |---------------| + +Notes: + 1. separate daemon isolates code and minimizes impact on rpc.nisd + 2. 4.x and 5.x rpc.nisd dns forwarding support + 3. rpc.nisd default operation has not changed, user must select dns + resolution at rpc.nisd startup + + +rpc.nisd: (modifications) + - added option to select DNS fwding (-B [-t netid]) + - added setup_resolv() (uses transient p#) + - added resolv_req() in ypproc_match_svc() to forward req + +rpc.nisd_resolv: (implementation) + - leverage ypserv asynch resolv code for minimum risk + - non-blocking daemon + - SIGUSR1 toggles verbose/debugging info + - cleanup/exit if parent dies + - cleanup/exit on INT, QUIT, TERM + + +Testing included: + + Normal Operation: + - udp,tcp,ticots,ticlts -t selections + - DNS match + - no DNS match + - continuous forwarded requests for 24hrs (5 per sec) + - 4.1 rpc.nisd + - 5.2 rpc.nisd + + Error Recovery: + - default to 1st loopback on invalid -t xxx selection + - missing /etc/resolv.conf + - down DNS server + - wedged rpc.nisd_resolv + - killed rpc.nisd_resolv + - killed rpc.nisd, rpc.nisd_resolv cleanup + + Error Checking: + - usage: using -t without -B and using -B without -Y + diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/Makefile b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/Makefile new file mode 100644 index 0000000000..b86aaf456f --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/Makefile @@ -0,0 +1,74 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#pragma ident "%Z%%M% %I% %E% SMI" +# +# Makefile for the nis+ dns fwd daemon +# + +SRVOBJS= main.o \ + ypresolv_proc.o \ + rpc_as.o \ + svc_run_as.o \ + ngethostbyname.o \ + nget_answer.o \ + nres_search.o \ + nres_rcv.o \ + nres_send.o + +COMMONOBJS= resolv_common.o + +OBJS= $(SRVOBJS) $(COMMONOBJS) +SRCS= $(OBJS:.o=.c) + +include $(SRC)/cmd/Makefile.cmd + +PROG= rpc.nisd_resolv + +CFLAGS += $(CCVERBOSE) + +# We don't use -lsocket for 4.x build +LDLIBS += -lnsl -lresolv -lsocket + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTUSRSBINPROG) + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +resolv_common.o: ../resolv_common.c + $(COMPILE.c) ../resolv_common.c + +lint: + $(LINT.c) $(SRVOBJS:.o=.c) ../resolv_common.c $(LDLIBS) + +clobber: clean + $(RM) $(PROG) + +clean: + $(RM) $(OBJS) diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/main.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/main.c new file mode 100644 index 0000000000..36931ee5cf --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/main.c @@ -0,0 +1,298 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1993-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdarg.h> +#include <sys/systeminfo.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/errno.h> +#include <errno.h> +#include <stdio.h> +#include <signal.h> +#include <time.h> +#include <string.h> +#include <rpc/rpc.h> +#include <unistd.h> +#include <stdlib.h> +#include <syslog.h> /* 5.x includes stdarg.h, varargs.h */ +#include <rpcsvc/yp_prot.h> +#include "prnt.h" + +#define YPDNSPROG 123456L /* default prognum: not used */ +#define YPDNSVERS 2L + +extern void dispatch(struct svc_req *rqstp, SVCXPRT *transp); +extern void svc_run_as(void); + +static struct timeval start_time; /* Time service started running. */ +int verbose = 0; /* Verbose mode, 0=off, 1=on */ +int verbose_out = 0; /* Verbose ouput, 0=log, 1=stdout */ +static int background = TRUE; /* Forground or bkgrd */ +pid_t ppid = 0; /* for terminating if nisd dies */ +static long prognum = YPDNSPROG; /* for cleanup (may want transient) */ +SVCXPRT *reply_xprt4; /* used for sendreply (IPv4) */ +struct netconfig *udp_nconf4; /* used to set xprt caller in dispatch */ +SVCXPRT *reply_xprt6; /* used for sendreply (IPv6) */ +struct netconfig *udp_nconf6; /* used to set xprt caller in dispatch */ + +/* + * The -v -V is used to distinguiish between verbose to stdout + * versus verbose to syslog rather then the background flag + * because this may be started from a daemon (nisd). When started + * from a daemon we can't use forground to print to stdout. + */ +static void usage() +{ + (void) fprintf(stderr, + "usage: rpc.nisd_resolv [-v|-V] [-F[-C xx]] [-t xx] [-p yy]\n"); + (void) fprintf(stderr, "Options supported by this version :\n"); + (void) fprintf(stderr, "\tF - run in forground\n"); + (void) fprintf(stderr, + "\tC fd - use fd for service xprt (from nisd)\n"); + (void) fprintf(stderr, "\tv - verbose syslog\n"); + (void) fprintf(stderr, "\tV - verbose stdout\n"); + (void) fprintf(stderr, "\tt xx - use transport xxx\n"); + (void) fprintf(stderr, "\tp yy - use transient program# yyy\n"); + (void) fprintf(stderr, "Use SIGUSR1 to toggle verbose mode.\n"); + exit(1); +} + +void +prnt(int info_or_err, char *format, ...) +{ + va_list ap; + + va_start(ap, format); + if (info_or_err == 1 /* error */) { + if (verbose_out) { + (void) vfprintf(stderr, format, ap); + } else + (void) vsyslog(LOG_ERR, format, ap); + } else if (verbose) { + if (verbose_out) { + (void) vfprintf(stdout, format, ap); + } else + (void) vsyslog(LOG_INFO, format, ap); + } + + va_end(ap); +} + +void +cleanup(int sig) +{ + /* unreg prog */ + if (sig) prnt(P_INFO, "unregistering resolv server and exiting.\n"); + (void) rpcb_unset(prognum, YPDNSVERS, NULL); + exit(0); +} + +/* ARGSUSED */ +static void +toggle_verbose(int sig) +{ + (void) signal(SIGUSR1, toggle_verbose); + + if (verbose) { + prnt(P_INFO, "verbose turned off.\n"); + verbose = FALSE; + } else { + verbose = TRUE; + prnt(P_INFO, "verbose turned on.\n"); + } +} + +/* + * Callback function passed to fdwalk() to close all files. + */ +static int +void_close(void *use_fdp, int fd) +{ + if (fd != *(int *)use_fdp) + (void) close(fd); + return (0); +} + +main(int argc, char *argv[]) +{ + int i; + int use_fd = -2; /* not set */ + int c; + SVCXPRT *transp; + char *t_type = "ticots"; + struct netconfig *nc; + int connmaxrec = RPC_MAXDATASIZE; + + /* + * Process the command line arguments + */ + opterr = 0; + (void) chdir("/var/nis"); + while ((c = getopt(argc, argv, "vVFC:p:t:")) != -1) { + switch (c) { + case 'v' : /* syslog */ + verbose = TRUE; + verbose_out = 0; + break; + case 'V' : /* stdout */ + verbose = TRUE; + verbose_out = 1; + break; + case 'F' : + background = FALSE; + break; + case 'C' : + use_fd = atoi(optarg); + break; + case 't' : + t_type = optarg; + break; + case 'p' : + prognum = atol(optarg); + break; + case '?': + usage(); + } + } + + if (background) { + switch (fork()) { + case -1: + (void) fprintf(stderr, + "Couldn't fork a process: exiting.\n"); + exit(1); + /* NOTREACHED */ + case 0: + break; + default: + exit(0); + } + + closefrom(0); + (void) open("/dev/null", O_RDONLY); + (void) open("/dev/null", O_WRONLY); + (void) dup(1); + } else { /* foreground */ + /* pardon the mess: due to transient p# requirement */ + switch (use_fd) { + case -2: /* -C not used: just close stdin */ + (void) close(0); + break; + case -1: /* close all (nisd non transient) */ + closefrom(0); + break; + default: /* use use_fd as svc fd; close others (nisd trans) */ + ppid = getppid(); + (void) fdwalk(void_close, &use_fd); + break; + } + } + + if (!verbose_out) + openlog("rpc.nisd_resolv", LOG_PID+LOG_NOWAIT, LOG_DAEMON); + + (void) signal(SIGUSR1, toggle_verbose); + (void) signal(SIGINT, cleanup); + (void) signal(SIGQUIT, cleanup); + (void) signal(SIGTERM, cleanup); + + (void) gettimeofday(&start_time, 0); + prnt(P_INFO, "Starting nisd resolv server: %s", + ctime((time_t *)&start_time.tv_sec)); + + /* + * Set non-blocking mode and maximum record size for + * connection oriented RPC transports. + */ + if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) { + prnt(P_INFO, "unable to set maximum RPC record size\n"); + } + + /* no check for already running since using transient */ + + if ((nc = getnetconfigent(t_type)) == NULL) { + prnt(P_ERR, "cannot get %s netconf.\n", t_type); + exit(1); + } + if (use_fd != -1 && use_fd != -2) { + /* use passed in fd = use_fd */ + transp = svc_tli_create(use_fd, nc, NULL, YPMSGSZ, YPMSGSZ); + if (transp == NULL) { + prnt(P_ERR, "cannot create service xprt.\n"); + exit(1); + } + /* parent did rpcb_set(): just add to callout */ + if (!svc_reg(transp, prognum, YPDNSVERS, dispatch, NULL)) { + prnt(P_ERR, "cannot register service xprt.\n"); + exit(1); + } + } else { + (void) rpcb_unset(prognum, YPDNSVERS, NULL); + if (!svc_tp_create(dispatch, prognum, YPDNSVERS, nc)) { + prnt(P_ERR, "cannot create resolv service.\n"); + exit(1); + } + } + freenetconfigent(nc); + + /* Need udp xprt for sendreply()s, but don't reg it. */ + udp_nconf4 = getnetconfigent("udp"); + udp_nconf6 = getnetconfigent("udp6"); + if (udp_nconf4 == 0 && udp_nconf6 == 0) { + prnt(P_ERR, "cannot get udp/udp6 netconf.\n"); + exit(1); + } + + if (udp_nconf4 != 0) + reply_xprt4 = svc_tli_create(RPC_ANYFD, udp_nconf4, NULL, + YPMSGSZ, YPMSGSZ); + else + reply_xprt4 = 0; + + + if (udp_nconf6 != 0) + reply_xprt6 = svc_tli_create(RPC_ANYFD, udp_nconf6, NULL, + YPMSGSZ, YPMSGSZ); + else + reply_xprt6 = 0; + + if (reply_xprt4 == NULL && reply_xprt6 == NULL) { + prnt(P_ERR, "cannot create udp/udp6 xprt.\n"); + exit(1); + } else + prnt(P_INFO, "created sendreply handle(s).\n"); + + /* keep udp_nconf[46] which is used later to create nbuf from uaddr */ + + prnt(P_INFO, "entering loop.\n"); + + svc_run_as(); + + return (0); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nget_answer.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nget_answer.c new file mode 100644 index 0000000000..8d668a31ef --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nget_answer.c @@ -0,0 +1,421 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1993,1998 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* Taken from 4.1.3 ypserv resolver code. */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <ctype.h> +#include <syslog.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <strings.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include "nres.h" +#include "prnt.h" + +#ifndef NO_DATA +#define NO_DATA NO_ADDRESS +#endif + +#ifndef LOG_AUTH +#define LOG_AUTH 0 +#endif + +typedef union { + HEADER hdr; + uchar_t buf[MAXPACKET]; +} querybuf; + +typedef union { + int32_t al; + char ac; +} align; + +extern int h_errno; + +static struct hostent *getanswer(const querybuf *, int, const char *, int); +extern int lookup_AF_type(struct cache_ent *chl); +extern uint16_t _getshort(const uchar_t *); +extern uint32_t _getlong(const uchar_t *); + +#define MAXALIASES 35 +#define MAXADDRS 35 + +static const char AskedForGot[] = + "gethostby*.getanswer: asked for \"%s\", got \"%s\""; + +static char *h_addr_ptrs[MAXADDRS + 1]; +static struct hostent host; +static char *host_aliases[MAXALIASES]; +static char hostbuf[8*1024]; + + + +struct hostent * +nres_getanswer(temp) +struct nres *temp; +{ + querybuf *answer; + int anslen; + char *name; + struct hostent *ret; + + answer = (querybuf *)temp->answer; + anslen = temp->answer_len; + name = temp->name; + if (ret = getanswer(answer, anslen, name, temp->qtype)) { + temp->h_errno = 0; + prnt(P_INFO, "nres_getanswer: return OK.\n"); + return (ret); + } + + temp->h_errno = h_errno; + prnt(P_INFO, "nres_getanswer: return FAIL(err=%d).\n", h_errno); + return ((struct hostent *)NULL); +} + + +int +nres_chkreply(temp) +struct nres *temp; +{ + char *answer; + int anslen; + HEADER *hp; + answer = temp -> answer; + anslen = temp -> answer_len; + + /* initialize some hosts variables for getanswer */ + host.h_addrtype = lookup_AF_type(temp->userinfo); + host.h_length = (host.h_addrtype == AF_INET) ? INADDRSZ : IN6ADDRSZ; + + if (anslen <= 0) { + prnt(P_INFO, "nres_chkreply: send error.\n"); + temp->h_errno = TRY_AGAIN; + return (anslen); + } + hp = (HEADER *) answer; + if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { + prnt(P_INFO, "rcode = %d, ancount=%d.\n", hp->rcode, + ntohs(hp->ancount)); + switch (hp->rcode) { + case NXDOMAIN: + temp->h_errno = HOST_NOT_FOUND; + break; + case SERVFAIL: + temp->h_errno = TRY_AGAIN; + break; + case NOERROR: + temp->h_errno = NO_DATA; + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + temp->h_errno = NO_RECOVERY; + break; + } + return (-1); + } + return (anslen); +} + + + + + +/* + * getanswer() is copied from usr/src/lib/libresolv2/common/gethnamaddr.c. + */ + +static struct hostent * +getanswer(answer, anslen, qname, qtype) + const querybuf *answer; + int anslen; + const char *qname; + int qtype; +{ + const HEADER *hp; + const uchar_t *cp; + int n; + const uchar_t *eom; + char *bp, **ap, **hap; + int type, class, buflen, ancount, qdcount; + int haveanswer, had_error; + int toobig = 0; + char tbuf[MAXDNAME+1]; + const char *tname; + int (*name_ok) __P((const char *)); + + prnt(P_INFO, "getanswer: qname=%s\n", qname); + tname = qname; + host.h_name = NULL; + eom = answer->buf + anslen; + switch (qtype) { + case T_A: + case T_AAAA: + name_ok = res_hnok; + break; + case T_PTR: + name_ok = res_dnok; + break; + default: + return (NULL); + } + /* + * find first satisfactory answer + */ + hp = &answer->hdr; + ancount = ntohs(hp->ancount); + qdcount = ntohs(hp->qdcount); + bp = hostbuf; + buflen = sizeof (hostbuf); + cp = answer->buf + HFIXEDSZ; + if (qdcount != 1) { + h_errno = NO_RECOVERY; + return (NULL); + } + n = dn_expand(answer->buf, eom, cp, bp, buflen); + if ((n < 0) || !(*name_ok)(bp)) { + h_errno = NO_RECOVERY; + return (NULL); + } + cp += n + QFIXEDSZ; + if (qtype == T_A || qtype == T_AAAA) { + /* + * res_send() has already verified that the query name is the + * same as the one we sent; this just gets the expanded name + * (i.e., with the succeeding search-domain tacked on). + */ + n = strlen(bp) + 1; /* for the \0 */ + host.h_name = bp; + bp += n; + buflen -= n; + /* The qname can be abbreviated, but h_name is now absolute. */ + qname = host.h_name; + } + ap = host_aliases; + *ap = NULL; + host.h_aliases = host_aliases; + hap = h_addr_ptrs; + *hap = NULL; + host.h_addr_list = h_addr_ptrs; + haveanswer = 0; + had_error = 0; + while (ancount-- > 0 && cp < eom && !had_error) { + n = dn_expand(answer->buf, eom, cp, bp, buflen); + if ((n < 0) || !(*name_ok)(bp)) { + had_error++; + continue; + } + cp += n; /* name */ + type = _getshort(cp); + cp += INT16SZ; /* type */ + class = _getshort(cp); + cp += INT16SZ + INT32SZ; /* class, TTL */ + n = _getshort(cp); + cp += INT16SZ; /* len */ + if (class != C_IN) { + /* XXX - debug? syslog? */ + cp += n; + continue; /* XXX - had_error++ ? */ + } + if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) { + if (ap >= &host_aliases[MAXALIASES-1]) + continue; + n = dn_expand(answer->buf, eom, cp, tbuf, + sizeof (tbuf)); + if ((n < 0) || !(*name_ok)(tbuf)) { + had_error++; + continue; + } + cp += n; + /* Store alias. */ + *ap++ = bp; + n = strlen(bp) + 1; /* for the \0 */ + bp += n; + buflen -= n; + /* Get canonical name. */ + n = strlen(tbuf) + 1; /* for the \0 */ + if (n > buflen) { + had_error++; + continue; + } + strcpy(bp, tbuf); + host.h_name = bp; + bp += n; + buflen -= n; + continue; + } + if (qtype == T_PTR && type == T_CNAME) { + n = dn_expand(answer->buf, eom, cp, tbuf, + sizeof (tbuf)); + if ((n < 0) || !res_hnok(tbuf)) { + had_error++; + continue; + } + cp += n; + /* Get canonical name. */ + n = strlen(tbuf) + 1; /* for the \0 */ + if (n > buflen) { + had_error++; + continue; + } + strcpy(bp, tbuf); + tname = bp; + bp += n; + buflen -= n; + continue; + } + if (type != qtype) { + syslog(LOG_NOTICE|LOG_AUTH, + "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"", + qname, p_class(C_IN), p_type(qtype), + p_type(type)); + cp += n; + continue; /* XXX - had_error++ ? */ + } + switch (type) { + case T_PTR: + if (strcasecmp(tname, bp) != 0) { + syslog(LOG_NOTICE|LOG_AUTH, + AskedForGot, tname, bp); + cp += n; + continue; /* XXX - had_error++ ? */ + } + n = dn_expand(answer->buf, eom, cp, bp, buflen); + if ((n < 0) || !res_hnok(bp)) { + had_error++; + break; + } +/* + * #if MULTI_PTRS_ARE_ALIASES + * cp += n; + * if (!haveanswer) + * host.h_name = bp; + * else if (ap < &host_aliases[MAXALIASES-1]) + * *ap++ = bp; + * else + * n = -1; + * if (n != -1) { + * n = strlen(bp) + 1; * for the \0 * + * bp += n; + * buflen -= n; + * } + * break; + * #else + */ + host.h_name = bp; + h_errno = NETDB_SUCCESS; + return (&host); +/* + * #endif + */ + case T_A: + case T_AAAA: + if (strcasecmp(host.h_name, bp) != 0) { + syslog(LOG_NOTICE|LOG_AUTH, + AskedForGot, host.h_name, bp); + cp += n; + continue; /* XXX - had_error++ ? */ + } +#ifdef SUNW_REJECT_BOGUS_H_LENGTH + /* Don't accept unexpected address length */ + if (n != host.h_length) { + cp += n; + continue; + } + if (!haveanswer) { +#else + if (haveanswer) { + if (n != host.h_length) { + cp += n; + continue; + } + } else { +#endif + int nn; + + host.h_name = bp; + nn = (int)(strlen(bp) + 1); /* for the \0 */ + bp += nn; + buflen -= nn; + } + + bp += sizeof (align) - ((ulong_t)bp % sizeof (align)); + + if (bp + n >= &hostbuf[sizeof (hostbuf)]) { + prnt(P_INFO, "getanswer: size (%ld) too big\n", + (int)n); + had_error++; + continue; + } + if (hap >= &h_addr_ptrs[MAXADDRS-1]) { + if (!toobig++) + prnt(P_INFO, + "getanswer: Too many addresses (%d)\n", + MAXADDRS); + cp += n; + continue; + } + bcopy(cp, *hap++ = bp, n); + bp += n; + buflen -= n; + cp += n; + break; + default: + abort(); + } + if (!had_error) + haveanswer++; + } + if (haveanswer) { + *ap = NULL; + *hap = NULL; + if (!host.h_name) { + n = strlen(qname) + 1; /* for the \0 */ + if (n > buflen) + goto try_again; + strcpy(bp, qname); + host.h_name = bp; + bp += n; + buflen -= n; + } + h_errno = NETDB_SUCCESS; + return (&host); + } +try_again: + h_errno = TRY_AGAIN; + return (NULL); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ngethostbyname.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ngethostbyname.c new file mode 100644 index 0000000000..4d89237863 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ngethostbyname.c @@ -0,0 +1,536 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1993,1998 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* Taken from 4.1.3 ypserv resolver code. */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <ctype.h> +#include <netdb.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include <syslog.h> +#include "nres.h" +#include "prnt.h" + +#ifndef NO_DATA +#define NO_DATA NO_ADDRESS +#endif + +int h_errno; /* defined here */ + +static void nres_abort_xmit(struct nres *); +static struct nres *nres_setup(char *, + void (*)(void *, struct hostent *, ulong_t, struct cache_ent *, int), + struct cache_ent *); +static int nres_dosrch(struct nres *); +static int nres_register(struct nres *, int); +extern struct hostent *nres_getanswer(struct nres *); +extern int lookup_T_type(struct cache_ent *); +extern int lookup_AF_type(struct cache_ent *chl); + +/* + * these two routines return immediate errors in h_errno and null + * if they fail or the return a struct nres + */ + +struct nres * +nres_gethostbyname(name, handler, info) + char *name; + struct cache_ent *info; + void (*handler) (); +{ + struct nres *temp; + char *cp; + + + /* + * disallow names consisting only of digits/dots, unless they end in + * a dot. + */ + if (isdigit(name[0])) { + cp = name; + /* CONSTCOND */ + while (1) { + if (!*cp) { + if (*--cp == '.') + break; + h_errno = HOST_NOT_FOUND; + return ((struct nres *)0); + } + if (!isdigit(*cp) && *cp != '.') + break; + cp++; + } + } + + temp = nres_setup(name, handler, info); + + if (temp != NULL) { + temp->h_errno = TRY_AGAIN; + if (nres_dosrch(temp) >= 0) + return (temp); + else { + if (temp->udp_socket >= 0) + (void) close(temp->udp_socket); + if (temp->tcp_socket >= 0) + (void) close(temp->tcp_socket); + h_errno = temp->h_errno; + free((char *) temp); + return ((struct nres *) 0); + + } + } else { + prnt(P_INFO, "nres-gethostbyname:setup failed.\n"); + return ((struct nres *) - 1); + } + +} + + +/* + * NOTE: nres_gethostbyaddr() should never be used to lookup IPv4 mapped + * and tunnelled addresses. The client side getXbyY routine should + * have already check for this kind of lookup and translated to the + * proper IPv4 lookup. + */ +struct nres * +nres_gethostbyaddr(addr, len, type, handler, info) + char *addr; + int len; + int type; + struct cache_ent *info; + void (*handler) (); +{ + struct nres *temp; + unsigned char *uaddr = (unsigned char *)addr; + char qbuf[MAXDNAME], *qp; + int n; + + switch (type) { + case AF_INET: + (void) sprintf(qbuf, "%d.%d.%d.%d.in-addr.arpa", + (uaddr[3] & 0xff), (uaddr[2] & 0xff), + (uaddr[1] & 0xff), (uaddr[0] & 0xff)); + break; + case AF_INET6: + qp = qbuf; + for (n = IN6ADDRSZ - 1; n >= 0; n--) { + qp += sprintf(qp, "%x.%x.", + uaddr[n] & 0xf, (uaddr[n] >> 4) & 0xf); + } + strcpy(qp, "ip6.int"); + break; + default: + return ((struct nres *) 0); + } + + temp = nres_setup(qbuf, handler, info); + if (temp != NULL) { + temp->reverse = REVERSE_PTR; + if (type == AF_INET) + (void) memcpy(&(temp->theaddr.s_addr), addr, 4); + else + (void) memcpy(&(temp->theaddr6.s6_addr), addr, 16); + temp->h_errno = TRY_AGAIN; + if (nres_dosrch(temp) >= 0) + return (temp); + else { + if (temp->udp_socket >= 0) + (void) close(temp->udp_socket); + if (temp->tcp_socket >= 0) + (void) close(temp->tcp_socket); + h_errno = temp->h_errno; + free((char *) temp); + return ((struct nres *) 0); + + } + } else { + prnt(P_INFO, "nres-gethostbyaddr:setup failed.\n"); + return ((struct nres *) - 1); + } + +} + +/* + * A timeout has occured -- try to retransmit, if it fails call abort_xmit to + * decide to pursue the search or give up + */ + +static void +nres_dotimeout(as) + rpc_as *as; +{ + + struct nres *temp; + temp = (struct nres *) as->as_userptr; + + /* + * timeout + */ + prnt(P_INFO, "timeout.\n"); + temp->current_ns = temp->current_ns + 1; + + if (temp->using_tcp) { + (void) close(temp->tcp_socket); + temp->tcp_socket = -1; + (void) rpc_as_unregister(as); /* if you close it */ + } + + if (nres_xmit(temp) < 0) { + temp->h_errno = TRY_AGAIN; + (void) rpc_as_unregister(as); + nres_abort_xmit(temp); + } else { + if (temp->using_tcp) { + if (nres_register(temp, temp->tcp_socket) < 0) { + temp->h_errno = TRY_AGAIN; + nres_abort_xmit(temp); + } + } else { + if (nres_register(temp, temp->udp_socket) < 0) { + temp->h_errno = TRY_AGAIN; + nres_abort_xmit(temp); + } + } + } +} + +/* this advances the search with dosrch or gives up */ +/* if it gives up it calls the users 'done' function */ +/* this is the timeout way to call the user */ +static void +nres_abort_xmit(temp) + struct nres *temp; +{ + /* called on timeout */ + int give_up; + prnt(P_INFO, "nres_abort().\n"); + give_up = 0; + if (temp->search_index == 1) { + give_up = 1; + prnt(P_INFO, "Name server(s) seem to be down.\n"); + } else if (nres_dosrch(temp) < 0) + give_up = 1; + if (give_up) { + prnt(P_INFO, + "more srching aborted: would ret try_again to caller.\n"); + temp->h_errno = TRY_AGAIN; + if (temp->done) + (temp->done)((void *)temp, NULL, 0, + temp->userinfo, temp->h_errno); + if (temp->udp_socket >= 0) + (void) close(temp->udp_socket); + if (temp->tcp_socket >= 0) + (void) close(temp->tcp_socket); + free((char *) temp); + } +} + +/* + * try to pursue the search by calling nres_search and nres_xmit -- if both + * work then register an asynch reply to come to nres_dorcv or a timeout to + * nres_dotimeout + */ +/* + * 0 means that the search is continuing -1 means that the search is not + * continuing h_errno has the cause. + */ + +static int +nres_dosrch(temp) + struct nres *temp; +{ + int type; + + if (nres_search(temp) >= 0) { + type = temp->qtype; + prnt(P_INFO, "search \'%s\'.\n", temp->search_name); + temp->question_len = res_mkquery(QUERY, temp->search_name, + C_IN, type, (uchar_t *) NULL, 0, NULL, + (uchar_t *)temp->question, MAXPACKET); + if (temp->question_len < 0) { + temp->h_errno = NO_RECOVERY; + prnt(P_INFO, "res_mkquery --NO RECOVERY.\n"); + return (-1); + } + if (nres_xmit(temp) < 0) { + prnt(P_INFO, "nres_xmit() fails.\n"); + temp->h_errno = TRY_AGAIN; + return (-1); + } else { + if (temp->using_tcp) { + if (nres_register(temp, temp->tcp_socket) < 0) { + temp->h_errno = TRY_AGAIN; + return (-1); + } + } else { + if (nres_register(temp, temp->udp_socket) < 0) { + temp->h_errno = TRY_AGAIN; + return (-1); + } + } + } + return (0); + } + return (-1); +} + + +/* + * this processes an answer received asynchronously a nres_rcv is done to + * pick up the packet, if it fails we just return, otherwise we unregister + * the fd, check the reply. If the reply has an answer we call nres_getanswer + * to get the answer, otherwise there is no answer an we call nres_dosrch to + * press the search forward, if nres_dosrch works we return. If the search + * can not be continued or if we got the answer we call the users done + * routine + */ + +static void +nres_dorecv(as) + struct rpc_as *as; +{ + struct nres *temp; + struct nres *again; + struct hostent *theans; + int status; + struct in_addr **a; + struct in6_addr **a6; + void (*done) (); + + temp = (struct nres *) as->as_userptr; + theans = NULL; + errno = 0; + status = nres_rcv(temp); + if (status > 0) { + prnt(P_INFO, "Recieved chars=%d.\n", temp->answer_len); + if (verbose && verbose_out) p_query((uchar_t *)temp->answer); + } else if (status < 0) { + prnt(P_INFO, "nres_rcv() hard fails.\n"); + (void) rpc_as_unregister(as); /* socket was closed for us */ + nres_dotimeout(as); /* this may revive it */ + return; /* keep running */ + } else { + prnt(P_INFO, "nres_rcv() soft fails.\n"); + return; /* keep running */ + } + (void) rpc_as_unregister(as); + + /* reply part */ + temp->answer_len = nres_chkreply(temp); + + if (temp->answer_len < 0) { + if (errno == ECONNREFUSED) { + temp->h_errno = TRY_AGAIN; + goto out; + } + if (temp->h_errno == NO_DATA) + temp->got_nodata++; + if ((temp->h_errno != HOST_NOT_FOUND && + temp->h_errno != NO_DATA) || + (_res.options & RES_DNSRCH) == 0) + goto out; + } else { + prnt(P_INFO, "nres_getanswer().\n"); + theans = nres_getanswer(temp); + goto out; + } + prnt(P_INFO, "continuing search... .\n"); + if (nres_dosrch(temp) < 0) + goto out; + return; /* keep running with new search */ + +out: + prnt(P_INFO, "done with this case.\n"); + /* answer resolution */ + if ((theans == NULL) && temp->got_nodata) { + temp->h_errno = NO_DATA; + prnt(P_INFO, "no_data.\n"); + } + + + done = temp->done; + if (theans) { + if (temp->reverse == REVERSE_PTR) { + /* raise security */ + theans->h_addrtype = temp->af_type; + if (theans->h_addrtype == AF_INET) { + theans->h_length = 4; + theans->h_addr_list[0] = + (char *) &(temp->theaddr); + } else { + theans->h_length = 16; + theans->h_addr_list[0] = + (char *) &(temp->theaddr6); + } + theans->h_addr_list[1] = (char *) 0; + if (temp->udp_socket >= 0) + (void) close(temp->udp_socket); + if (temp->tcp_socket >= 0) + (void) close(temp->tcp_socket); + again = nres_setup(theans->h_name, temp->done, + temp->userinfo); + if (again != NULL) { + if (again->af_type == AF_INET) { + again->qtype = T_A; + again->theaddr = temp->theaddr; + } else { + again->qtype = T_AAAA; + again->theaddr6 = temp->theaddr6; + } + again->reverse = REVERSE_A; + again->h_errno = TRY_AGAIN; + if (nres_dosrch(again) < 0) { + if (done) + (*done) (again, NULL, 0, + again->userinfo, again->h_errno); + if (again->udp_socket >= 0) + (void) close(again->udp_socket); + if (again->tcp_socket >= 0) + (void) close(again->tcp_socket); + free((char *) again); + } + } else { + /* memory error */ + temp->h_errno = TRY_AGAIN; + if (done) + (*done) (temp, NULL, 0, temp->userinfo, + temp->h_errno); + + } + free((char *) temp); + return; + } else if (temp->reverse == REVERSE_A) { + int found_addr = FALSE; + if (temp->af_type == AF_INET) { + for (a = (struct in_addr **)theans->h_addr_list; + *a; a++) { + if (memcmp(*a, &(temp->theaddr), + theans->h_length) == 0) { + if (done) + (*done) (temp, + theans, temp->ttl, + temp->userinfo, + temp->h_errno); + done = NULL; + found_addr = TRUE; + break; + } + } + if (!found_addr) { /* weve been spoofed */ + char bb[100]; + (void) inet_ntop(AF_INET, + (void *)&temp->theaddr, bb, 100); + prnt(P_ERR, + "nres_gethostbyaddr: %s != %s.\n", + temp->name, bb); + theans = NULL; + temp->h_errno = HOST_NOT_FOUND; + } + } else { + /* AF_INET6 */ + for (a6 = (struct in6_addr **) + theans->h_addr_list; *a6; a6++) { + if (memcmp(*a6, &(temp->theaddr6), + theans->h_length) == 0) { + if (done) + (*done) (temp, + theans, temp->ttl, + temp->userinfo, + temp->h_errno); + done = NULL; + found_addr = TRUE; + break; + } + } + if (!found_addr) { /* weve been spoofed */ + char bb[100]; + (void) inet_ntop(AF_INET6, + (void *)&temp->theaddr6, bb, 100); + prnt(P_ERR, + "nres_gethostbyaddr: %s != %s.\n", + temp->name, bb); + theans = NULL; + temp->h_errno = HOST_NOT_FOUND; + } + } + } + } + if (done) + (*done) (temp, theans, temp->ttl, + temp->userinfo, temp->h_errno); + if (temp->udp_socket >= 0) + (void) close(temp->udp_socket); + if (temp->tcp_socket >= 0) + (void) close(temp->tcp_socket); + free((char *) temp); + return; /* done running */ + +} + +static int +nres_register(a, b) + int b; + struct nres *a; +{ + a->nres_rpc_as.as_fd = b; + a->nres_rpc_as.as_timeout_flag = TRUE; + a->nres_rpc_as.as_timeout = nres_dotimeout; + a->nres_rpc_as.as_recv = nres_dorecv; + a->nres_rpc_as.as_userptr = (char *) a; + return (rpc_as_register(&(a->nres_rpc_as))); +} + +static struct nres * +nres_setup(name, done, userinfo) + char *name; + struct cache_ent *userinfo; + void (*done) (); +{ + struct nres *tmp; + + tmp = (struct nres *) calloc(1, sizeof (struct nres)); + if (tmp == NULL) + return (tmp); + (void) strncpy(tmp->name, name, MAXDNAME); + tmp->tcp_socket = -1; + tmp->udp_socket = -1; + tmp->done = done; + tmp->userinfo = userinfo; + tmp->qtype = lookup_T_type(userinfo); + tmp->af_type = lookup_AF_type(userinfo); + return (tmp); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres.h new file mode 100644 index 0000000000..b416731f17 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres.h @@ -0,0 +1,97 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1993-1999 by Sun Microsystems, Inc. + * All rights reserved. + */ + +/* Taken from 4.1.3 ypserv resolver code. */ + +#ifndef _NRES_H +#define _NRES_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <netdb.h> +#include <arpa/nameser.h> +#include "rpc_as.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if PACKETSZ > 1024 +#define MAXPACKET PACKETSZ +#else +#define MAXPACKET 1024 +#endif +#define REVERSE_PTR 1 +#define REVERSE_A 2 + +struct nres { + rpc_as nres_rpc_as; + struct cache_ent *userinfo; + void (*done) (void *, struct hostent *, + ulong_t, struct cache_ent *, int); + int af_type; /* AF_INET or AF_INET6 */ + int qtype; /* query type: T_A, T_AAAA, T_PTR */ + + int h_errno; + int reverse; /* used for gethostbyaddr */ + struct in_addr theaddr; /* gethostbyaddr */ + struct in6_addr theaddr6; /* gethostbyaddr IPv6 */ + char name[MAXDNAME + 1]; /* gethostbyame name */ + char search_name[2 * MAXDNAME + 2]; + int search_index; /* 0 up as we chase path */ + char question[MAXPACKET]; + char answer[MAXPACKET]; + int using_tcp; /* 0 ->udp in use */ + int udp_socket; + int tcp_socket; + int got_nodata; /* no_data rather than name_not_found */ + int question_len; + int answer_len; + int current_ns; + int retries; + int ttl; /* ttl value from response */ +}; + +extern struct netconfig *udp_nconf4; +extern struct netconfig *udp_nconf6; + +extern int nres_xmit(struct nres *); +extern struct hostent *nres_getanswer(struct nres *); +extern int nres_search(struct nres *); +extern int nres_rcv(struct nres *); +extern int nres_chkreply(struct nres *); +extern struct nres *nres_gethostbyname(char *, + void (*)(void *, struct hostent *, ulong_t, struct cache_ent *, int), + struct cache_ent *); +extern struct nres *nres_gethostbyaddr(char *, int, int, + void (*)(void *, struct hostent *, ulong_t, struct cache_ent *, int), + struct cache_ent *); + +#ifdef __cplusplus +} +#endif + +#endif /* _NRES_H */ diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_rcv.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_rcv.c new file mode 100644 index 0000000000..852056cebf --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_rcv.c @@ -0,0 +1,154 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1993,1998 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* Taken from 4.1.3 ypserv resolver code. */ + +/* + * Send query to name server and wait for reply. + */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <syslog.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include "nres.h" +#include "prnt.h" + + +int +nres_rcv(struct nres *tnr) +{ + register int n; + int resplen; + ushort_t len; + char *cp; + HEADER *hp = (HEADER *)tnr->question; + HEADER *anhp = (HEADER *)tnr->answer; + int s; + int truncated = 0; + char junk[512]; + if (tnr->using_tcp == 0) { + s = tnr->udp_socket; + + if ((resplen = recv(s, tnr->answer, MAXPACKET, 0)) <= 0) { + prnt(P_ERR, "recv failed: %s.\n", strerror(errno)); + return (-1); + } + if (hp->id != anhp->id) { + /* + * response from old query, ignore it + */ + prnt(P_INFO, "old answer.\n"); + if (verbose && verbose_out) + p_query((uchar_t *)tnr->answer); + return (0); /* wait again */ + } + if (!(_res.options & RES_IGNTC) && anhp->tc) { + /* + * get rest of answer + */ + prnt(P_INFO, "truncated answer..\n"); + (void) close(tnr->udp_socket); + tnr->udp_socket = -1; + tnr->using_tcp = 1; + return (-1); + } + tnr->answer_len = resplen; + return (1); + } else { + /* tcp case */ + s = tnr->tcp_socket; + /* + * Receive length & response + */ + cp = tnr->answer; + len = sizeof (short); + while (len != 0 && (n = read(s, (char *)cp, (int)len)) > 0) { + cp += n; + len -= n; + } + if (n <= 0) { + prnt(P_ERR, "read failed: %s.\n", strerror(errno)); + (void) close(s); + tnr->tcp_socket = -1; + return (-1); + } + cp = tnr->answer; + if ((resplen = ntohs(*(ushort_t *)cp)) > MAXPACKET) { + prnt(P_INFO, "response truncated.\n"); + len = MAXPACKET; + truncated = 1; + } else + len = resplen; + while (len != 0 && (n = read(s, (char *)cp, (int)len)) > 0) { + cp += n; + len -= n; + } + if (n <= 0) { + prnt(P_ERR, "read failed: %s.\n", strerror(errno)); + (void) close(s); + tnr->tcp_socket = -1; + return (-1); + } + if (truncated) { + /* + * Flush rest of answer so connection stays in synch. + */ + anhp->tc = 1; + len = resplen - MAXPACKET; + while (len != 0) { + n = (len > sizeof (junk) ? sizeof (junk) : len); + if ((n = read(s, junk, n)) > 0) + len -= n; + else + break; + } + } + if (hp->id != anhp->id) { + /* + * response from old query, ignore it + */ + prnt(P_INFO, "old answer.\n"); + if (verbose && verbose_out) + p_query((uchar_t *)tnr->answer); + return (0); /* wait again */ + + } + tnr->answer_len = resplen; + return (1); + } +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_search.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_search.c new file mode 100644 index 0000000000..8c0bc2005b --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_search.c @@ -0,0 +1,157 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1993,1998 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* Taken from 4.1.3 ypserv resolver code. */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <syslog.h> +#include <stdlib.h> +#include <ctype.h> +#include <netdb.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include "nres.h" +#include "prnt.h" + +static void nres_querydomain(char *, char *, char *); +static char *nres_hostalias(char *); + +nres_search(struct nres *block) +{ + register char *cp, *domain; + int n; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) + return (-1); + + block->retries = 0; /* start clock */ + if (block->search_index < 0) + return (-1); + /* only try exact match for reverse cases */ + if (block->reverse) { + (void) nres_querydomain(block->name, (char *)NULL, + block->search_name); + block->search_index = -1; + return (0); + } + + for (cp = block->name, n = 0; *cp; cp++) + if (*cp == '.') + n++; + /* n indicates the presence of trailing dots */ + + if (block->search_index == 0) { + if (n == 0 && (cp = nres_hostalias(block->name))) { + (void) strncpy(block->search_name, cp, 2 * MAXDNAME); + block->search_index = -1; /* if hostalias try 1 name */ + return (0); + } + } + if ((n == 0 || *--cp != '.') && (_res.options & RES_DEFNAMES)) { + domain = _res.dnsrch[block->search_index]; + if (domain) { + (void) nres_querydomain(block->name, domain, + block->search_name); + block->search_index++; + return (0); + } + } + if (n) { + (void) nres_querydomain(block->name, (char *)NULL, + block->search_name); + block->search_index = -1; + return (0); + } + block->search_index = -1; + return (-1); +} + +/* + * Perform a call on res_query on the concatenation of name and domain, + * removing a trailing dot from name if domain is NULL. + */ +static void +nres_querydomain(char *name, char *domain, char *nbuf) +{ + int n; + + if (domain == NULL) { + /* + * Check for trailing '.'; copy without '.' if present. + */ + n = strlen(name) - 1; + if (name[n] == '.') { + (void) memcpy(nbuf, name, n); + nbuf[n] = '\0'; + } else + (void) strcpy(nbuf, name); + } else + (void) sprintf(nbuf, "%.*s.%.*s", + MAXDNAME, name, MAXDNAME, domain); + + prnt(P_INFO, "nres_querydomain(, %s).\n", nbuf); +} + +static char * +nres_hostalias(char *name) +{ + register char *C1, *C2; + FILE *fp; + char *file; + char buf[BUFSIZ]; + static char abuf[MAXDNAME]; + + file = getenv("HOSTALIASES"); + if (file == NULL || (fp = fopen(file, "r")) == NULL) + return (NULL); + buf[sizeof (buf) - 1] = '\0'; + while (fgets(buf, sizeof (buf), fp)) { + for (C1 = buf; *C1 && !isspace(*C1); ++C1); + if (!*C1) + break; + *C1 = '\0'; + if (!strcasecmp(buf, name)) { + while (isspace(*++C1)); + if (!*C1) + break; + for (C2 = C1 + 1; *C2 && !isspace(*C2); ++C2); + abuf[sizeof (abuf) - 1] = *C2 = '\0'; + (void) strncpy(abuf, C1, sizeof (abuf) - 1); + (void) fclose(fp); + return (abuf); + } + } + (void) fclose(fp); + return (NULL); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_send.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_send.c new file mode 100644 index 0000000000..98a4a5a3e9 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_send.c @@ -0,0 +1,166 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1993-1999 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* Taken from 4.1.3 ypserv resolver code. */ + +/* + * Send query to name server and wait for reply. + */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <syslog.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include <string.h> +#include <strings.h> +#include "nres.h" +#include "prnt.h" + + +nres_xmit(struct nres *tnr) +{ + char *buf; + int buflen; + int v_circuit; + ushort_t len; + + struct iovec iov[2]; + + buf = tnr->question; + buflen = tnr->question_len; + + prnt(P_INFO, "nres_xmit().\n"); + if (verbose && verbose_out) p_query((uchar_t *)buf); + if (!(_res.options & RES_INIT)) + if (res_init() == -1) { + return (-1); + } + v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ; + if (tnr->using_tcp) + v_circuit = 1; + if (v_circuit) + tnr->using_tcp = 1; + + prnt(P_INFO, "this is retry %d.\n", tnr->retries); + + if (_res.nscount <= 0) { + prnt(P_INFO, "nres_xmit -- no name servers\n"); + return (-1); + } + if (tnr->retries >= _res.retry) { + prnt(P_INFO, + "nres_xmit -- retries exausted %d.\n", _res.retry); + return (-1); + } + if (tnr->current_ns >= _res.nscount) { + tnr->current_ns = 0; + tnr->retries = tnr->retries + 1; + } + tnr->nres_rpc_as.as_timeout_remain.tv_sec = (_res.retrans << + (tnr->retries)) / _res.nscount; + tnr->nres_rpc_as.as_timeout_remain.tv_usec = 0; + if (tnr->nres_rpc_as.as_timeout_remain.tv_sec < 1) + tnr->nres_rpc_as.as_timeout_remain.tv_sec = 1; + + for (; tnr->current_ns < _res.nscount; tnr->current_ns++) { + prnt(P_INFO, + "Querying server (# %d) address = %s.\n", tnr->current_ns + 1, + inet_ntoa(_res.nsaddr_list[tnr->current_ns].sin_addr)); + if (v_circuit) { + + /* + * Use virtual circuit. + */ + if (tnr->tcp_socket < 0) { + tnr->tcp_socket = socket(AF_INET, + SOCK_STREAM, 0); + if (tnr->tcp_socket < 0) { + prnt(P_ERR, "socket failed: %s.\n", + strerror(errno)); + if (tnr->udp_socket < 0) + return (-1); + } + if (connect(tnr->tcp_socket, + (struct sockaddr *)&(_res.nsaddr_list[tnr->current_ns]), + sizeof (struct sockaddr)) < 0) { + prnt(P_ERR, "connect failed: %s.\n", + strerror(errno)); + (void) close(tnr->tcp_socket); + tnr->tcp_socket = -1; + continue; + } + } + /* + * Send length & message + */ + len = htons((ushort_t)buflen); + iov[0].iov_base = (caddr_t)&len; + iov[0].iov_len = sizeof (len); + iov[1].iov_base = tnr->question; + iov[1].iov_len = tnr->question_len; + if (writev(tnr->tcp_socket, iov, 2) != + sizeof (len) + buflen) { + prnt(P_ERR, "write failed: %s.\n", + strerror(errno)); + (void) close(tnr->tcp_socket); + tnr->tcp_socket = -1; + continue; + } + /* reply will come on tnr->tcp_socket */ + } else { + /* + * Use datagrams. + */ + if (tnr->udp_socket < 0) + tnr->udp_socket = socket(AF_INET, + SOCK_DGRAM, 0); + if (tnr->udp_socket < 0) + return (-1); + + if (sendto(tnr->udp_socket, buf, buflen, 0, + (struct sockaddr *)&_res.nsaddr_list[tnr->current_ns], + sizeof (struct sockaddr)) != buflen) { + prnt(P_ERR, "sendto failed: %s.\n", + strerror(errno)); + continue; + } else { + if (tnr->retries == 0) + return (0); + } + } + } + return (0); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/prnt.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/prnt.h new file mode 100644 index 0000000000..d89a084858 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/prnt.h @@ -0,0 +1,48 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1993,1998 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#ifndef _PRNT_H +#define _PRNT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#define P_INFO 0 +#define P_ERR 1 + +extern int verbose_out; +extern int verbose; + +extern void prnt(int info_or_err, char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* _PRNT_H */ diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.c new file mode 100644 index 0000000000..a039bcb612 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.c @@ -0,0 +1,327 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1993,1998 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* Taken from 4.1.3 ypserv resolver code. */ + +#include "rpc_as.h" +#include <sys/types.h> +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> +#include <poll.h> +#include <string.h> + +#define CHECK_PARENT_SECS 600 /* every 10 min */ + +extern pid_t ppid; +extern void cleanup(int); + +#define POLLFD_EXTEND 512 + +static rpc_as **rpc_as_handles; +static pollfd_t *rpc_as_pollset; +static int rpc_as_max_pollfd = -1; +static int rpc_as_pollfd_allocd = 0; + +pollfd_t * +rpc_as_get_pollset() +{ + return (rpc_as_pollset); +} + +int +rpc_as_get_max_pollfd() +{ + return (rpc_as_max_pollfd); +} + +static bool_t rpc_as_init() +{ + int i; + + if (rpc_as_handles != NULL) + return (TRUE); + + rpc_as_handles = (rpc_as **) + calloc(POLLFD_EXTEND, sizeof (rpc_as *)); + if (rpc_as_handles == NULL) { + return (FALSE); + } + + rpc_as_pollset = (pollfd_t *)calloc(POLLFD_EXTEND, sizeof (pollfd_t)); + if (rpc_as_pollset == NULL) { + free(rpc_as_handles); + rpc_as_handles = NULL; + return (FALSE); + } + rpc_as_pollfd_allocd = POLLFD_EXTEND; + + for (i = 0; i < rpc_as_pollfd_allocd; i++) { + rpc_as_pollset[i].fd = -1; + rpc_as_pollset[i].events = 0; + rpc_as_pollset[i].revents = 0; + rpc_as_handles[i] = NULL; + } + + return (TRUE); +} + +/* + * Activate a asynchronous handle. + */ +int +rpc_as_register(rpc_as *xprt) +{ + + if ((rpc_as_handles == NULL) && (!rpc_as_init())) + return (-1); + + if (xprt->as_fd < 0) + return (-1); /* can't register less than zero */ + + /* + * If the descriptor is bigger than the pollset and handles + * array, grow it by POLLFD_EXTEND until it is large enough. + */ + if (xprt->as_fd >= rpc_as_pollfd_allocd) { + int i = rpc_as_pollfd_allocd; + pollfd_t *ptmp; + rpc_as **htmp; + + do { + rpc_as_pollfd_allocd += POLLFD_EXTEND; + } while (xprt->as_fd >= rpc_as_pollfd_allocd); + + ptmp = realloc(rpc_as_pollset, + sizeof (pollfd_t) * rpc_as_pollfd_allocd); + if (ptmp == NULL) { + rpc_as_pollfd_allocd = i; + return (-1); + } + + htmp = realloc(rpc_as_handles, + sizeof (rpc_as *) * rpc_as_pollfd_allocd); + if (htmp == NULL) { + rpc_as_pollfd_allocd = i; + return (-1); + } + + /* + * Initialize the new elements + */ + rpc_as_pollset = ptmp; + rpc_as_handles = htmp; + for (; i < rpc_as_pollfd_allocd; i++) { + rpc_as_pollset[i].fd = -1; + rpc_as_pollset[i].events = 0; + rpc_as_pollset[i].revents = 0; + rpc_as_handles[i] = NULL; + } + } + + rpc_as_handles[xprt->as_fd] = xprt; + rpc_as_pollset[xprt->as_fd].fd = xprt->as_fd; + rpc_as_pollset[xprt->as_fd].events = + POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND; + rpc_as_pollset[xprt->as_fd].revents = 0; + if (xprt->as_fd > rpc_as_max_pollfd) + rpc_as_max_pollfd = xprt->as_fd; + return (0); +} + +static void +remove_pollfd(int fd) +{ + if (fd < 0 || fd > rpc_as_max_pollfd) + return; + + /* + * Don't shrink handles, pollset, or rpc_as_max_pollfd for now + */ + rpc_as_handles[fd] = (rpc_as *)0; + rpc_as_pollset[fd].fd = -1; + rpc_as_pollset[fd].events = 0; + rpc_as_pollset[fd].revents = 0; +} + +/* + * De-activate an asynchronous handle. + */ +int +rpc_as_unregister(rpc_as *xprt) +{ + if ((rpc_as_handles == NULL) && (!rpc_as_init())) + return (-1); + if (xprt->as_fd < 0) + return (-1); /* can't unregister less than zero */ + + if ((xprt->as_fd <= rpc_as_max_pollfd) && + (rpc_as_handles[xprt->as_fd] == xprt)) { + remove_pollfd(xprt->as_fd); + return (0); + } + return (-1); +} + +/* + * Check through each element of the poll set looking for returned + * events, if found and it corresponds to an active xprt, call as_recv + * and decrement pollretval so a later svc_getreq_poll can + * be called. + */ +void +rpc_as_rcvreq_poll(pollfd_t *pollset, int *pollretval) +{ + int i; + rpc_as *xprt; + + if ((rpc_as_handles == NULL) && (!rpc_as_init())) + return; + + for (i = 0; i <= rpc_as_max_pollfd; i++) { + pollfd_t *p = &pollset[i]; + if (p->revents) { + /* fd has input waiting */ + if (p->revents & POLLNVAL) { + remove_pollfd(p->fd); + *pollretval -= 1; + continue; + } + + xprt = rpc_as_handles[p->fd]; + if (xprt) { + if (xprt->as_recv) + xprt->as_recv(xprt, p->fd); + else + (void) rpc_as_unregister(xprt); + /* + * Clear the event + */ + p->revents = 0; + *pollretval -= 1; + } + } + } +} + +struct timeval +rpc_as_get_timeout() +{ + int tsecs; + int tusecs; + struct timeval now; + struct timeval ans; + static struct timeval last; + struct rpc_as **rhd; + int sock; + + ans.tv_sec = CHECK_PARENT_SECS; /* check parent time */ + ans.tv_usec = 0; + + if ((rpc_as_handles == NULL) && (!rpc_as_init())) + return (ans); + + /* Calculate elapsed time */ + (void) gettimeofday(&now, (struct timezone *)0); + if (last.tv_sec) { + tsecs = now.tv_sec - last.tv_sec; + tusecs = now.tv_usec - last.tv_usec; + last = now; + } else { + last = now; + tsecs = 0; + tusecs = 0; + } + while (tusecs < 0) { + tusecs += 1000000; + tsecs--; + } + if (tsecs < 0) + tsecs = 0; + + rhd = rpc_as_handles; + + for (sock = 0; sock <= rpc_as_max_pollfd; sock++) { + if (rhd[sock] == (struct rpc_as *)NULL) + continue; + + if (rhd[sock]->as_timeout_flag) { + + rhd[sock]->as_timeout_remain.tv_sec -= tsecs; + rhd[sock]->as_timeout_remain.tv_usec -= tusecs; + + while (rhd[sock]->as_timeout_remain.tv_usec < 0) { + rhd[sock]->as_timeout_remain.tv_sec--; + rhd[sock]->as_timeout_remain.tv_usec += 1000000; + } + if (rhd[sock]->as_timeout_remain.tv_sec < 0) { + rhd[sock]->as_timeout_remain.tv_sec = 0; + rhd[sock]->as_timeout_remain.tv_usec = 0; + } + + if (timercmp(&(rhd[sock]->as_timeout_remain), &ans, + < /*EMPTY*/)) + ans = rhd[sock]->as_timeout_remain; + } + } + + return (ans); +} + +void +rpc_as_timeout(struct timeval twaited) +{ + + struct rpc_as **rhd; + int sock; + + /* ppid only set when using transient from parent (nisd) */ + if (ppid && kill(ppid, 0)) + cleanup(0); + + if ((rpc_as_handles == NULL) && (!rpc_as_init())) + return; + rhd = rpc_as_handles; + + for (sock = 0; sock <= rpc_as_max_pollfd; sock++) { + if (rhd[sock] == (struct rpc_as *)NULL) + continue; + if (rhd[sock]->as_timeout_flag) { + if ((timercmp(&(rhd[sock]->as_timeout_remain), &twaited, + < /*EMPTY*/)) || + (timercmp(&(rhd[sock]->as_timeout_remain), &twaited, + == /*EMPTY*/))) { + if (rhd[sock]->as_timeout) + rhd[sock]->as_timeout(rhd[sock]); + else + (void) rpc_as_unregister(rhd[sock]); + + } + } + } +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.h new file mode 100644 index 0000000000..a7fa56e284 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.h @@ -0,0 +1,63 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1993,1998 by Sun Microsystems, Inc. + * All rights reserved. + */ + +/* Taken from 4.1.3 ypserv resolver code. */ + +#ifndef _RPC_AS_H +#define _RPC_AS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <rpc/types.h> +#include <poll.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct rpc_as { + char *as_userptr; /* anything you like */ + struct timeval as_timeout_remain; + int as_fd; + bool_t as_timeout_flag; /* set if timeouts wanted */ + void (*as_timeout)(); /* routine to call if timeouts wanted */ + void (*as_recv)(); /* routine to call if data is present */ +}; +typedef struct rpc_as rpc_as; + +extern struct timeval rpc_as_get_timeout(void); +extern pollfd_t *rpc_as_get_pollset(void); +extern int rpc_as_get_max_pollfd(void); +extern void rpc_as_timeout(struct timeval); +extern void rpc_as_rcvreq_poll(pollfd_t *, int *); +extern int rpc_as_register(rpc_as *); +extern int rpc_as_unregister(rpc_as *); + +#ifdef __cplusplus +} +#endif + +#endif /* _RPC_AS_H */ diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/svc_run_as.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/svc_run_as.c new file mode 100644 index 0000000000..f6df3423c9 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/svc_run_as.c @@ -0,0 +1,126 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1993-1998 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* Taken from 4.1.3 ypserv resolver code. */ + +/* + * This is an example of using rpc_as.h an asynchronous polling + * mechanism, asynchronously polled fds are combined with the + * service fds. The minimum timeout is calculated, and + * the user waits for that timeout or activity on either the + * async pollset or the svc pollset. + */ + +#include <rpc/rpc.h> +#include <errno.h> +#include <stdlib.h> +#include "rpc_as.h" +#include <stropts.h> +#include <string.h> +#include <poll.h> +#include "prnt.h" + +extern int __rpc_timeval_to_msec(struct timeval *t); + +/* + * Merge two arrays of pollfd's, assumptions: + * Arrays are ordered so element N contains descriptor N + * Out array is as large as max(asize, bsize) + */ +static void +merge_pollfds(struct pollfd *out, struct pollfd *a, int asize, + struct pollfd *b, int bsize) +{ + int i; + int outsize; + + outsize = (asize > bsize) ? asize : bsize; + + for (i = 0; i < outsize; i++) { + if (i < asize && a[i].fd != -1) + out[i] = a[i]; + else if (i < bsize && b[i].fd != -1) + out[i] = b[i]; + else + out[i].fd = -1; + } +} + +void +svc_run_as() +{ + struct timeval timeout; + struct pollfd *svc_pollset = NULL; + struct pollfd *as_pollset; + int nfds = 0; + int max_fds; + int as_max_pollfd; + int pollret = 0; + + for (;;) { + as_max_pollfd = rpc_as_get_max_pollfd() + 1; + as_pollset = rpc_as_get_pollset(); + + max_fds = (as_max_pollfd > svc_max_pollfd) ? + as_max_pollfd : svc_max_pollfd; + if (nfds != max_fds) { + svc_pollset = realloc(svc_pollset, + sizeof (pollfd_t) * max_fds); + nfds = max_fds; + } + + if (nfds == 0) + break; /* None waiting, hence return */ + + /* + * Merge together rpc_as_get_pollset and svc_pollfd + */ + merge_pollfds(svc_pollset, as_pollset, as_max_pollfd, + svc_pollfd, svc_max_pollfd); + + timeout = rpc_as_get_timeout(); + + switch ((pollret = poll(svc_pollset, nfds, + __rpc_timeval_to_msec(&timeout)))) { + case -1: + if (errno == EINTR) { + continue; + } + prnt(P_ERR, "svc_run: - poll failed: %s\n", + strerror(errno)); + return; + case 0: + rpc_as_timeout(timeout); + break; + default: + rpc_as_rcvreq_poll(svc_pollset, &pollret); + + svc_getreq_poll(svc_pollset, pollret); + } + } +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ypresolv_proc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ypresolv_proc.c new file mode 100644 index 0000000000..eedc39cecb --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ypresolv_proc.c @@ -0,0 +1,677 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * The resolv code was lifted from 4.1.3 ypserv. References to child/pid + * have been changed to cache/nres to reflect what is really happening. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* ******************** include headers ****************************** */ +#include <netdb.h> +#include <ctype.h> +#include <syslog.h> +#include <rpc/rpc.h> +#include <rpcsvc/yp_prot.h> +#include <netdir.h> +#include <stdlib.h> +#include <strings.h> +#include <arpa/inet.h> +#include "../resolv_common.h" +#include "prnt.h" +#include "nres.h" + +#define RESP_NOW 0 +#define RESP_LATER 2 + +#define NQTIME 10 /* minutes */ +#define PQTIME 30 /* minutes */ + +/* + * Cache entries for storing rpc.nisd req and resolv req info + */ +struct cache_ent { + struct nres *nres; + datum key; + datum val; + char *map; + struct timeval enqtime; + int h_errno; + SVCXPRT *xprt; + struct netbuf caller; + char buf[MAX_UADDR]; + unsigned long xid; + unsigned long ttl; + struct cache_ent *next_cache_ent; +}; + + +int lookup_AF_type(struct cache_ent *); + +/* ******************** static vars and funcs ************************ */ +static struct cache_ent *cache_head = NULL; +static ulong_t svc_setxid(SVCXPRT *, ulong_t); +static void my_done(void *, struct hostent *, ulong_t, struct cache_ent *, + int); +static int yp_matchdns(char *, datum, datum *, unsigned *, SVCXPRT *); +static void free_cache_ent(struct cache_ent *x); + +/* ******************** extern vars and funcs ************************ */ +extern int verbose; +extern SVCXPRT *reply_xprt4; +extern SVCXPRT *reply_xprt6; + +static void +yp_resolv(sa_family_t af, void *req, SVCXPRT *transp) +{ + struct ypresp_val resp; + int respond = RESP_NOW; + int i; + char tmp[12]; /* max size of 9 rounded up to multiple of 4 bytes */ + char buf[MAX_UADDR]; + struct netbuf *nbuf; + struct bogus_data *bd = NULL; + struct ypfwdreq_key4 *req4 = (struct ypfwdreq_key4 *)req; + struct ypfwdreq_key6 *req6 = (struct ypfwdreq_key6 *)req; + in_port_t port; + + resp.valdat.dptr = NULL; + resp.valdat.dsize = 0; + + /* Set the reply_xprt: xid and caller here, to fit yp_matchdns() */ + if (af == AF_INET6) { + (void) inet_ntop(AF_INET6, req6->addr, buf, sizeof (buf)); + port = req6->port; + } else { + struct in_addr in4; + /* + * This doesn't make much sense, but the for some reason + * the caller converted req->ip to host byte order, and in + * the name of backward compatibility... + */ + in4.s_addr = htonl(req4->ip); + (void) strcpy(buf, inet_ntoa(in4)); + port = req4->port; + } + + (void) snprintf(tmp, sizeof (tmp), ".%u.%u", + (port>>8) & 0x00ff, port & 0x00ff); + (void) strcat(buf, tmp); + if ((nbuf = uaddr2taddr((af == AF_INET6) ? udp_nconf6 : udp_nconf4, + buf)) == NULL) { + prnt(P_ERR, "can't get args.\n"); + return; + } + if (nbuf->len > MAX_UADDR) { /* added precaution */ + prnt(P_ERR, "uaddr too big for cache.\n"); + netdir_free((void*)nbuf, ND_ADDR); + return; + } + SETCALLER(transp, nbuf); + /* + * Set su_tudata.addr for sendreply() t_sendudata() + * since we never did a recv on this unreg'ed xprt. + */ + if (!bd) { /* just set maxlen and buf once */ + bd = getbogus_data(transp); + bd->su_tudata.addr.maxlen = GETCALLER(transp)->maxlen; + bd->su_tudata.addr.buf = GETCALLER(transp)->buf; + } + bd->su_tudata.addr.len = nbuf->len; + netdir_free((void*)nbuf, ND_ADDR); + (void) svc_setxid(transp, (af == AF_INET6) ? req6->xid : req4->xid); + + respond = yp_matchdns((af == AF_INET6) ? req6->map : req4->map, + (af == AF_INET6) ? req6->keydat : req4->keydat, + &resp.valdat, &resp.status, transp); + + if (respond == RESP_NOW) + if (!svc_sendreply(transp, + (xdrproc_t)xdr_ypresp_val, (char *)&resp)) { + prnt(P_ERR, "can't respond to rpc request.\n"); + } +} + +void +dispatch(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + struct ypfwdreq_key4 req4; + struct ypfwdreq_key6 req6; + + switch (rqstp->rq_proc) { + case NULLPROC: + if (!svc_sendreply(transp, xdr_void, 0)) + prnt(P_ERR, "can't respond to ping.\n"); + break; + case YPDNSPROC4: + req4.map = NULL; + req4.keydat.dptr = NULL; + req4.xid = 0; + req4.ip = 0; + req4.port = 0; + + if (!svc_getargs(transp, xdr_ypfwdreq_key4, (char *)&req4)) { + prnt(P_ERR, "can't get args.\n"); + svcerr_decode(transp); + return; + } + + /* args were ok: don't wait for resolver */ + if (!svc_sendreply(transp, xdr_void, 0)) + prnt(P_ERR, "can't ack nisd req.\n"); + + yp_resolv(AF_INET, &req4, reply_xprt4); + + if (!svc_freeargs(transp, xdr_ypfwdreq_key4, (char *)&req4)) { + prnt(P_ERR, "can't free args.\n"); + exit(1); + } + + break; + case YPDNSPROC6: + req6.map = NULL; + req6.keydat.dptr = NULL; + req6.xid = 0; + req6.addr = 0; + req6.port = 0; + + if (!svc_getargs(transp, xdr_ypfwdreq_key6, (char *)&req6)) { + prnt(P_ERR, "can't get args.\n"); + svcerr_decode(transp); + return; + } + + /* args were ok: don't wait for resolver */ + if (!svc_sendreply(transp, xdr_void, 0)) + prnt(P_ERR, "can't ack nisd req.\n"); + + yp_resolv(AF_INET6, &req6, reply_xprt6); + + if (!svc_freeargs(transp, xdr_ypfwdreq_key6, (char *)&req6)) { + prnt(P_ERR, "can't free args.\n"); + exit(1); + } + + break; + default: + prnt(P_ERR, "call to bogus proc.\n"); + svcerr_noproc(transp); + break; + } +} + +static struct cache_ent * +cache_ent_bykey(map, keydat) + char *map; + datum keydat; +{ + struct cache_ent *chl; + struct cache_ent *prev; + struct timeval now; + struct timezone tzp; + int secs; + if (keydat.dptr == NULL) + return (NULL); + if (keydat.dsize <= 0) + return (NULL); + if (map == NULL) + return (NULL); + (void) gettimeofday(&now, &tzp); + + for (prev = cache_head, chl = cache_head; chl; /* */) { + /* check for expiration */ + if (chl->h_errno == TRY_AGAIN) + secs = NQTIME * 60; + else + secs = chl->ttl; + if ((chl->nres == 0) && + (chl->enqtime.tv_sec + secs) < now.tv_sec) { + prnt(P_INFO, + "bykey:stale cache_ent flushed %x.\n", chl); + /* deleteing the first element is tricky */ + if (chl == cache_head) { + cache_head = cache_head->next_cache_ent; + free_cache_ent(chl); + prev = cache_head; + chl = cache_head; + continue; + } else { + /* deleteing a middle element */ + prev->next_cache_ent = chl->next_cache_ent; + free_cache_ent(chl); + chl = prev->next_cache_ent; + continue; + } + } else if (chl->map) + if (0 == strcmp(map, chl->map)) + if (chl->key.dptr) { + /* supress trailing null */ + if (keydat.dptr[keydat.dsize - 1] == 0) + keydat.dsize--; + if ((chl->key.dsize == keydat.dsize)) + if (!strncasecmp(chl->key.dptr, + keydat.dptr, keydat.dsize)) { + /* move to beginning */ + if (chl != cache_head) { + prev->next_cache_ent = chl->next_cache_ent; + chl->next_cache_ent = cache_head; + cache_head = chl; + } + return (chl); + } + } + prev = chl; + chl = chl->next_cache_ent; + } + return (NULL); +} + +static struct cache_ent * +new_cache_ent(map, keydat) + char *map; + datum keydat; +{ + struct cache_ent *chl; + struct timezone tzp; + + chl = (struct cache_ent *)calloc(1, sizeof (struct cache_ent)); + if (chl == NULL) + return (NULL); + prnt(P_INFO, "cache_ent enqed.\n"); + chl->caller.buf = chl->buf; + chl->caller.maxlen = sizeof (chl->buf); + chl->map = (char *)strdup(map); + if (chl->map == NULL) { + free(chl); + return (NULL); + } + chl->key.dptr = (char *)malloc(keydat.dsize + 1); + if (chl->key.dptr == NULL) { + free(chl->map); + free(chl); + return (NULL); + } + if (keydat.dptr != NULL) + /* delete trailing null */ + if (keydat.dptr[keydat.dsize - 1] == 0) + keydat.dsize = keydat.dsize - 1; + chl->key.dsize = keydat.dsize; + chl->key.dptr[keydat.dsize] = '\0'; + chl->val.dptr = 0; + (void) memcpy(chl->key.dptr, keydat.dptr, keydat.dsize); + (void) gettimeofday(&(chl->enqtime), &tzp); + chl->next_cache_ent = cache_head; + cache_head = chl; + return (chl); +} + +static struct cache_ent * +deq_cache_ent(x) + struct cache_ent *x; +{ + struct cache_ent *chl; + struct cache_ent *prev; + if (x == cache_head) { + cache_head = cache_head->next_cache_ent; + x->next_cache_ent = NULL; + return (x); + } + for (chl = cache_head, prev = cache_head; chl; + chl = chl->next_cache_ent) { + if (chl == x) { + /* deq it */ + prev->next_cache_ent = chl->next_cache_ent; + chl->next_cache_ent = NULL; + return (chl); + } + prev = chl; + } + return (NULL); /* bad */ +} + +static void +free_cache_ent(x) + struct cache_ent *x; +{ + if (x == NULL) + return; + if (x->map) + free(x->map); + if (x->key.dptr) + free(x->key.dptr); + if (x->val.dptr) + free(x->val.dptr); + free(x); +} + +static ulong_t +svc_setxid(xprt, xid) + register SVCXPRT *xprt; + ulong_t xid; +{ + register struct bogus_data *su = getbogus_data(xprt); + ulong_t old_xid; + if (su == NULL) + return (0); + old_xid = su->su_xid; + su->su_xid = xid; + return (old_xid); +} + + +static int +yp_matchdns(map, keydat, valdatp, statusp, transp) + char *map; /* map name */ + datum keydat; /* key to match (e.g. host name) */ + datum *valdatp; /* returned value if found */ + unsigned *statusp; /* returns the status */ + SVCXPRT *transp; +{ + struct nres *h; + int byname, byaddr; + int byname_v6, byaddr_v6; + struct cache_ent *chl; + struct timeval now; + struct timezone tzp; + int try_again; + int af_type; + + try_again = 0; + /* + * Skip the domain resolution if: 1. it is not turned on 2. map other + * than hosts.byXXX 3. a null string (usingypmap() likes to send + * these) 4. a single control character (usingypmap() again) + */ + byname = strcmp(map, "hosts.byname") == 0; + byaddr = strcmp(map, "hosts.byaddr") == 0; + byname_v6 = strcmp(map, "ipnodes.byname") == 0; + byaddr_v6 = strcmp(map, "ipnodes.byaddr") == 0; + if ((!byname && !byaddr && !byname_v6 && !byaddr_v6) || + keydat.dsize == 0 || keydat.dptr[0] == '\0' || + !isascii(keydat.dptr[0]) || !isgraph(keydat.dptr[0])) { + *statusp = (unsigned)YP_NOKEY; + return (RESP_NOW); + } + + chl = cache_ent_bykey(map, keydat); + if (chl) { + (void) gettimeofday(&now, &tzp); + if (chl->h_errno == TRY_AGAIN) + try_again = 1; + else if (chl->nres) { + /* update xid */ + if (transp) { + chl->xprt = transp; + chl->caller.len = transp->xp_rtaddr.len; + (void) memcpy(chl->caller.buf, + transp->xp_rtaddr.buf, + transp->xp_rtaddr.len); + chl->xid = svc_getxid(transp); + prnt(P_INFO, "cache_ent %s: xid now %d.\n", + chl->key.dptr, chl->xid); + } + return (RESP_LATER); /* drop */ + } + switch (chl->h_errno) { + case NO_RECOVERY: +#ifndef NO_DATA +#define NO_DATA NO_ADDRESS +#endif + case NO_DATA: + case HOST_NOT_FOUND: + prnt(P_INFO, "cache NO_KEY.\n"); + *statusp = (unsigned)YP_NOKEY; + return (RESP_NOW); + + case TRY_AGAIN: + prnt(P_INFO, "try_again.\n"); + try_again = 1; + break; + case 0: + prnt(P_INFO, "cache ok.\n"); + if (chl->val.dptr) { + *valdatp = chl->val; + *statusp = (unsigned)YP_TRUE; + return (RESP_NOW); + } + break; + + default: + free_cache_ent(deq_cache_ent(chl)); + chl = NULL; + break; + } + } + /* have a trier activated -- tell them to try again */ + if (try_again) { + if (chl->nres) { + *statusp = (unsigned)YP_NOMORE; + /* try_again overloaded */ + return (RESP_NOW); + } + } + if (chl) { + (void) gettimeofday(&(chl->enqtime), &tzp); + } else + chl = new_cache_ent(map, keydat); + + if (chl == NULL) { + perror("new_cache_ent failed"); + *statusp = (unsigned)YP_YPERR; + return (RESP_NOW); + } + if (byname || byname_v6) + h = nres_gethostbyname(chl->key.dptr, my_done, chl); + else { + struct in_addr addr; + struct in6_addr addr6; + af_type = (strcmp(chl->map, "ipnodes.byaddr") == 0) ? + AF_INET6 : AF_INET; + if (af_type == AF_INET6 || af_type == AF_INET) { + if (inet_pton(af_type, chl->key.dptr, + (af_type == AF_INET6) ? (void *)&addr6 : (void *)&addr) + == -1) { + *statusp = (unsigned)YP_NOKEY; + return (RESP_NOW); + } + h = nres_gethostbyaddr( + (af_type == AF_INET6) ? (void *)&addr6 : (void *)&addr, + (af_type == AF_INET6) ? sizeof (addr6) : sizeof (addr), + af_type, my_done, chl); + } else { + *statusp = (unsigned)YP_NOKEY; + return (RESP_NOW); + } + } + if (h == 0) { /* definite immediate reject */ + prnt(P_INFO, "immediate reject.\n"); + free_cache_ent(deq_cache_ent(chl)); + *statusp = (unsigned)YP_NOKEY; + return (RESP_NOW); + } else if (h == (struct nres *)-1) { + perror("nres failed\n"); + *statusp = (unsigned)YP_YPERR; + return (RESP_NOW); + } else { + chl->nres = h; + /* should stash transport so my_done can answer */ + if (try_again) { + *statusp = (unsigned)YP_NOMORE; + /* try_again overloaded */ + return (RESP_NOW); + + } + chl->xprt = transp; + if (transp) { + chl->caller.len = transp->xp_rtaddr.len; + (void) memcpy(chl->caller.buf, transp->xp_rtaddr.buf, + transp->xp_rtaddr.len); + chl->xid = svc_getxid(transp); + } + return (RESP_LATER); + } +} + +static void +my_done(n, h, ttl, chl, errcode) + void *n; /* opaque */ + struct hostent *h; + ulong_t ttl; + struct cache_ent *chl; + int errcode; +{ + static char buf[1024]; + char *endbuf, *tptr; + datum valdatp; + int i; + SVCXPRT *transp; + unsigned long xid_hold; + struct ypresp_val resp; + struct timezone tzp; + struct netbuf caller_hold, *addrp; + char uaddr[sizeof (struct sockaddr_in6)]; + int af_type, bsize; + + prnt(P_INFO, "my_done: %s.\n", chl->key.dptr); + (void) gettimeofday(&(chl->enqtime), &tzp); + chl->nres = 0; + caller_hold.maxlen = sizeof (uaddr); + caller_hold.buf = uaddr; + + if (h == NULL) { + chl->h_errno = errcode; + if (chl->h_errno == TRY_AGAIN) + resp.status = (unsigned)YP_NOMORE; + else + resp.status = (unsigned)YP_NOKEY; + valdatp.dptr = NULL; + valdatp.dsize = 0; + } else { + chl->h_errno = 0; + chl->ttl = (ttl != 0 && ttl < PQTIME*60) ? ttl : (PQTIME*60); + endbuf = buf; + bsize = sizeof (buf); + bzero((void *)endbuf, bsize); + af_type = lookup_AF_type(chl); + + /* build the return list */ + for (i = 0; h->h_addr_list[i]; i++) { + tptr = endbuf; + (void *) inet_ntop(af_type, + (af_type == AF_INET6) ? + (void *) (h->h_addr_list[i]) : + (void *) (h->h_addr_list[i]), + endbuf, bsize); + endbuf = &endbuf[strlen(endbuf)]; + bsize = buf + sizeof (buf) - endbuf; + (void) snprintf(endbuf, bsize, "\t%s\n", h->h_name); + endbuf = &endbuf[strlen(endbuf)]; + if ((bsize = buf + sizeof (buf) - endbuf) < 300) + break; + prnt(P_INFO, "my_done: bsize=%d str=%s", bsize, tptr); + } + valdatp.dptr = buf; + valdatp.dsize = strlen(buf); + /* remove trailing newline */ + if (valdatp.dsize) { + valdatp.dptr[valdatp.dsize-1] = '\0'; + valdatp.dsize -= 1; + } + chl->val.dsize = valdatp.dsize; + chl->val.dptr = (char *)malloc(valdatp.dsize); + if (chl->val.dptr == NULL) { + perror("my_done"); + free_cache_ent(deq_cache_ent(chl)); + return; + } + (void) memcpy(chl->val.dptr, valdatp.dptr, valdatp.dsize); + resp.status = (unsigned)YP_TRUE; + } + /* try to answer here */ + + if (valdatp.dptr) + prnt(P_INFO, "my_done: return %s.\n", valdatp.dptr); + transp = chl->xprt; + if (transp && transp->xp_rtaddr.len <= caller_hold.maxlen) { + caller_hold.len = transp->xp_rtaddr.len; + (void) memcpy(caller_hold.buf, transp->xp_rtaddr.buf, + transp->xp_rtaddr.len); + xid_hold = svc_setxid(transp, chl->xid); + addrp = &(chl->caller); + SETCALLER(transp, addrp); + resp.valdat = valdatp; + if (!svc_sendreply(transp, (xdrproc_t)xdr_ypresp_val, + (char *)&resp)) { + return; + } + addrp = &caller_hold; + SETCALLER(transp, addrp); + (void) svc_setxid(transp, xid_hold); + } +} + +/* + * this routine returns the DNS query type: + * T_A: IPv4 + * T_AAAA: IPv6 + */ +int +lookup_T_type(struct cache_ent *chl) +{ + + if (strcmp(chl->map, "ipnodes.byname") == 0) { + prnt(P_INFO, "lookup_T_type: T_AAAA\n"); + return (T_AAAA); + } else if ((strcmp(chl->map, "hosts.byaddr") == 0) || + (strcmp(chl->map, "ipnodes.byaddr") == 0)) { + prnt(P_INFO, "lookup_T_type: T_PTR\n"); + return (T_PTR); + } + prnt(P_INFO, "lookup_T_type: T_A\n"); + return (T_A); +} + + +/* + * this routine returns the AF type for the request: + * AF_INET: ipv4 + * AF_INET6: IPv6 + */ +int +lookup_AF_type(struct cache_ent *chl) +{ + if ((strcmp(chl->map, "ipnodes.byname") == 0) || + (strcmp(chl->map, "ipnodes.byaddr") == 0)) { + prnt(P_INFO, "lookup_AF_type: AF_INET6\n"); + return (AF_INET6); + } + prnt(P_INFO, "lookup_AF_type: AF_INET\n"); + return (AF_INET); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/yp_ns_proc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/yp_ns_proc.c new file mode 100644 index 0000000000..c3ce75f147 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/yp_ns_proc.c @@ -0,0 +1,2312 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 1991-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * yp_ns_proc.c + * + * This module contains the implementation for backward compatibility + * with NIS version 2 (aka YP), so that a version 3 server (aka NIS+) can + * serve a version 2 client requests [only for Sun standard maps]. + * It provides the routines that the dispatch function yp_prog_svc in + * yp_svc.c calls. That file, yp_svc.c, reflects + * the interface definition that is described in the nis.x/yp.x files. + * + * This module contains the Namespace manipulation procedures and is + * not supposed to access the database directly. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <rpc/rpc.h> +#include <netinet/in.h> +#include <netconfig.h> +#include <netdir.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/yp_prot.h> +/* "nis_proc.h" includes "nis_mt.h" */ +#include "nis_proc.h" +#undef master + +#include <netdb.h> +#include <ctype.h> + +#define ORGDIR ".org_dir." +#define ORGDIR1 org_dir +#define ORGDIR2 "org_dir." +#define ORGLEN 10 /* len(ORGDIR + null char) */ +#define DEFAULTKEY "key" /* search column name for the non-standard maps */ +#define BY "by" +#define CNAME "cname" +#define CNAMELEN 5 +#define MAILNAME "alias" /* name of the first column in mail_aliases */ +#define MAILADDR "expansion" /* name of the second column in mail_aliases */ +#define NGROUPNAME "name" /* column name of netgroup name in netgroup */ +#define COLVAL(n) (e->en_cols.en_cols_val[n].ENVAL && COLLEN(n) > 0) ? \ + e->en_cols.en_cols_val[n].ENVAL : "" +#define COLLEN(n) e->en_cols.en_cols_val[n].ENLEN +#define COLGETVAL(n) e->en_cols.en_cols_val[n].ENVAL +#define ENCOLVAL(e, n) e->en_cols.en_cols_val[n].ENVAL ? \ + e->en_cols.en_cols_val[n].ENVAL : "" + +extern nis_object *nis_return_list(nis_object *, + obj_list *, int, nis_name, int *, int, int, nis_attr *); +extern nis_object *nis_censor_object(nis_object *, + table_col *, nis_name); + +extern int resolv_flag; +extern int resolv_pid; +extern CLIENT *resolv_client; +extern char *resolv_tp; + +long err_conv_nistoyp(); +nis_error getzobj_orgdir(); +int ypcheck_nisdir(); +struct ypresp_maplist *ypproc_maplist_svc(); +void map2table(); +bool_t xdr_ypresp_all(); + +int serv_wart = 0; +/* to help the two key lookup on port/proto or service/proto in services */ + +int mail_wart_byaddr = 0; +/* to pass the column name info from map2table to cook_record */ + +int netid_wart = 0; +/* the request on cred table/auth_name is for netid */ + +int mult_lines_wart = 0; +/* the client can handle mulitple lines of reply from ypproc_match_svc */ + +static nis_error +cook_record_from_entry(entry_obj *, char **, int *, char *, nis_object *); +static void +cook_an_ngroup(entry_obj *, char **, int *); +static nis_error +cook_host_record(nis_object *, int, char **, int *, struct svc_req *); +static char *getcaller_inet(); + +#define record (__nis_get_tsd()->yp_ns_proc_record) +#define keyval (__nis_get_tsd()->yp_ns_proc_keyval) + + +static +int +check_map(map) + char *map; +{ + int n; + + if (map == 0 || (n = strlen(map)) == 0 || n >= YPMAXMAP) + return (0); + return (1); +} + +static +char * +check_domain(domain) + char *domain; +{ + int n; + + if (domain == 0 || *domain == 0 || strlen(domain) >= YPMAXDOMAIN) + return (0); + while (*domain && *domain == '.') + domain++; + if (*domain == 0) + return (0); + return (domain); +} + +static +int +check_key(keydat) + datum *keydat; +{ + if (keydat->dsize == 0 || keydat->dptr == 0) + return (0); + return (1); +} + +/* + * This determines whether or not the domain passed is served by this server, + * and returns a boolean. + */ +int * +ypproc_domain_svc(dname, rqstp) + string_t *dname; + struct svc_req *rqstp; +{ + char *domain; +#define isserved (__nis_get_tsd()->ypproc_domain_svc_isserved) + + if ((domain = check_domain(*dname)) == 0) + return (&isserved); + + if (verbose) + syslog(LOG_INFO, "ypserv: bind request from %s", + getcaller_inet(rqstp)); + isserved = ypcheck_nisdir(domain); + if (!isserved) + syslog(LOG_ERR, "ypserv: Domain %s not supported", *dname); + return (&isserved); +} + +#undef isserved + +/* + * We keep a list of the domains that have been broadcasted, but that + * we don't serve so that we will only syslog a message about it + * once. + */ +struct domain_list { + char *domain; + struct domain_list *next; +}; +struct domain_list *domains; +DECLMUTEXLOCK(domains); + +/* + * Check to see if dname is in the list of domains. As a side-effect + * we add it to the list. + */ +static +int +domain_list_check(char *dname) +{ + struct domain_list *dl; + + MUTEXLOCK(domains, "domain_list_check(domains)"); + for (dl = domains; dl; dl = dl->next) { + if (strcmp(dname, dl->domain) == 0) { + MUTEXUNLOCK(domains, "domain_list_check(domains)"); + return (1); + } + } + + if ((dl = malloc(sizeof (*dl))) != 0 && + (dl->domain = strdup(dname)) != 0) { + dl->next = domains; + domains = dl; + } else if (dl != 0) { + free(dl); + } + + MUTEXUNLOCK(domains, "domain_list_check(domains)"); + return (0); +} + +int * +ypproc_domain_nonack_svc(dname, rqstp) + string_t *dname; + struct svc_req *rqstp; +{ + char *domain; +#define isserved (__nis_get_tsd()->ypproc_domain_nonack_svc_isserved) + + if ((domain = check_domain(*dname)) == 0) + return (&isserved); + + if (verbose) + syslog(LOG_INFO, "ypserv: (broadcast) request from %s", + getcaller_inet(rqstp)); + isserved = ypcheck_nisdir(domain); + if (isserved) + return (&isserved); + else { + + /* + * This case is the one in which the domain is not supported, + * and in which we are not to respond in the unsupported + * case. We are going to make an error happen to allow the + * portmapper to end his wait without the normal udp timeout + * period. The assumption here is that the only process in + * the world which is using the function in its + * no-answer-if-nack form is the portmapper, which is doing + * the krock for pseudo-broadcast. If some poor fool calls + * this function as a single-cast message, the nack case will + * look like an incomprehensible error. Sigh... (The + * traditional Unix disclaimer) + */ + + svcerr_decode(rqstp->rq_xprt); + if (!domain_list_check(domain)) { + syslog(LOG_ERR, + "ypserv: Domain %s not supported (broadcast)", + *dname); + } + return (0); + } +} + +#undef isserved + +/* + * This implements the "get master name" function. + */ +struct ypresp_master * +ypproc_master_svc(req, rqstp) + struct ypreq_nokey *req; + struct svc_req *rqstp; +{ +#define resp (__nis_get_tsd()->ypproc_master_svc_resp) +#define masterbuf (__nis_get_tsd()->ypproc_master_svc_masterbuf) + nis_object *org_zobj = NULL; + nis_error status; + char *domain; + char *table, *column, *full_tblnm; + nis_db_result *dbres; + int i; + + if ((domain = check_domain(req->domain)) == 0 || + !check_map(req->map)) { + resp.status = YP_BADARGS; + return (&resp); + } + + resp.status = YP_TRUE; + resp.master = masterbuf; + if (verbose) + syslog(LOG_INFO, "ypserv: yp_master MAP = %s from %s", + req->map, getcaller_inet(rqstp)); + status = getzobj_orgdir(domain, &org_zobj); + if (status != NIS_SUCCESS || org_zobj == NULL) { + syslog(LOG_INFO, "ypserv: yp_master failed for %s%s.", + ORGDIR2, domain); + resp.status = err_conv_nistoyp(NIS_NAMEUNREACHABLE); + return (&resp); + } + strcpy(resp.master, + (nis_leaf_of((org_zobj)->DI_data.do_servers.do_servers_val[0].name))); + map2table(req->map, &table, &column); + + if ((full_tblnm = (char *)XCALLOC(1, strlen(table) + ORGLEN + + strlen(domain) + 1)) == NULL) { + resp.status = err_conv_nistoyp(NIS_NOMEMORY); + return (&resp); + } + strcpy(full_tblnm, table); + strcat(full_tblnm, ORGDIR); + strcat(full_tblnm, domain); + if (*(domain + strlen(domain) - 1) != '.') + strcat(full_tblnm, "."); + + /* Get the information base (table) object from the database */ + dbres = db_lookup(full_tblnm); + /* + * MEMORY POLICY: db_lookup adds dbres to the cleanup list. + * We use it and forget it. + */ + if (dbres->status != NIS_SUCCESS || (!dbres->obj)) { + if (dbres->status == NIS_NOTFOUND) + resp.status = err_conv_nistoyp(NIS_NOSUCHTABLE); + else + resp.status = err_conv_nistoyp(dbres->status); + XFREE(full_tblnm); + return (&resp); + } + if ((__type_of(dbres->obj) != NIS_TABLE_OBJ) || + (column && + ((i = get_keyndx(&(dbres->obj->TA_data), column)) < 0 || + (!((dbres->obj->TA_data.ta_cols.ta_cols_val + i)->tc_flags & + TA_SEARCHABLE))))) { + resp.status = err_conv_nistoyp(NIS_NOSUCHTABLE); + } + XFREE(full_tblnm); + return (&resp); +} + +#undef resp +#undef masterbuf + +entry_obj * +get_netid_entry(tobj, e, table_name, princp, err) + nis_object *tobj; /* cred table object */ + entry_obj *e; /* correspoding DES entry in cred table */ + char *table_name; /* fully qualified cred table name */ + nis_name princp; + long *err; +{ + ib_request table_req; + nis_attr inkey[2]; + nis_db_list_result *entry_obj_list; + nis_object *ret_en_objs; + int num_ntrees; + int all_read; + char *val; + + val = COLVAL(2); /* netname */ + if (((int)strlen(val) > 5) && (! isdigit(val[5]))) { + /* + * this section handles netid for root. + * Just return the corresponding DES entry, leave up to + * cook_record_from_entry() to format the return value. + */ + return (e); + } + memset((char *)inkey, 0, 2*sizeof (nis_attr)); + inkey[0].zattr_ndx = CNAME; + inkey[0].ZAVAL = (char *)XMALLOC(COLLEN(0)); + add_cleanup((void (*)())XFREE, + (char *)inkey[0].ZAVAL, "get_netid_entry zaval"); + memcpy(inkey[0].ZAVAL, COLVAL(0), COLLEN(0)); + inkey[0].ZALEN = COLLEN(0); + + inkey[1].zattr_ndx = "auth_type"; + inkey[1].ZAVAL = "LOCAL"; + inkey[1].ZALEN = 6; + + table_req.ibr_srch.ibr_srch_len = 2; + table_req.ibr_srch.ibr_srch_val = inkey; + table_req.ibr_name = table_name; + + entry_obj_list = db_list(table_req.ibr_name, + table_req.ibr_srch.ibr_srch_len, + table_req.ibr_srch.ibr_srch_val); + if (entry_obj_list->status != NIS_SUCCESS) { + if (entry_obj_list->status == NIS_NOTFOUND) + *err = YP_NOKEY; + else + *err = YP_NOMAP; /* bad column, attr etc. */ + return (NULL); + } + + /* + * Now, process the list of objects we've got to return. + * We know, we gonna return only one entry. The real reason for + * going through this is to enforce nis_return_list's censorship. + */ + all_read = __can_do(NIS_READ_ACC, tobj->zo_access, tobj, princp); + ret_en_objs = nis_return_list(tobj, entry_obj_list->objs, + entry_obj_list->numo, princp, &num_ntrees, all_read, + 2, inkey); + + if ((ret_en_objs == NULL) || !(num_ntrees) || + (__type_of(ret_en_objs) != NIS_ENTRY_OBJ)) { + *err = YP_NOKEY; + return (NULL); + } + return (&(ret_en_objs->EN_data)); +} + +struct ypresp_val * +ypproc_match_svc(req, rqstp) + struct ypreq_key *req; + struct svc_req *rqstp; +{ +#define resp (__nis_get_tsd()->ypproc_match_svc_resp) + char *table, *column; + int len; + entry_obj *e, *netid_e; + char princp[1024]; + char map[YPMAXMAP]; + int num_ntrees; + nis_error status; + + ib_request table_req; + char *t, *proto; + char *col_proto = "proto"; + nis_attr inkey[2]; + char *domain; + nis_db_list_result *entry_obj_list; + nis_object *ret_en_objs; + nis_db_result *dbres; + int i, j, all_read; + + if ((domain = check_domain(req->domain)) == 0 || + !check_map(req->map) || + !check_key(&req->keydat)) { + resp.status = YP_BADARGS; + return (&resp); + } + + resp.status = YP_TRUE; + + if (verbose) { + syslog(LOG_INFO, "ypserv: yp_match MAP = %s from %s", + req->map, getcaller_inet(rqstp)); + } + + /* + * All YP requests are unauthenticated and are successful + * only if the database has nobody-read access. + */ + + if (resolv_flag) { /* save real map name */ + strncpy(map, req->map, sizeof (map)-1); + map[sizeof (map)-1] = '\0'; + } + + map2table(req->map, &table, &column); + + if ((strcmp(table, "netgroup") == 0) && + (strcmp(column, NGROUPNAME) != 0)) { + /* We only support one netgroup YP map, "netgroup". */ + cook_err_resp(&resp, YP_NOMAP); + return (&resp); + } + + /* Cook the ib_request */ + if ((table_req.ibr_name = (char *)XCALLOC(1, \ + (strlen(table) + ORGLEN + strlen(domain) + 1))) == + NULL) { + cook_err_resp(&resp, err_conv_nistoyp(NIS_NOMEMORY)); + return (&resp); + } + strcpy(table_req.ibr_name, table); + strcat(table_req.ibr_name, ORGDIR); + strcat(table_req.ibr_name, domain); + if (*(domain + strlen(domain) - 1) != '.') + strcat(table_req.ibr_name, "."); + + /* Get the information base (table) object from the database */ + dbres = db_lookup(table_req.ibr_name); + /* + * MEMORY POLICY: db_lookup adds dbres to the cleanup list. + * We use it and forget it. + */ + if (dbres->status != NIS_SUCCESS || (!dbres->obj)) { + if (dbres->status == NIS_NOTFOUND) + cook_err_resp(&resp, err_conv_nistoyp(NIS_NOSUCHTABLE)); + else + cook_err_resp(&resp, err_conv_nistoyp(dbres->status)); + XFREE(table_req.ibr_name); + return (&resp); + } + if (__type_of(dbres->obj) != NIS_TABLE_OBJ) { + /* there goes yp's limited knowledge of the NIS+ universe ! */ + cook_err_resp(&resp, err_conv_nistoyp(NIS_NOSUCHTABLE)); + XFREE(table_req.ibr_name); + return (&resp); + } + + /* + * If we are using the default key check that the the map actually + * contains this column. If not the log an informational error instead + * of proceeding to the lookup where a more serious error would be + * produced. + */ + if (strcmp(column, DEFAULTKEY) == 0) { + if (get_keyndx(dbres->obj->TA_data, column) < 0) { + syslog(LOG_INFO, + "%s is not explicitly supported in YP " + "compatibility mode. Unsupported NIS+ tables " + "must have column named \'%s\'.", + table, DEFAULTKEY); + cook_err_resp(&resp, err_conv_nistoyp(NIS_NOSUCHTABLE)); + XFREE(table_req.ibr_name); + return (&resp); + } + } + + /* Cook the nis_attr key */ + memset((char *)inkey, 0, 2*sizeof (nis_attr)); + inkey[0].zattr_ndx = column; + /* + * ASSERT: column is never NULL, + * could be client supplied or DEFAULTKEY + */ + + /* + * Do the jugglery to conform to the NIS+ convention of null + * terminated data in the database. Cannot rely on YP + * that it will always pass a null terminated key. + */ + i = req->keydat.dsize; + if (req->keydat.dptr[i-1] != '\0') + i++; + inkey[0].ZAVAL = (char *)XMALLOC(i); + inkey[0].ZALEN = i; + if (inkey[0].ZAVAL == NULL) { + cook_err_resp(&resp, err_conv_nistoyp(NIS_NOMEMORY)); + XFREE(table_req.ibr_name); + return (&resp); + } + strncpy(inkey[0].ZAVAL, req->keydat.dptr, req->keydat.dsize); + inkey[0].ZAVAL[i-1] = '\0'; + + cook_err_resp(&resp, err_conv_nistoyp(NIS_SUCCESS)); + + /* Now, do the actual lookup for an entry we want */ + table_req.ibr_srch.ibr_srch_len = 1; + table_req.ibr_srch.ibr_srch_val = inkey; + + if (serv_wart && (strcmp(table, "services") == 0) && + ((proto = strchr(inkey[0].ZAVAL, '/')) != NULL)) { + *proto++ = '\0'; + inkey[1].zattr_ndx = col_proto; + inkey[1].ZALEN = (strlen(proto) + 1); + inkey[1].ZAVAL = proto; + inkey[0].ZALEN = i - inkey[1].ZALEN; + table_req.ibr_srch.ibr_srch_len++; + } + + if (strcmp(table, "cred") == 0) { + /* only make the publickey.byname type of information visible */ + inkey[1].zattr_ndx = "auth_type"; + table_req.ibr_srch.ibr_srch_len++; + inkey[1].ZAVAL = "DES"; + inkey[1].ZALEN = 4; + } + + entry_obj_list = db_list(table_req.ibr_name, + table_req.ibr_srch.ibr_srch_len, + table_req.ibr_srch.ibr_srch_val); + if (entry_obj_list->status != NIS_SUCCESS) { + if (entry_obj_list->status == NIS_NOTFOUND) + if (resolv_flag && + resolv_req(&resolv_flag, &resolv_client, + &resolv_pid, resolv_tp, + rqstp->rq_xprt, req, map)) { + XFREE(table_req.ibr_name); + XFREE(inkey[0].ZAVAL); + return (NULL); /* fwd'ed req: skip reply */ + } else + cook_err_resp(&resp, YP_NOKEY); + else + /* bad column, attr etc. */ + cook_err_resp(&resp, YP_NOMAP); + XFREE(table_req.ibr_name); + XFREE(inkey[0].ZAVAL); + return (&resp); + } + + /* + * Now, process the list of objects we've got to return. + * We know, we gonna return only one entry. The real reason for + * going through this is to enforce nis_return_list's censorship. + */ + nis_getprincipal(princp, rqstp); + all_read = __can_do(NIS_READ_ACC, dbres->obj->zo_access, + dbres->obj, princp); + ret_en_objs = nis_return_list(dbres->obj, entry_obj_list->objs, + entry_obj_list->numo, princp, &num_ntrees, all_read, + 2, inkey); + + if ((ret_en_objs == NULL) || !(num_ntrees) || + (__type_of(ret_en_objs) != NIS_ENTRY_OBJ)) { + cook_err_resp(&resp, YP_NOKEY); + XFREE(table_req.ibr_name); + XFREE(inkey[0].ZAVAL); + return (&resp); + } + if (strcmp(table, "netgroup") == 0) { + /* + * This is as grungy as any code can get. The + * netgroup format of the NIS+ table is quite + * different from that in YP; for instance, we + * do not have "reverse" lookup support. We write + * the complete code here. + */ + if (strcmp(column, NGROUPNAME) == 0) { + int i = 0, len; + char *ngrp; + + ngrp = XMALLOC(YPMAXRECORD); + add_cleanup((void (*)())XFREE, + (char *)ngrp, "yp (match) mem"); + memset(record, 0, YPMAXRECORD); + resp.valdat.dptr = record; + while ((ret_en_objs + i) && (i < num_ntrees)) { + e = &((ret_en_objs + i)->EN_data); + len = 0; + cook_an_ngroup(e, &ngrp, &len); + /* + * len returned here includes the + * NULL character '\0' + */ + if ((strlen(record) + len) > YPMAXRECORD) + break; + strcat(record, ngrp); + strcat(record, " "); + i++; + } + if ((num_ntrees == 1) && + (strcmp(COLVAL(5), "") != 0) && + ((strlen(record) + COLLEN(5) + 3) <= + YPMAXRECORD)) { + /* + * the special case of leaf netgroup, + * print comments + */ + strcat(record, "\t#"); + strcat(record, COLVAL(5)); + } + resp.valdat.dsize = strlen(record); + } else + cook_err_resp(&resp, YP_NOMAP); + XFREE(table_req.ibr_name); + XFREE(inkey[0].ZAVAL); + return (&resp); + } + + e = &(ret_en_objs->EN_data); + if (netid_wart) { + long yperr = YP_TRUE; + + netid_e = e; + e = get_netid_entry(dbres->obj, netid_e, + table_req.ibr_name, princp, &yperr); + if (! e) { + cook_err_resp(&resp, yperr); + XFREE(table_req.ibr_name); + XFREE(inkey[0].ZAVAL); + return (&resp); + } + } + + /* construct a complete entry from all columns */ + memset(record, 0, YPMAXRECORD); + t = record; + if (strcmp(table, "hosts") == 0) { + status = cook_host_record(ret_en_objs, num_ntrees, + &t, &len, rqstp); + } else { + status = cook_record_from_entry(e, &t, &len, table, dbres->obj); + } + if (status != NIS_SUCCESS) { + cook_err_resp(&resp, err_conv_nistoyp(status)); + XFREE(table_req.ibr_name); + XFREE(inkey[0].ZAVAL); + return (&resp); + } + + resp.valdat.dptr = record; + resp.valdat.dsize = len; + + XFREE(table_req.ibr_name); + XFREE(inkey[0].ZAVAL); + return (&resp); +} + +#undef resp + +struct ypresp_key_val * +ypproc_first_svc(req, rqstp) + struct ypreq_nokey *req; + struct svc_req *rqstp; +{ +#define resp (__nis_get_tsd()->ypproc_first_svc_resp) + char *table, *column, *full_tblnm, *t; + int len, key_ndx; + entry_obj *e, *netid_e = NULL; + nis_error status; + char *domain; + nis_object *ret_en_objs; + nis_db_result *dbres; + nis_fn_result *fnr; + char princp[1024]; + netobj cookie; + int all_readable = 0; + + if ((domain = check_domain(req->domain)) == 0 || + !check_map(req->map)) { + resp.status = YP_BADARGS; + return (&resp); + } + + resp.status = YP_TRUE; + if (verbose) + syslog(LOG_INFO, "ypserv: yp_first MAP = %s from %s", + req->map, getcaller_inet(rqstp)); + + /* + * All YP requests are unauthenticated and are successful + * only if the database has nobody-read access. + */ + + map2table(req->map, &table, &column); + if (strcmp(table, "netgroup") == 0) { + cook_err_keyresp(&resp, YP_NOMORE); + return (&resp); + } + + if ((full_tblnm = (char *)XCALLOC(1, strlen(table) + ORGLEN + + strlen(domain) + 1)) == + NULL) { + cook_err_keyresp(&resp, err_conv_nistoyp(NIS_NOMEMORY)); + return (&resp); + } + strcpy(full_tblnm, table); + strcat(full_tblnm, ORGDIR); + strcat(full_tblnm, domain); + if (*(domain + strlen(domain) - 1) != '.') + strcat(full_tblnm, "."); + + /* Get the information base (table) object from the database */ + dbres = db_lookup(full_tblnm); + /* + * MEMORY POLICY: db_lookup adds dbres to the cleanup list. + * We use it and forget it. + */ + if (dbres->status != NIS_SUCCESS || (!dbres->obj)) { + if (dbres->status == NIS_NOTFOUND) + cook_err_keyresp(&resp, + err_conv_nistoyp(NIS_NOSUCHTABLE)); + else + cook_err_keyresp(&resp, + err_conv_nistoyp(dbres->status)); + XFREE(full_tblnm); + return (&resp); + } + if (__type_of(dbres->obj) != NIS_TABLE_OBJ) { + cook_err_keyresp(&resp, err_conv_nistoyp(NIS_NOSUCHTABLE)); + XFREE(full_tblnm); + return (&resp); + } + + nis_getprincipal(princp, rqstp); + all_readable = __can_do(NIS_READ_ACC, dbres->obj->zo_access, + dbres->obj, princp); + /* + * MEMORY POLICY on db_firstib/nextib: always call it with + * !(flags & 2), the db_*ib routines will add fnr and fnr->obj + * to the cleanup list. We never free cookie.n_bytes if the + * cookie is to be passwd to db_nextib(). db_nextib() always + * frees the cookie it is given. + */ + fnr = db_firstib(full_tblnm, 0, NULL, FN_NOMANGLE, NULL); + if (fnr->status != NIS_SUCCESS) { + cook_err_keyresp(&resp, YP_NOMORE); + XFREE(full_tblnm); + return (&resp); + } + cookie = fnr->cookie; +doagain: + do { + if (all_readable) { + ret_en_objs = fnr->obj; + break; + } else { + ret_en_objs = nis_censor_object(fnr->obj, + dbres->obj->TA_data.ta_cols.ta_cols_val, + princp); + } + if (! ret_en_objs) { + fnr = db_nextib(full_tblnm, &cookie, 0, NULL); + if (fnr->status != NIS_SUCCESS) { + cook_err_keyresp(&resp, YP_NOMORE); + XFREE(full_tblnm); + return (&resp); + } + cookie = fnr->cookie; + } + } while ((ret_en_objs == NULL) && (fnr->status == NIS_SUCCESS)); + + if (fnr->status != NIS_SUCCESS) { + cook_err_keyresp(&resp, YP_NOMORE); + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + XFREE(full_tblnm); + return (&resp); + } + if (__type_of(ret_en_objs) != NIS_ENTRY_OBJ) { + cook_err_keyresp(&resp, YP_NOKEY); + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + XFREE(full_tblnm); + return (&resp); + } + e = &(ret_en_objs->EN_data); + + if ((strcmp(table, "cred") == 0) && + (strcmp(COLVAL(1), "DES") != 0)) { + fnr = db_nextib(full_tblnm, &cookie, 0, NULL); + if (fnr->status != NIS_SUCCESS) { + cook_err_keyresp(&resp, YP_NOMORE); + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + XFREE(full_tblnm); + return (&resp); + } + cookie = fnr->cookie; + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + goto doagain; + } + + if ((strcmp(table, "cred") == 0) && + (strcmp(COLVAL(1), "DES") == 0) && + netid_wart) { + long yperr = YP_TRUE; + + netid_e = e; + e = get_netid_entry(dbres->obj, + netid_e, full_tblnm, princp, &yperr); + if (! e) { + fnr = db_nextib(full_tblnm, &cookie, 0, NULL); + if (fnr->status != NIS_SUCCESS) { + cook_err_keyresp(&resp, YP_NOMORE); + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + XFREE(full_tblnm); + return (&resp); + } + cookie = fnr->cookie; + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + goto doagain; + } + } + add_cleanup((void (*)())XFREE, + (char *)cookie.n_bytes, "yp (first) cookie"); + XFREE(full_tblnm); + + /* construct a complete entry from all columns */ + memset(record, 0, YPMAXRECORD); + t = &(record[0]); + status = cook_record_from_entry(e, &t, &len, table, dbres->obj); + if (status != NIS_SUCCESS) { + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + cook_err_keyresp(&resp, err_conv_nistoyp(status)); + return (&resp); + } + + key_ndx = get_keyndx(&(dbres->obj->TA_data), column); + if ((key_ndx < 0) || (key_ndx > (e->en_cols.en_cols_len - 1))) { + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + cook_err_keyresp(&resp, YP_NOKEY); + return (&resp); + } else { + /* + * We are cheating the yp_first/next client by sending the pattern + * "<key>\n\0#<cookie><int>" as the key in resp.keydat. They loose + * if they don't send the same key back to us or send it back after + * str*cpy()'ing to a new place. + */ + int enlen; + char *enval; + char *pattern = "\n\0#"; + int patlen = 3; + + if (netid_wart && netid_e) + e = netid_e; + enlen = COLLEN(key_ndx); + enval = COLVAL(key_ndx); + resp.keydat.dptr = + XMALLOC(enlen + patlen + cookie.n_len + sizeof (int)); + add_cleanup((void (*)())XFREE, (char *)resp.keydat.dptr, + "yp (first) resp.keydat.dptr"); + resp.keydat.dsize = enlen + patlen + cookie.n_len + + sizeof (int); + memcpy(resp.keydat.dptr, enval, enlen); + memcpy(resp.keydat.dptr + enlen, pattern, patlen); + memcpy((resp.keydat.dptr + enlen + patlen), cookie.n_bytes, + cookie.n_len); + /* skip these many bytes before getting to the cookie */ + enlen += patlen; + memcpy((resp.keydat.dptr + enlen + cookie.n_len), + (char *)&enlen, sizeof (int)); + } + + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + resp.valdat.dptr = record; + resp.valdat.dsize = len; + + return (&resp); +} + +#undef resp + +struct ypresp_key_val * +ypproc_next_svc(req, rqstp) + struct ypreq_key *req; + struct svc_req *rqstp; +{ +#define resp (__nis_get_tsd()->ypproc_next_svc_resp) + char *t, *table, *column, *full_tblnm; + int len; + entry_obj *e, *netid_e = NULL; + nis_error status; + char *domain; + nis_object *ret_en_objs; + nis_db_result *dbres; + char princp[1024]; + nis_fn_result *fnr; + netobj cookie; + int key_ndx; + int all_readable = 0; + + if ((domain = check_domain(req->domain)) == 0 || + !check_map(req->map) || + !check_key(&req->keydat)) { + resp.status = YP_BADARGS; + return (&resp); + } + + resp.status = YP_TRUE; + if (verbose) + syslog(LOG_INFO, "ypserv: yp_next MAP = %s from %s", + req->map, getcaller_inet(rqstp)); + + /* + * All YP requests are unauthenticated and are successful + * only if the database has nobody-read access. + */ + + map2table(req->map, &table, &column); + if (strcmp(table, "netgroup") == 0) { + cook_err_keyresp(&resp, YP_NOMORE); + return (&resp); + } + + if ((full_tblnm = (char *)XCALLOC(1, strlen(table) + ORGLEN + + strlen(domain) + 1)) == NULL) { + cook_err_keyresp(&resp, YP_YPERR); + return (&resp); + } + strcpy(full_tblnm, table); + strcat(full_tblnm, ORGDIR); + strcat(full_tblnm, domain); + if (*(domain + strlen(domain) - 1) != '.') + strcat(full_tblnm, "."); + + /* Get the information base (table) object from the database */ + dbres = db_lookup(full_tblnm); + /* + * MEMORY POLICY: db_lookup adds dbres to the cleanup list. + * We use it and forget it. + */ + if (dbres->status != NIS_SUCCESS || (!dbres->obj)) { + if (dbres->status == NIS_NOTFOUND) + cook_err_keyresp(&resp, + err_conv_nistoyp(NIS_NOSUCHTABLE)); + else + cook_err_keyresp(&resp, + err_conv_nistoyp(dbres->status)); + XFREE(full_tblnm); + return (&resp); + } + if (__type_of(dbres->obj) != NIS_TABLE_OBJ) { + /* there goes yp's limited knowledge of the NIS+ universe ! */ + cook_err_keyresp(&resp, err_conv_nistoyp(NIS_NOSUCHTABLE)); + XFREE(full_tblnm); + return (&resp); + } + + /* + * We have cheated the yp_first/next client by sending the pattern + * "<key>\n\0#<cookie><int>" as the key in resp.keydat. They loose + * if they haven't sent the same key back to us or sent it back after + * str*cpy()'ing to a new place. + */ + memcpy((char *)&len, + (req->keydat.dptr + req->keydat.dsize - sizeof (int)), + sizeof (int)); + if ((len < 0) || (len > req->keydat.dsize)) { + /* We got a cookie we can't understand */ + cook_err_keyresp(&resp, YP_NOMORE); + XFREE(full_tblnm); + return (&resp); + } + t = (req->keydat.dptr) + len; + if ((*(t - 1) != '#') || (*(t -2) != '\0')) { + /* We got a cookie we can't understand */ + cook_err_keyresp(&resp, YP_NOMORE); + XFREE(full_tblnm); + return (&resp); + } + cookie.n_len = req->keydat.dsize - len - sizeof (int); + cookie.n_bytes = XMALLOC(cookie.n_len); + memcpy(cookie.n_bytes, t, cookie.n_len); + + nis_getprincipal(princp, rqstp); + all_readable = __can_do(NIS_READ_ACC, dbres->obj->zo_access, + dbres->obj, princp); + fnr = db_nextib(full_tblnm, &cookie, 0, NULL); + if (fnr->status != NIS_SUCCESS) { + cook_err_keyresp(&resp, YP_NOMORE); + XFREE(full_tblnm); + return (&resp); + } + cookie = fnr->cookie; +doagain: + do { + if (all_readable) { + ret_en_objs = fnr->obj; + break; + } else { + ret_en_objs = nis_censor_object(fnr->obj, + dbres->obj->TA_data.ta_cols.ta_cols_val, + princp); + } + if (! ret_en_objs) { + fnr = db_nextib(full_tblnm, &cookie, 0, NULL); + if (fnr->status != NIS_SUCCESS) { + cook_err_keyresp(&resp, YP_NOMORE); + XFREE(full_tblnm); + return (&resp); + } + cookie = fnr->cookie; + } + } while ((ret_en_objs == NULL) && (fnr->status == NIS_SUCCESS)); + + if (fnr->status != NIS_SUCCESS) { + cook_err_keyresp(&resp, YP_NOMORE); + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + XFREE(full_tblnm); + return (&resp); + } + if (__type_of(ret_en_objs) != NIS_ENTRY_OBJ) { + cook_err_keyresp(&resp, YP_NOKEY); + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + XFREE(full_tblnm); + return (&resp); + } + e = &(ret_en_objs->EN_data); + + if ((strcmp(table, "cred") == 0) && + (strcmp(COLVAL(1), "DES") != 0)) { + fnr = db_nextib(full_tblnm, &cookie, 0, NULL); + if (fnr->status != NIS_SUCCESS) { + cook_err_keyresp(&resp, YP_NOMORE); + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + XFREE(full_tblnm); + return (&resp); + } + cookie = fnr->cookie; + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + goto doagain; + } + + if ((strcmp(table, "cred") == 0) && + (strcmp(COLVAL(1), "DES") == 0) && + netid_wart) { + long yperr = YP_TRUE; + + netid_e = e; + e = get_netid_entry(dbres->obj, + netid_e, full_tblnm, princp, &yperr); + if (! e) { + fnr = db_nextib(full_tblnm, &cookie, 0, NULL); + if (fnr->status != NIS_SUCCESS) { + cook_err_keyresp(&resp, YP_NOMORE); + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + XFREE(full_tblnm); + return (&resp); + } + cookie = fnr->cookie; + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + goto doagain; + } + } + add_cleanup((void (*)())XFREE, + (char *)cookie.n_bytes, "yp (next) cookie"); + XFREE(full_tblnm); + + /* construct a complete entry from all columns */ + memset(record, 0, YPMAXRECORD); + t = &(record[0]); + status = cook_record_from_entry(e, &t, &len, table, dbres->obj); + if (status != NIS_SUCCESS) { + cook_err_keyresp(&resp, err_conv_nistoyp(status)); + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + return (&resp); + } + + key_ndx = get_keyndx(&(dbres->obj->TA_data), column); + if ((key_ndx < 0) || (key_ndx > (e->en_cols.en_cols_len - 1))) { + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + cook_err_keyresp(&resp, YP_NOMORE); + return (&resp); + } else { + /* + * We are cheating the yp_first/next client by sending the pattern + * "<key>\n\0#<cookie><int>" as the key in resp.keydat. They loose + * if they don't send the same key back to us or send it back after + * str*cpy()'ing to a new place. + */ + int enlen; + char *enval; + char *pattern = "\n\0#"; + int patlen = 3; + + if (netid_wart && netid_e) + e = netid_e; + enlen = COLLEN(key_ndx); + enval = COLVAL(key_ndx); + resp.keydat.dptr = XMALLOC(enlen + patlen + cookie.n_len + + sizeof (int)); + add_cleanup((void (*)())XFREE, (char *)resp.keydat.dptr, + "yp (next) resp.keydat.dptr"); + resp.keydat.dsize = enlen + patlen + cookie.n_len + + sizeof (int); + memcpy(resp.keydat.dptr, enval, enlen); + memcpy(resp.keydat.dptr + enlen, pattern, patlen); + memcpy((resp.keydat.dptr + enlen + patlen), cookie.n_bytes, + cookie.n_len); + /* skip these many bytes before getting to the cookie */ + enlen += patlen; + memcpy((resp.keydat.dptr + enlen + cookie.n_len), + (char *)&enlen, sizeof (int)); + } + + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + + resp.valdat.dptr = record; + resp.valdat.dsize = len; + return (&resp); +} + +#undef resp + +struct ypresp_all * +ypproc_all_svc(req, rqstp) + struct ypreq_nokey *req; + struct svc_req *rqstp; +{ +#define resp (__nis_get_tsd()->ypproc_all_svc_resp) + char *table, *column, *full_tblnm; + nis_error status; + char *domain; + pid_t pid; + SVCXPRT *transp = rqstp->rq_xprt; + + nis_db_result *dbres; + char princp[1024]; + + if ((domain = check_domain(req->domain)) == 0 || + !check_map(req->map)) { + resp.status = YP_BADARGS; + return (&resp); + } + + memset((char *)&resp, 0, sizeof (struct ypresp_all)); + resp.status = YP_TRUE; + if (verbose) + syslog(LOG_INFO, "ypserv: yp_all MAP = %s from %s", + req->map, getcaller_inet(rqstp)); + + /* + * All YP requests are unauthenticated and are successful + * only if the database has nobody-read access. + */ + + map2table(req->map, &table, &column); + if (strcmp(table, "netgroup") == 0) { + resp.status = YP_NOMORE; + return (&resp); + } + + if ((full_tblnm = (char *)XCALLOC(1, strlen(table) + ORGLEN + + strlen(domain) + 1)) == NULL) { + resp.status = YP_YPERR; + return (&resp); + } + add_cleanup((void (*)())XFREE, + (char *)full_tblnm, "yp (all) full_tblnm"); + strcpy(full_tblnm, table); + strcat(full_tblnm, ORGDIR); + strcat(full_tblnm, domain); + if (*(domain + strlen(domain) - 1) != '.') + strcat(full_tblnm, "."); + + /* Get the information base (table) object from the database */ + dbres = db_lookup(full_tblnm); + /* + * MEMORY POLICY: db_lookup adds dbres to the cleanup list. + * We use it and forget it. + */ + if (dbres->status != NIS_SUCCESS || (!dbres->obj)) { + if (dbres->status == NIS_NOTFOUND) + resp.status = err_conv_nistoyp(NIS_NOSUCHTABLE); + else + resp.status = err_conv_nistoyp(dbres->status); + XFREE(full_tblnm); + return (&resp); + } + if ((__type_of(dbres->obj) != NIS_TABLE_OBJ) || + (column && (strcmp(column, DEFAULTKEY) != 0) && +((resp.key_column_ndx = get_keyndx(&(dbres->obj->TA_data), column)) < 0 || + (!((dbres->obj->TA_data.ta_cols.ta_cols_val + + resp.key_column_ndx)->tc_flags & TA_SEARCHABLE))))) { + resp.status = err_conv_nistoyp(NIS_NOSUCHTABLE); + XFREE(full_tblnm); + return (&resp); + } + + nis_getprincipal(princp, rqstp); + resp.table_name = full_tblnm; + resp.princp = strdup(princp); + add_cleanup((void (*)()) XFREE, (char *)resp.princp, "principal name"); + resp.status = YP_TRUE; /* so far, so good. */ + resp.table_zobj = dbres->obj; + + /* + * Send reply from this thread without forking. An alternative + * implementation would be to create a new thread to send the + * reply; both have their complementary advantages and disadvantages: + * + * Send reply from this thread: + * + * Simpler to implement + * + * No new thread created, so probably slightly quicker + * + * No runaway resource usage (i.e., lots of threads) in + * rpc.nisd + * + * Don't have to worry about 'transp' (= rqstp->rq_xprt) + * possibly becoming invalid while a child thread is running. + * + * Send reply from a new thread: + * + * Less risk of denial-of-service if yp_all() calls use up + * all of the auto RPC mode service threads + * + * We opt for simplicity. + */ + if (!svc_sendreply(transp, + (xdrproc_t)xdr_ypresp_all, (char *)&resp)) { + svcerr_systemerr(transp); + } + + return (0); +} + +#undef resp + +static void +free_ypmaplist(maplist) + struct ypmaplist *maplist; +{ + struct ypmaplist *tmp; + while (maplist) { + tmp = maplist->ypml_next; + (void) XFREE(maplist); + maplist = tmp; + } +} + +struct ypresp_maplist * +ypproc_maplist_svc(dname, rqstp) + string_t *dname; + struct svc_req *rqstp; +{ +#define maplist (__nis_get_tsd()->ypproc_maplist_svc_maplist) + struct ypmaplist *map, *mapnames; + nis_object *z_obj = NULL; + nis_error status; + char *domain; + int namesz, i; + char orgdir[YPMAXDOMAIN + ORGLEN]; + nis_fn_result *fnr; + netobj cookie; + + if ((domain = check_domain(*dname)) == 0) { + maplist.status = YP_BADARGS; + maplist.list = 0; + return (&maplist); + } + + maplist.status = YP_TRUE; + maplist.list = NULL; + if (verbose) + syslog(LOG_INFO, "ypserv: yp_maplist from %s", + getcaller_inet(rqstp)); + status = getzobj_orgdir(domain, &z_obj); + if (status != NIS_SUCCESS || z_obj == NULL) { + syslog(LOG_INFO, "ypserv: yp_maplist failed for ORGDIR1.%s.", + domain); + maplist.status = err_conv_nistoyp(NIS_NAMEUNREACHABLE); + return (&maplist); + } + strcpy(orgdir, ORGDIR2); + strcat(orgdir, domain); + if (*(domain + strlen(domain) - 1) != '.') + strcat(orgdir, "."); + + /* + * MEMORY POLICY on db_firstib/nextib: always call it with + * !(flags & 2), the db_*ib routines will add fnr and fnr->obj + * to the cleanup list. We never free cookie.n_bytes if the + * cookie is to be passwd to db_nextib(). db_nextib() always + * frees the cookie it is given. + */ + fnr = db_firstib(orgdir, 0, NULL, FN_NOMANGLE, NULL); + while ((fnr->status == NIS_SUCCESS) && + (__type_of(fnr->obj) == NIS_TABLE_OBJ)) { + char *tblname, mname[YPMAXMAP + 1]; + struct table_obj *t_obj; + struct table_col *tcol; + + cookie = fnr->cookie; + memset(mname, 0, YPMAXMAP+1); + /* + * POLICY: For each table name, the corresponding map names + * are concocted by expanding <tablename>.by<approriate_column>, + * where appropriate_column is a searchable column in the + * table that does not have the names CNAME (ignore this) or + * DEFAULTKEY (expand only to <tablename>). Idea is that the + * client MUST be able to lookup EVERY such mapname returned, + * should not return too many mapnames and should not confuse + * users by renaming conventional NIS mapnames. + * + * HACKS: As a consequence of the above policy, we must do + * something special to rename all the "auto_*" tables to + * "auto.*", suppress printing "services.byproto", print + * a new map "services.byservicename" that really looks at the + * key "name" in the services table (look at map2table() for + * the explanation of this hack), and print "mail.aliases" + * and "mail.byaddr" as the only two maps for the mail_aliases + * table. + */ + tblname = fnr->obj->zo_name; + t_obj = &(fnr->obj->TA_data); + for (i = 0; i < t_obj->ta_cols.ta_cols_len; i++) { + tcol = t_obj->ta_cols.ta_cols_val + i; + if (!(tcol->tc_flags & TA_SEARCHABLE) || + (strcmp(CNAME, tcol->tc_name) == 0) || + (strlen(tblname) + strlen(tcol->tc_name) + 3) > + YPMAXMAP) { + continue; + } + + if (strcmp(tblname, "mail_aliases") == 0) { + if (strcmp(MAILNAME, tcol->tc_name) == 0) + strcpy(mname, "mail.aliases"); + else if (strcmp(MAILADDR, tcol->tc_name) == 0) + strcpy(mname, "mail.byaddr"); + else + continue; + } else if (strncmp(tblname, "auto_", 5) == 0) { + char *ptr; + strcpy(mname, tblname); + ptr = strchr(mname, '_'); + if (ptr) + *ptr = '.'; + } else if (strcmp(tblname, "services") == 0) { + if (strcmp("name", tcol->tc_name) == 0) + strcpy(mname, "services.byservicename"); + else if (strcmp("port", tcol->tc_name) == 0) + strcpy(mname, "services.byname"); + else + continue; + } else if (strcmp(tblname, "cred") == 0) { + if (strcmp("auth_type", tcol->tc_name) == 0) + strcpy(mname, "publickey.byname"); + else if (strcmp("auth_name", + tcol->tc_name) == 0) + strcpy(mname, "netid.byname"); + else + continue; + } else if (strcmp(tblname, "netgroup") == 0) { + if (strcmp(NGROUPNAME, tcol->tc_name) == 0) + strcpy(mname, "netgroup"); + else + continue; + } else { + strcpy(mname, tblname); + if (strcmp(tcol->tc_name, DEFAULTKEY) != 0) { + strcat(mname, "."); + strcat(mname, BY); + strcat(mname, tcol->tc_name); + } + } + if ((map = (struct ypmaplist *)XMALLOC( + (unsigned)sizeof (struct ypmaplist))) == NULL) { + maplist.status = YP_YPERR; + break; + } + map->ypml_next = maplist.list; + maplist.list = map; + namesz = strlen(mname); + if (namesz <= YPMAXMAP) { + (void) strcpy(map->ypml_name, mname); + } else { + (void) strncpy(map->ypml_name, mname, YPMAXMAP); + map->ypml_name[YPMAXMAP] = '\0'; + } + } + fnr = db_nextib(orgdir, &cookie, FN_NOMANGLE, NULL); + } + add_cleanup(free_ypmaplist, (void *)(maplist.list), "ypmaplist"); + return (&maplist); +} + +#undef maplist + +/* + * This one does most of the server side work for yp_all. Of course, + * should not be generated by rpcgen. Fetches the whole database + * for the map and serializes a stream of struct ypresp_key_val's. + */ +bool_t +xdr_ypresp_all(xdrs, resp) + XDR *xdrs; + struct ypresp_all *resp; +{ + struct ypresp_key_val respdat; + nis_object *ret_en_objs; + nis_fn_result *fnr; + netobj cookie; + entry_obj *e, *netid_e = NULL; + int len; + int nokey = 0; /* can we isolate the key part of the entry ? */ + bool_t more = TRUE; + nis_error status; +#define short_tblnm (__nis_get_tsd()->xdr_ypresp_all_short_tblnm) + char *t, *p = short_tblnm; + int all_readable = 0; + + respdat.keydat.dptr = respdat.valdat.dptr = (char *)NULL; + respdat.keydat.dsize = respdat.valdat.dsize = 0; + respdat.status = resp->status; + + if (!resp->table_name || !resp->princp || (resp->status != YP_TRUE)) { + if (!xdr_bool(xdrs, &more)) + return (FALSE); + if (!xdr_ypresp_key_val(xdrs, &respdat)) + return (FALSE); + more = FALSE; + if (!xdr_bool(xdrs, &more)) + return (FALSE); + return (TRUE); + } + + memset(p, 0, YPMAXMAP); + strcpy(p, resp->table_name); + t = strchr(p, '.'); + if (t) + *t = '\0'; + + all_readable = __can_do(NIS_READ_ACC, resp->table_zobj->zo_access, + resp->table_zobj, resp->princp); + /* + * MEMORY POLICY on db_firstib/nextib: always call it with + * !(flags & 2), the db_*ib routines will add fnr and fnr->obj + * to the cleanup list. We never free cookie.n_bytes if the + * cookie is to be passwd to db_nextib(). db_nextib() always + * frees the cookie it is given. + */ + fnr = db_firstib(resp->table_name, 0, NULL, FN_NOMANGLE, NULL); + cookie = fnr->cookie; + while (fnr->status == NIS_SUCCESS) { + if (all_readable) + ret_en_objs = fnr->obj; + else + ret_en_objs = nis_censor_object(fnr->obj, + resp->table_zobj->TA_data.ta_cols.ta_cols_val, + resp->princp); + if (ret_en_objs == NULL) { + fnr = db_nextib(resp->table_name, + &cookie, FN_NOMANGLE, NULL); + cookie = fnr->cookie; + continue; /* avoid the song 'n dance below */ + } + if (__type_of(ret_en_objs) != NIS_ENTRY_OBJ) { + respdat.status = YP_NOKEY; + if (!xdr_bool(xdrs, &more)) + return (FALSE); + if (!xdr_ypresp_key_val(xdrs, &respdat)) + return (FALSE); + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + break; + } + e = &(ret_en_objs->EN_data); + + if ((strcmp(p, "cred") == 0) && + (strcmp(COLVAL(1), "DES") != 0)) { + fnr = db_nextib(resp->table_name, + &cookie, FN_NOMANGLE, NULL); + cookie = fnr->cookie; + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + continue; + } + + if ((strcmp(p, "cred") == 0) && + (strcmp(COLVAL(1), "DES") == 0) && + netid_wart) { + long yperr = YP_TRUE; + + netid_e = e; + e = get_netid_entry(resp->table_zobj, netid_e, + resp->table_name, resp->princp, &yperr); + if (! e) { + fnr = db_nextib(resp->table_name, &cookie, + FN_NOMANGLE, NULL); + cookie = fnr->cookie; + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + continue; + } + } + + if ((resp->key_column_ndx < 0) || + (resp->key_column_ndx > (e->en_cols.en_cols_len - 1))) + nokey = 1; + + /* construct a complete entry from all columns */ + memset(record, 0, YPMAXRECORD); + t = &(record[0]); + status = cook_record_from_entry(e, &t, &len, p, + resp->table_zobj); + if (status != NIS_SUCCESS) { + respdat.status = err_conv_nistoyp(status); + if (!xdr_bool(xdrs, &more)) + return (FALSE); + if (!xdr_ypresp_key_val(xdrs, &respdat)) + return (FALSE); + break; + } + memset(keyval, 0, YPMAXRECORD); + if (! nokey) { + if (netid_wart && netid_e) + e = netid_e; + strncpy(keyval, + COLVAL(resp->key_column_ndx), + COLLEN(resp->key_column_ndx)); + if (serv_wart && + (strcmp(p, "services") == 0)) { + strcat(keyval, "/"); + strcat(keyval, COLVAL(2)); + } + } + respdat.keydat.dptr = keyval; + respdat.keydat.dsize = strlen(keyval); + respdat.valdat.dptr = record; + respdat.valdat.dsize = len; + if (!xdr_bool(xdrs, &more)) + return (FALSE); + if (!xdr_ypresp_key_val(xdrs, &respdat)) + return (FALSE); + fnr = db_nextib(resp->table_name, &cookie, FN_NOMANGLE, NULL); + cookie = fnr->cookie; + if (ret_en_objs && !all_readable) + nis_destroy_object(ret_en_objs); + } + + more = FALSE; + if (!xdr_bool(xdrs, &more)) + return (FALSE); + return (TRUE); +} + +#undef short_tblnm + +static char * +upcase(s) + char *s; +{ + char *t; + + for (t = s; *t; t++) + *t = toupper(*t); + return (s); +} + +/* + * Following cook_* routines are very flimsy, and should be kept static. + */ + +static +cook_err_resp(resp, stat) + struct ypresp_val *resp; + long stat; +{ + resp->status = stat; + resp->valdat.dptr = NULL; + resp->valdat.dsize = 0; +} + +static +cook_err_keyresp(keyresp, stat) + struct ypresp_key_val *keyresp; + long stat; +{ + keyresp->status = stat; + keyresp->keydat.dptr = NULL; + keyresp->keydat.dsize = 0; + keyresp->valdat.dptr = NULL; + keyresp->valdat.dsize = 0; +} + +/* + * Takes a NIS+ entry and the column separator and returns + * a reasonable looking YP record and its length. + */ + +static nis_error +cook_record_from_entry(e, record_dat, record_len, table, tobj) + entry_obj *e; + char **record_dat; + int *record_len; + char *table; + nis_object *tobj; +{ + int i, len; + char *entrydat = *record_dat, *last; + char minibuf[80]; + table_obj *tbl = &tobj->TA_data; + table_col *cols = tbl->ta_cols.ta_cols_val; + int num_cols = tbl->ta_cols.ta_cols_len; + + for (i = 0, len = 0; i < num_cols; i++, len++) + len += e->en_cols.en_cols_val[i].ENLEN; + + /* a rare occasion, to safeguard against garbage in e */ + if (++len > YPMAXRECORD) + return (NIS_NOMEMORY); + + if (strcmp(table, "hosts") == 0) + sprintf(entrydat, "%s %s\t#%s", COLVAL(2), COLVAL(1), + COLVAL(3)); + else if (strcmp(table, "passwd") == 0) + sprintf(entrydat, "%s:%s:%s:%s:%s:%s:%s", COLVAL(0), + COLVAL(1), COLVAL(2), COLVAL(3), + COLVAL(4), COLVAL(5), COLVAL(6)); + else if (strcmp(table, "group") == 0) + sprintf(entrydat, "%s:%s:%s:%s", COLVAL(0), COLVAL(1), + COLVAL(2), COLVAL(3)); + else if (strcmp(table, "services") == 0) + sprintf(entrydat, "%s %s/%s #%s", COLVAL(1), COLVAL(3), + COLVAL(2), COLVAL(4)); + else if (strcmp(table, "ethers") == 0) + sprintf(entrydat, "%s %s #%s", COLVAL(0), COLVAL(1), COLVAL(2)); + else if (strcmp(table, "networks") == 0) + sprintf(entrydat, "%s %s #%s", COLVAL(1), COLVAL(2), COLVAL(3)); + else if (strcmp(table, "netmasks") == 0) + sprintf(entrydat, "%s #%s", COLVAL(1), COLVAL(2)); + else if (strcmp(table, "rpc") == 0) + sprintf(entrydat, "%s %s #%s", COLVAL(1), COLVAL(2), COLVAL(3)); + else if (strcmp(table, "timezone") == 0) + sprintf(entrydat, "%s %s", COLVAL(1), COLVAL(0)); + else if (strcmp(table, "protocols") == 0) { + minibuf[79] = '\0'; /* make sure its terminated */ + strncpy(minibuf, COLVAL(1), 79); + sprintf(entrydat, "%s %s %s #%s", minibuf, COLVAL(2), + upcase(COLVAL(1)), COLVAL(3)); + } else if (strcmp(table, "cred") == 0) { + if (netid_wart) { + if (strcmp(COLVAL(1), "DES") == 0) { + /* netid for root */ + char *col, *p, *q; + + if ((col = strdup(COLVAL(2))) == NULL) + return (NIS_NOMEMORY); + p = strchr(col, '.'); + if ((p != NULL) && + ((q = strchr(++p, '@')) != NULL)) { + *q = '\0'; + sprintf(entrydat, "0:%s", p); + } else { + free(col); + return (NIS_BADOBJECT); + } + free(col); + } else { + /* netid for user */ + sprintf(entrydat, "%s:%s", COLVAL(2), + COLVAL(3)); + } + } else { + char *p; + p = strchr(COLVAL(3), ':'); + if (p) + *p = 0; + /* weed out the uid/gid junk. why is it there ? */ + sprintf(entrydat, "%s:%s", COLVAL(3), COLVAL(4)); + } + } else if (strcmp(table, "mail_aliases") == 0) + if (mail_wart_byaddr) + sprintf(entrydat, "%s", COLVAL(0)); + else + sprintf(entrydat, " %s", COLVAL(1)); + else if (strncmp(table, "auto_", 5) == 0) + sprintf(entrydat, "%s", COLVAL(1)); + else { + /* + * For 2-column key/value tables we build a value + * with whatever is in column 1. For other tables + * we combine all of the columns with the table + * separator. + */ + if (num_cols == 2 && + strcasecmp(cols[0].tc_name, "key") == 0 && + strcasecmp(cols[1].tc_name, "value") == 0) { + sprintf(entrydat, "%s", COLVAL(1)); + } else { + entrydat[0] = 0; + for (i = 0; i < num_cols; i++) { + if (i) { + strncat(entrydat, + (char *)&tbl->ta_sep, 1); + } + strcat(entrydat, COLVAL(i)); + } + } + } + if (verbose) + syslog(LOG_INFO, + "cook_record_from_entry: returns %s", entrydat); + *record_len = strlen(entrydat); + + /* XXX Just a hack to workaround 1083096 ??? */ + if (strcmp(table, "mail_aliases") == 0) + (*record_len)++; + + return (NIS_SUCCESS); +} + +#define best_address (__nis_get_tsd()->best_host_address_best_address) + +static +char * +best_host_address(nis_object *obj, int nobj, struct svc_req *rqstp) +{ + int i; + SVCXPRT *xp; + struct netconfig *nc; + ulong_t addr; + char *beststring; + ulong_t bestaddr; + char *tstring; + ulong_t taddr; + + /* determine the address of the client and store in 'addr' */ + if (rqstp == NULL) + return (NULL); + xp = rqstp->rq_xprt; + nc = getnetconfigent(xp->xp_netid); + if (nc == NULL) + return (NULL); + if (strcmp(nc->nc_protofmly, "inet") != 0) { + freenetconfigent(nc); + return (NULL); + } + addr = ((struct sockaddr_in *)xp->xp_rtaddr.buf)->sin_addr.s_addr; + + /* start with first object as best address */ + beststring = ENTRY_VAL(&obj[0], 2); + bestaddr = inet_addr(beststring); + + /* loop through other objects and see if there is a better address */ + for (i = 1; i < nobj; i++) { + tstring = ENTRY_VAL(&obj[i], 2); + taddr = inet_addr(tstring, 2); + if (ntohl(addr ^ taddr) < ntohl(addr ^ bestaddr)) { + bestaddr = taddr; + beststring = tstring; + } + } + + /* set global variable to best address found */ + return (strdup(beststring)); +} + +/* + * This is a qsort comparison function for host objects. + * The objects are sorted by address. For hosts with the + * same address, the canonical object (cname == name) is + * sorted before the other objects with the same address. + */ +static int +host_object_compar(p1, p2) + const void *p1; + const void *p2; +{ + int st; + int iscanon1; + int iscanon2; + entry_obj *e1 = &((nis_object *)p1)->EN_data; + entry_obj *e2 = &((nis_object *)p2)->EN_data; + + /* if addresses differ, return comparison */ + st = strcmp(ENCOLVAL(e1, 2), ENCOLVAL(e2, 2)); + if (st != 0) { + /* + * If either address matches the best host address for + * the client, sort that before all others. + */ + if (best_address) { + if (strcmp(ENCOLVAL(e1, 2), best_address) == 0) + return (-1); + else if (strcmp(ENCOLVAL(e2, 2), best_address) == 0) + return (1); + } + return (st); + } + + /* determine if any objects are canonical (cname == name) */ + iscanon1 = strcmp(ENCOLVAL(e1, 0), ENCOLVAL(e1, 1)); + iscanon2 = strcmp(ENCOLVAL(e2, 0), ENCOLVAL(e2, 1)); + + if (iscanon1 == iscanon2) + return (0); + + if (iscanon1) + return (-1); + else + return (1); +} + + +#define STRCAT_CHECK(ptr, s, space) { \ + int len; \ + \ + len = strlen(s); \ + if (len + 1 > space) { \ + nis_err = NIS_NOMEMORY; \ + goto out_of_space; \ + } else { \ + strcat(ptr, s); \ + ptr += len; \ + space -= len; \ + } \ +} + +/* + * Takes an NIS+ entry and returns a reasonable looking YP host record + * and its length. We sort the host objects by host address. For + * hosts with the same address, the canonical host object (cname == name) + * is placed before the other objects with the same address. For each + * address, we print the entries for that address. If a comment appears in + * an entry, then we save it and print it after all of the host names for + * the address have been printed. + * + * The fields in a host object are: + * COLVAL(0) - official host name (canonical name) + * COLVAL(1) - host name (alias name) + * COLVAL(2) - host address + * COLVAL(3) - comment + * + * We use COLGETVAL instead COLVAL for the comment field so that if it + * is null we don't put an obnoxious "\t#" at the end. + */ +static nis_error +cook_host_record(objs, num_objs, record_dat, record_len, rqstp) + nis_object *objs; + int num_objs; + char **record_dat; + int *record_len; + struct svc_req *rqstp; +{ + int i; + entry_obj *e; + char *comment; + char *ptr; + char *s; + int space; + int did_line; + char *current_address; + char *completed_entry; + char *cname; + char *name; + char *addr; + nis_error nis_err = NIS_SUCCESS; /* assume success */ + + *record_len = 0; + + if (num_objs == 0) { + return (NIS_SUCCESS); + } else if (num_objs > 1) { + best_address = best_host_address(objs, num_objs, rqstp); + qsort((void *)objs, num_objs, sizeof (nis_object), + host_object_compar); + if (best_address) { + free(best_address); + best_address = NULL; + } + } + + ptr = *record_dat; /* this is where we write our data */ + space = YPMAXRECORD; /* this is how much space we have */ + comment = 0; /* we save the comment field here */ + did_line = 0; /* if true, we have started an entry */ + completed_entry = ptr; /* keeps track of last full entry */ + current_address = ""; + + for (i = 0; i < num_objs; i++) { + e = &objs[i].EN_data; + cname = COLVAL(0); + name = COLVAL(1); + addr = COLVAL(2); + + if (strcmp(addr, current_address) != 0) { + /* new address, finish off previous entry */ + current_address = addr; + if (did_line) { + if (comment) { + STRCAT_CHECK(ptr, "\t# ", space); + STRCAT_CHECK(ptr, comment, space); + comment = 0; + } + STRCAT_CHECK(ptr, "\n", space); + } + STRCAT_CHECK(ptr, addr, space); + STRCAT_CHECK(ptr, " ", space); + STRCAT_CHECK(ptr, cname, space); + did_line = 1; + } + if (strcmp(name, cname) != 0) { + STRCAT_CHECK(ptr, " ", space); + STRCAT_CHECK(ptr, name, space); + } + if (!comment) { + s = COLGETVAL(3); + if (s && *s) + comment = s; + } + + completed_entry = ptr; + } + if (did_line && comment) { + STRCAT_CHECK(ptr, "\t# ", space); + STRCAT_CHECK(ptr, comment, space); + } + completed_entry = ptr; + +out_of_space: + + *completed_entry = 0; /* in case failed on partial entry */ + *record_len = completed_entry - *record_dat; + + if (verbose) { + syslog(LOG_INFO, + "cook_host_record: returning \"%s\"", *record_dat); + } + + return (nis_err); +} + +/* + * Given an entry in the netgroup table, constructs a string + * consisting of either a member netgroup name, or a leaf + * netgroup like "(host,name,domain)". + */ +static void +cook_an_ngroup(e, val, vallen) + entry_obj *e; + char **val; + int *vallen; +{ + if (strcmp(COLVAL(1), "") == 0) { + *vallen = COLLEN(2) + COLLEN(3) + COLLEN(4) + 5; + if (*vallen > YPMAXRECORD) { + sprintf(*val, ""); + *vallen = 1; + } else + sprintf(*val, "(%s,%s,%s)", + COLVAL(2), COLVAL(3), COLVAL(4)); + } else { + *vallen = COLLEN(1) + 1; + if (*vallen > YPMAXRECORD) { + sprintf(*val, ""); + *vallen = 1; + } else + sprintf(*val, COLVAL(1)); + } +} + +/* + * Goes through column names in table object and returns the + * index for the one that matches with the col_val passed. + * indexing is 0 thro n and returns -1 on failure. + */ +static int +get_keyndx(t_obj, col_val) + table_obj *t_obj; + char *col_val; +{ + int i; + + for (i = 0; i < t_obj->ta_cols.ta_cols_len; i++) + if (strcmp(col_val, + (t_obj->ta_cols.ta_cols_val + i)->tc_name) == 0) + return (i); + return (-1); +} + +static char * +getcaller_inet(rqstp) + struct svc_req *rqstp; +{ +#define buf (__nis_get_tsd()->getcaller_inet_buf) + SVCXPRT *xp; + char *uaddr; + struct netconfig *nc; + + if (rqstp == NULL) + return (""); + xp = rqstp->rq_xprt; + nc = (struct netconfig *)getnetconfigent(xp->xp_netid); + + if (nc) + uaddr = taddr2uaddr(nc, &(xp->xp_rtaddr)); + else + uaddr = "<no netconfig ent>"; + + sprintf(buf, "[tp=%s, netid=%d, uaddr=%s]", + xp->xp_tp, xp->xp_netid, uaddr); + + if (nc) { + freenetconfigent(nc); + XFREE(uaddr); + } + return (buf); +} + +#undef buf + +/* + * Hacks here onwards may go into a library, potentially + * to be inflicted on the users. + */ + +long +err_conv_nistoyp(inerr) + nis_error inerr; +{ + switch (inerr) { + case NIS_SUCCESS: + case NIS_S_SUCCESS: return (YP_TRUE); + case NIS_NOTFOUND: + case NIS_S_NOTFOUND: return (YP_FALSE); + case NIS_NAMEUNREACHABLE: + case NIS_NOTSEARCHABLE: + case NIS_UNKNOWNOBJ: return (YP_NODOM); + case NIS_NOSUCHTABLE: return (YP_NOMAP); + case NIS_BADOBJECT: + case NIS_NOMEMORY: return (YP_YPERR); + default: return (YP_YPERR); + } +} + +/* + * Takes the name of a domain/directory, fully qualifies it + * the most naive way and finds out if the corresponding + * NIS+ directory is served by this server. + */ + +int +ypcheck_nisdir(dname) + char *dname; +{ + char name[YPMAXDOMAIN + 2]; + struct ticks ticks; + nis_object *z_obj = NULL; + nis_error status; + int len = strlen(dname); + + (void) strncpy(name, dname, len); + if (name[len - 1] != '.') + name[len++] = '.'; + name[len] = '\0'; + status = __directory_object(name, &ticks, FALSE, &z_obj); + /* + * MEMORY POLICY: __directory_object maintains a cache and + * returns a pointer to an entry into it. We do not free + * anything here. + */ + return (status == NIS_SUCCESS); +} + +/* + * A little different from ypcheck_nisdir. This one attempts to + * get the nis_object for the "ORGDIR.<domainname>[.]" directory. + */ +nis_error +getzobj_orgdir(dname, z_obj) + char *dname; + nis_object **z_obj; +{ + char name[YPMAXDOMAIN + ORGLEN]; + struct ticks ticks; + nis_error status; + int len; + + (void) strcpy(name, ORGDIR2); + (void) strncat(name, dname, strlen(dname)); + len = strlen(name); + if (name[len - 1] != '.') + name[len++] = '.'; + name[len] = '\0'; + /* don't bother who's master */ + status = __directory_object(name, &ticks, FALSE, z_obj); + /* + * MEMORY POLICY: __directory_object maintains a cache and + * returns a pointer to an entry into it. We do not free + * anything here. + */ + return (status); +} + + +/* + * Supply an NIS style mapname and get back pointers to the + * names of corresponding table in NIS+ and the column to + * search. Does not make any guarantees that the table + * and/or column indeed exist in the NIS+ namespace. + */ +void +map2table(xx_mapname, xx_table, xx_column) + char *xx_mapname; + char **xx_table; + char **xx_column; +{ +#define tbl (__nis_get_tsd()->map2table_tbl) +#define col (__nis_get_tsd()->map2table_col) + char *p, *t = tbl, *c = col; + + memset(t, 0, YPMAXMAP); + memset(c, 0, NIS_MAXATTRNAME); + *xx_table = t; + *xx_column = c; + + serv_wart = 0; + netid_wart = 0; + mult_lines_wart = 0; + + if (verbose) + syslog(LOG_INFO, "map2table: maps from %s", xx_mapname); + if (strcmp(xx_mapname, "services.byname") == 0) { + /* + * This is a special and weird case. + * We have to be compatible with past mistakes + * when some ??????? built the services.byname map + * with a key of the form "port/proto". + */ + strcpy(t, "services"); + strcpy(c, "port"); + serv_wart = 1; + if (verbose) + syslog(LOG_INFO, "map2table: maps to %s and %s", t, c); + return; + } + if (strcmp(xx_mapname, "services.byservicename") == 0) { + /* + * This is even more special and weird case. + * We are trying to be nice to the YP clients by + * giving a map they can match on using the key + * service/proto. It turns out, they can do + * the match only on "service" or "port" as + * the key too. + */ + strcpy(t, "services"); + strcpy(c, "name"); + serv_wart = 1; + if (verbose) + syslog(LOG_INFO, "map2table: maps to %s and %s", t, c); + return; + } + + if (strcmp(xx_mapname, "publickey.byname") == 0) { + strcpy(t, "cred"); + strcpy(c, "auth_name"); + if (verbose) + syslog(LOG_INFO, "map2table: maps to %s and %s", t, c); + return; + } + + if (strcmp(xx_mapname, "netid.byname") == 0) { + strcpy(t, "cred"); + strcpy(c, "auth_name"); + netid_wart = 1; + if (verbose) + syslog(LOG_INFO, "map2table: maps to %s and %s", t, c); + return; + } + + if (strncmp(xx_mapname, "mail.", 5) == 0) { + strcpy(t, "mail_aliases"); + if (strcmp(xx_mapname, "mail.byaddr") == 0) { + mail_wart_byaddr = 1; + strcpy(c, MAILADDR); + } else /* if (strcmp(xx_mapname, "mail.aliases) == 0) */ { + mail_wart_byaddr = 0; + strcpy(c, MAILNAME); + } + if (verbose) + syslog(LOG_INFO, "map2table: maps to %s and %s", t, c); + return; + } + + if ((p = strrchr(xx_mapname, '.')) != NULL) { + /* + * First check for maps of type X.byY and then replace + * all dots by _ + */ + char *tmp; + + tmp = p; + if (strncmp(++p, BY, 2) == 0) { + p += 2; + if (*p == NULL) { + /* BY was really a name */ + *tmp = '_'; /* replace _ back */ + p = NULL; + } else { + *tmp = '\0'; + } + } else { + p = NULL; + } + /* replace the other dots by _ */ + while ((tmp = strchr(xx_mapname, '.')) != NULL) + *tmp = '_'; + } + strcpy(t, xx_mapname); + + /* only the gethostbyYY client backends can handle multiple lines */ + if (strcmp(t, "hosts") == 0) + mult_lines_wart = 1; + + if (p) + strcpy(c, p); + else if (strcmp(xx_mapname, "netgroup") == 0) + strcpy(c, NGROUPNAME); + else + /* + * POLICY: The default column for all maps that want to + * use the backward compatibility and are not among the + * privileged 12 must be DEFAULTKEY. The 12 are: passwd, + * group, hosts, networks, services, rpc, mail_aliases, + * ethers, protocols, netmasks, publickey and bootparams. + */ + strcpy(c, DEFAULTKEY); + + if (verbose) + syslog(LOG_INFO, "map2table: maps to %s and %s", t, c); +} + +#undef tbl +#undef col diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/ypserv1.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/ypserv1.c new file mode 100644 index 0000000000..a502e68e48 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/ypserv1.c @@ -0,0 +1,153 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef lint +static char sccsid[] = "%Z%%M% %I% %E% SMI"; +#endif +/* + * YP Version 1 compatibility code added to rpc.nisd. + * + * ypserv1.c + * + * Copyright (c) 1993 Sun Microsystems Inc + * All rights reserved. + * + * This file provides incomplete support for YP Version 1. + * + * This addresses bug #1136940. + * + * If a NIS+ server running in NIS compat mode does not respond + * immediately to a NIS version2 call, the 4.X clients fail over to NIS + * version 1 and then never switches back to version 2. As the rpc.nisd + * only supports NIS V2 protocol the client process will hang even when + * rpc.nisd comes up later. + * + * The fix (implemented here) is to provide support for NULL, DOMAIN, and + * DOMAIN_NOACK calls for version 1 YP (in the NIS compatibility case). + * + * From the 4.X source: /src/413/lib/libc/yp/yp_match.c, + * if we provide support for above three procedures, _yp_dobind() returns + * with binding for version 1, but later (*dofunc) fails because + * that RPC procedure for version 1 is not supported. As a result, yp_unbind() + * is called and then __yp_dobind() is again called. This time around, + * version 2 is tried first and that succeeds (because now hopefully, the NIS+ + * server is up and responding) and then voila! the (*dofunc) succeeds. + * + * Please note that there is no need to provide support for other YP version 1 + * procedures such as yp_match or yp_all. + */ + +#include <stdio.h> +#include <stdlib.h> /* getenv, exit */ +#include <sys/types.h> +#include <memory.h> +#include <stropts.h> +#include <syslog.h> +#include <rpcsvc/yp_prot.h> + +typedef char *domainname; + +extern int verbose; /* set by the -v switch */ + +/* + * NIS Version 1 (YP) Dispatch table + */ +static void *ypproc_null_1(); +extern int *ypproc_domain_svc(); +extern int *ypproc_domain_nonack_svc(); + +static bool_t +xdr_domainname(xdrs, objp) + register XDR *xdrs; + domainname *objp; +{ + register long *buf; + + if (!xdr_string(xdrs, objp, YPMAXDOMAIN)) + return (FALSE); + return (TRUE); +} + +void +ypprog_1(rqstp, transp) + struct svc_req *rqstp; + register SVCXPRT *transp; +{ + union { + domainname ypproc_domain_1_arg; + domainname ypproc_domain_nonack_1_arg; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + /* + * In the code that follows, we dispatch to the YP Version 2 + * calls for the DOMAIN and NONACK cases, since there's no + * reason to implement those here. + */ + switch (rqstp->rq_proc) { + case YPPROC_NULL: + xdr_argument = xdr_void; + xdr_result = xdr_void; + local = (char *(*)()) ypproc_null_1; + break; + + case YPPROC_DOMAIN: + xdr_argument = xdr_domainname; + xdr_result = xdr_bool; + local = (char *(*)()) ypproc_domain_svc; + break; + + case YPPROC_DOMAIN_NONACK: + xdr_argument = xdr_domainname; + xdr_result = xdr_bool; + local = (char *(*)()) ypproc_domain_nonack_svc; + break; + + default: + svcerr_noproc(transp); + return; + } + (void) memset((char *)&argument, 0, sizeof (argument)); + if (!svc_getargs(transp, xdr_argument, (caddr_t) &argument)) { + svcerr_decode(transp); + return; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, (caddr_t) &argument)) { + syslog(LOG_ERR, "ypserv_v1: unable to free arguments"); + exit(1); + } +} + +static void * +ypproc_null_1() +{ + static char dummy; + + return ((void *) &dummy); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/Makefile b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/Makefile new file mode 100644 index 0000000000..67fe4198b3 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/Makefile @@ -0,0 +1,80 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# Makefile for nispasswd daemon +# +# cmd/rpcsvc/nis/rpc.nispasswdd/Makefile +# + +PROG = rpc.nispasswdd + +DERIVED_FILES = nispasswd_xdr.c + +SRCS = rpc.nispasswdd.c npd_svc.c npd_cache.c npd_svcsubr.c npd_ypfwd.c $(DERIVED_FILES) + +OBJS = $(SRCS:%.c=%.o) + +include $(SRC)/cmd/Makefile.cmd + +PROTOCOL_DIR = $(ROOT)/usr/include/rpcsvc +LDLIBS += -lnsl + +# rpcgen flags +RPCGENFLAGS= -c -C -M + +CPPFLAGS += -I$(SRC)/lib/libnsl/include -I$(SRC)/lib/nsswitch/nisplus/common \ + -DDEBUG -D_REENTRANT + +# +LINTFLAGS= -L$(SRC)/lib/libnsl -c +LINTOUT= lint.out + +.KEEP_STATE: + +all := TARGET= all +install := TARGET= install +clean := TARGET= clean +lint := TARGET= lint + +all: $(DERIVED_FILES) $(PROG) +install: all $(ROOTUSRSBINPROG) +clean: + $(RM) core $(DERIVED_FILES) $(OBJS) $(PROG) +lint: + $(LINT.c) $(SRCS) $(LDLIBS) > $(LINTOUT) 2>&1 + +# +# build rules +# +$(PROG): $(OBJS) + $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +$(DERIVED_FILES) : $(PROTOCOL_DIR)/nispasswd.x + $(RPCGEN) $(RPCGENFLAGS) $(PROTOCOL_DIR)/nispasswd.x > $(DERIVED_FILES) + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.c b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.c new file mode 100644 index 0000000000..5eabb9451e --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.c @@ -0,0 +1,215 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * npd_cache.c + * NPD cache routines + * + * Copyright (c) 1994-2001 Sun Microsystems, Inc. + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <syslog.h> +#include <string.h> +#include <stdlib.h> +#include <shadow.h> +#include <synch.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/nispasswd.h> +#include <ctype.h> +#include "npd_cache.h" + +extern NIS_HASH_TABLE upd_list; +extern long cache_time; +static int __nis_hash_key(nis_name name, NIS_HASH_TABLE *table); + +void +free_upd_item(upd) +struct update_item *upd; +{ + if (upd == NULL) + return; + + upd = (struct update_item *)nis_remove_item(upd->ul_item.name, + &upd_list); + if (upd == NULL) + return; + + free(upd->ul_item.name); + free(upd->ul_user); + free(upd->ul_domain); + (void) memset(upd->ul_oldpass, 0, sizeof (upd->ul_oldpass)); + free(upd->ul_oldpass); + free(upd); + upd = NULL; +} + +/* + * return 0 if an entry already exists + * return -1 if out of memory + * return 1 on successful addition + */ +int +add_upd_item(principal, user, sameuser, domain, ident, rval, key, pass) +nis_name principal; +char *user; +bool_t sameuser; +char *domain; +ulong_t ident; +ulong_t rval; +des_block *key; +char *pass; +{ + struct update_item *old = NULL, *tmp = NULL; + + if ((principal == NULL || *principal == '\0') || + (user == NULL || *user == '\0') || + (domain == NULL || *domain == '\0') || + (pass == NULL || *pass == '\0')) + return (0); + + old = (struct update_item *)nis_find_item(principal, &upd_list); + if (old != NULL) { + return (0); + } + + tmp = (struct update_item *)calloc(1, sizeof (struct update_item)); + if (tmp == NULL) + return (-1); + + tmp->ul_item.name = strdup(principal); + if (tmp->ul_item.name == NULL) { + free(tmp); + return (-1); + } + tmp->ul_user = strdup(user); + if (tmp->ul_user == NULL) { + free(tmp->ul_item.name); + free(tmp); + return (-1); + } + tmp->ul_domain = strdup(domain); + if (tmp->ul_domain == NULL) { + free(tmp->ul_user); + free(tmp->ul_item.name); + free(tmp); + return (-1); + } + tmp->ul_oldpass = strdup(pass); + if (tmp->ul_oldpass == NULL) { + free(tmp->ul_domain); + free(tmp->ul_user); + free(tmp->ul_item.name); + free(tmp); + return (-1); + } + tmp->ul_sameuser = sameuser; + tmp->ul_ident = ident; + tmp->ul_rval = rval; + tmp->ul_key = *key; + tmp->ul_attempt = 1; + tmp->ul_expire = time(NULL) + cache_time; + + return (nis_insert_item((NIS_HASH_ITEM *) tmp, &upd_list)); +} + +bool_t +find_upd_item(principal, upd) +nis_name principal; +struct update_item **upd; +{ + struct update_item *found = NULL; + + if (principal == NULL || *principal == '\0') + return (FALSE); + + found = (struct update_item *)nis_find_item(principal, &upd_list); + if (found == NULL) + return (FALSE); + *upd = found; + return (TRUE); +} + +int +__npd_hash_key(name) +nis_name name; +{ + int key = __nis_hash_key(name, &upd_list); + + return (key >= 0 ? key+1 : key); +} + +struct update_item * +__npd_item_by_key(key) +int key; +{ + int size; + + key -= 1; + size = sizeof (upd_list.keys) / sizeof (upd_list.keys[0]); + + if (key < 0 || key >= size) + return (NULL); + return ((struct update_item *)upd_list.keys[key]); +} + +/* the following macro improves performance */ +#define LOWER(c) (isupper((c)) ? _tolower((c)) : (c)) + +static int +__nis_hash_key(name, table) +nis_name name; +NIS_HASH_TABLE *table; +{ + int key = 0; + unsigned char *s; + + if ((name == NULL || *name == '\0') || table == NULL) + return (-1); + + for (s = (unsigned char *) name; *s != 0; s++) + key += LOWER(*s); + key %= (sizeof (table->keys) / sizeof (table->keys[0])); + + return (key); +} + +void +__npd_print_entry(prin) +char *prin; +{ + struct update_item *pi; + + if (find_upd_item(prin, &pi) == TRUE) { + syslog(LOG_ERR, "user=%s, sameuser=%d", + pi->ul_user, pi->ul_sameuser); + syslog(LOG_ERR, "ident=%ld, rval=%ld", + pi->ul_ident, pi->ul_rval); + syslog(LOG_ERR, "attempt=%d, domain=%s", + pi->ul_attempt, pi->ul_domain); + return; + } else { + syslog(LOG_ERR, "no entry found for %s", prin); + return; + } +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.h b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.h new file mode 100644 index 0000000000..c308c51540 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.h @@ -0,0 +1,69 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * npd_cache.h + * + * Copyright (c) 1994, 2001 Sun Microsystems Inc + * All Rights Reserved. + */ + +#ifndef _NPD_CACHE_H +#define _NPD_CACHE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <rpcsvc/nis.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * cache for useful information + */ +struct update_item { + NIS_HASH_ITEM ul_item; /* generic tag */ + bool_t ul_sameuser; /* changing their own passwd */ + char *ul_user; /* username */ + char *ul_domain; /* domainname */ + ulong_t ul_rval; /* random value */ + ulong_t ul_ident; /* identifier */ + des_block ul_key; /* session key */ + char *ul_oldpass; /* old clear passwd */ + int ul_attempt; /* failed attempts per session */ + ulong_t ul_expire; /* expiration time */ +}; + + +bool_t find_upd_item(nis_name principal, struct update_item **upd); +void free_upd_item(struct update_item *upd); +struct update_item *__npd_item_by_key(int key); +int add_upd_item(nis_name principal, char *user, bool_t sameuser, char *domain, + ulong_t ident, ulong_t rval, des_block *key, char *pass); +void __npd_print_entry(char *prin); +int __npd_hash_key(nis_name name); + +#ifdef __cplusplus +} +#endif + +#endif /* _NPD_CACHE_H */ diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svc.c b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svc.c new file mode 100644 index 0000000000..8650eb0570 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svc.c @@ -0,0 +1,1231 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 1994-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * npd_svc.c + * NPD service routines + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <syslog.h> +#include <string.h> +#include <ctype.h> +#include <shadow.h> +#include <crypt.h> +#include <stdlib.h> +#include <unistd.h> +#include <rpcsvc/yppasswd.h> +#include <rpcsvc/nis.h> +#include <rpc/key_prot.h> +#include <rpc/des_crypt.h> +#include <rpcsvc/nispasswd.h> +#include "npd_cache.h" +#include "npd_svcsubr.h" +#include <sys/byteorder.h> +#include <rpcsvc/nis_dhext.h> + +extern int max_attempts; +extern int cache_time; +extern int verbose; +extern int debug; + +#define _NPD_PASSMAXLEN 16 +#define MAX_RETRY 3 + +extern char *ypfwd; /* YP domain to fwd NIS+ req */ + +/* + * service routine for first part of the nispasswd update + * protocol. + */ +bool_t +nispasswd_authenticate_1_svc(argp, result, rqstp) +npd_request *argp; +nispasswd_authresult *result; +struct svc_req *rqstp; +{ + bool_t check_aging = TRUE; /* default == check */ + bool_t same_user = TRUE; /* default == same user */ + bool_t is_admin = FALSE; /* default == not an admin */ + bool_t entry_exp = FALSE; /* default == not expired */ + bool_t upd_entry = FALSE; /* default == do not update */ + bool_t refresh = FALSE; /* default == do not refresh */ + int ans = 0; + char prin[NIS_MAXNAMELEN]; + unsigned char xpass[_NPD_PASSMAXLEN]; + struct update_item *entry = NULL; + nis_result *pass_res; + nis_object *pobj; + des_block deskeys[3], ivec, cryptbuf; + int status; + char *oldpass; + unsigned long randval; + uint32_t rval, ident; + keylen_t pkeylen; /* public key length (bits) */ + algtype_t algtype; /* public key algorithm type */ + + if (verbose) + syslog(LOG_ERR, "received NIS+ auth request for %s", + argp->username); + + /* check if I'm running on the host == master(domain) */ + if (__nis_ismaster(nis_local_host(), argp->domain) == FALSE) { + syslog(LOG_ERR, "not master for %s", argp->domain); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = NPD_NOTMASTER; + return (TRUE); + } + +again: + /* get caller info. from auth_handle */ + if (rqstp) + (void) __nis_auth2princ_rpcgss(prin, rqstp, refresh, 0); + else + prin[0] = '\0'; + + if (verbose) + syslog(LOG_INFO, "_authenticate_: principal of req is %s", + prin); + + /* caller == admin ? ; Y -> skip checks, N -> do aging checks */ + if ((*prin != '\0') && strcmp(prin, "nobody") != 0) + /* authenticated user, check if they are privileged */ + if (__nis_isadmin(prin, "passwd", argp->domain) == TRUE) { + check_aging = FALSE; + is_admin = TRUE; + } + + if ((*prin == '\0') || (strcmp(prin, "nobody") == 0)) { + /* "." + null + "." = 3 */ + if ((strlen(argp->username) + strlen(argp->domain) + 3) > + (size_t)NIS_MAXNAMELEN) { + syslog(LOG_ERR, "buffer too small"); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = + NPD_BUFTOOSMALL; + return (TRUE); + } + (void) sprintf(prin, "%s.%s", argp->username, argp->domain); + if (prin[strlen(prin) - 1] != '.') + (void) strcat(prin, "."); + if (debug) + syslog(LOG_DEBUG, + "_authenticate_: 'nobody' or NUL prinicipal set to '%s'", prin); + } + + /* + * The credential information may have changed for the user, + * we should refresh the cache to make sure or the user is + * a different user and the necessary checks need to be made. + */ + if (strncmp(prin, argp->username, strlen(argp->username)) != 0) + if (refresh) + same_user = FALSE; + else { + refresh = TRUE; + goto again; + } + + /* check if there is a cached entry */ + if ((find_upd_item(prin, &entry)) && + (strcmp(entry->ul_user, argp->username) == 0)) { + + /* found an entry - check if it has expired */ + if (entry->ul_expire > time(NULL)) { + entry->ul_attempt = entry->ul_attempt + 1; + /* + * check if this attempt > max_attempts. + */ + if (entry->ul_attempt > max_attempts) { + syslog(LOG_ERR, + "too many failed attempts for %s", + argp->username); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = + NPD_PASSINVALID; + return (TRUE); + } + if (argp->ident == 0) { + /* + * a new session and we have an entry cached + * but we have not reached max_attempts, so + * just update entry with the new pass + */ + upd_entry = TRUE; + } + } else { /* entry has expired */ + (void) free_upd_item(entry); + entry_exp = TRUE; + entry = NULL; + } + } else { + entry = (struct update_item *)__npd_item_by_key(argp->ident); + if (entry == NULL) /* no cached entry */ + if (argp->ident != 0) { + syslog(LOG_ERR, + "no cache entry found for %s but the identifier is %d", + argp->username, argp->ident); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = + NPD_IDENTINVALID; + return (TRUE); + } + } + + /* get passwd info for username */ + pass_res = nis_getpwdent(argp->username, argp->domain); + + if (pass_res == NULL) { + syslog(LOG_ERR, "invalid args %s and %s", + argp->username, argp->domain); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = NPD_NOSUCHENTRY; + return (TRUE); + } + switch (pass_res->status) { + case NIS_SUCCESS: + pobj = NIS_RES_OBJECT(pass_res); + break; + case NIS_NOTFOUND: + syslog(LOG_ERR, "no passwd entry found for %s", + argp->username); + (void) nis_freeresult(pass_res); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = NPD_NOSUCHENTRY; + return (TRUE); + default: + syslog(LOG_ERR, + "NIS+ error (%d) getting passwd entry for %s", + pass_res->status, argp->username); + (void) nis_freeresult(pass_res); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = NPD_NISERROR; + return (TRUE); + } + + /* if user check if 'min' days have passed since 'lastchg' */ + if (check_aging) { + if ((__npd_has_aged(pobj, &ans) == FALSE) && + ans == NPD_NOTAGED) { + syslog(LOG_ERR, + "password has not aged enough for %s", + argp->username); + (void) nis_freeresult(pass_res); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = ans; + return (TRUE); + } + /* if ans == NPD_NOSHDWINFO then aging cannot be enforced */ + } + + /* + * Find out what key length and algorithm type we're dealing with. + */ + if (strncmp("DES", argp->key_type, 4) == 0) { + pkeylen = AUTH_DES_KEYLEN; + algtype = AUTH_DES_ALGTYPE; + } else { + /* + * This will check if the key_type exists in the + * NIS+ security cf. + */ + if (__nis_translate_mechanism(argp->key_type, &pkeylen, + &algtype) < 0) { + syslog(LOG_ERR, + "cannot get keylen/algtype for key type '%s'", + argp->key_type); + (void) nis_freeresult(pass_res); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = + NPD_INVALIDARGS; + return (TRUE); + } + } + + /* generate CK (from P.c and S.d) */ + if (key_get_conv_g((const char *)argp->user_pub_key.user_pub_key_val, + pkeylen, algtype, deskeys, + AUTH_DES_KEY(pkeylen, algtype) ? 1 : 3) != 0) { + syslog(LOG_ERR, + "cannot generate common DES key for %s", + argp->username); + syslog(LOG_ERR, "is keyserv still running ?"); + syslog(LOG_ERR, "has %s keylogged in ?", nis_local_host()); + (void) nis_freeresult(pass_res); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = NPD_CKGENFAILED; + return (TRUE); + } + /* decrypt the passwd sent */ + if (argp->npd_authpass.npd_authpass_len != _NPD_PASSMAXLEN) { + syslog(LOG_ERR, "password length wrong"); + (void) nis_freeresult(pass_res); + result->status = NPD_TRYAGAIN; + result->nispasswd_authresult_u.npd_err = NPD_PASSINVALID; + return (TRUE); + } + (void) memcpy(xpass, argp->npd_authpass.npd_authpass_val, + _NPD_PASSMAXLEN); + + ivec.key.high = ivec.key.low = 0; + if (AUTH_DES_KEY(pkeylen, algtype)) + status = cbc_crypt(deskeys[0].c, (char *)xpass, + _NPD_PASSMAXLEN, DES_DECRYPT | DES_HW, + (char *)&ivec); + else + status = __cbc_triple_crypt(deskeys, (char *)xpass, + _NPD_PASSMAXLEN, DES_DECRYPT | DES_HW, + (char *)&ivec); + + if (DES_FAILED(status)) { + syslog(LOG_ERR, "failed to decrypt password"); + (void) nis_freeresult(pass_res); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = NPD_DECRYPTFAIL; + return (TRUE); + } + + /* assign an ID and generate R on the first call of a session */ + if (argp->ident == 0) { + ident = (uint32_t)__npd_hash_key(prin); + if ((int)ident == -1) { + syslog(LOG_ERR, "invalid ident value calculated"); + (void) nis_freeresult(pass_res); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = NPD_SYSTEMERR; + return (TRUE); + } + (void) __npd_gen_rval(&randval); + } else { + /* second or third attempt */ + ident = argp->ident; + if (entry == NULL) { + if (entry_exp) /* gen a new random val */ + (void) __npd_gen_rval(&randval); + else { + syslog(LOG_ERR, "cache corrupted"); + (void) nis_freeresult(pass_res); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = + NPD_SYSTEMERR; + return (TRUE); + } + } else { + if (strcmp(entry->ul_user, argp->username) != 0) { + /* gen a new random val */ + (void) __npd_gen_rval(&randval); + } else + randval = entry->ul_rval; + } + } + + rval = (uint32_t)randval; + if (! __npd_ecb_crypt(&ident, &rval, &cryptbuf, + sizeof (des_block), DES_ENCRYPT, &deskeys[0])) { + syslog(LOG_ERR, "failed to encrypt verifier"); + (void) nis_freeresult(pass_res); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = NPD_ENCRYPTFAIL; + return (TRUE); + } + /* encrypt the passwd and compare with that stored in NIS+ */ + if (same_user) { + if ((oldpass = ENTRY_VAL(pobj, 1)) == NULL) { + (void) nis_freeresult(pass_res); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = NPD_NOPASSWD; + return (TRUE); + } + if (strcmp(crypt((const char *)xpass, + (const char *)oldpass), oldpass) != 0) { + if (debug) + syslog(LOG_DEBUG, + "_authenticate_: pw decrypt failed"); + (void) nis_freeresult(pass_res); + result->nispasswd_authresult_u.npd_verf.npd_xid = + htonl(cryptbuf.key.high); + result->nispasswd_authresult_u.npd_verf.npd_xrandval = + htonl(cryptbuf.key.low); + /* cache relevant info */ + if (entry == NULL) { + ans = add_upd_item(prin, argp->username, + same_user, argp->domain, ident, rval, + &deskeys[0], (char *)xpass); + if (ans <= 0) { + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = + NPD_SYSTEMERR; + } else + result->status = NPD_TRYAGAIN; + } else { + /* found an entry, attempt == max_attempts */ + if (entry->ul_attempt == max_attempts) { + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = + NPD_SYSTEMERR; + /* + * not really a system error but we + * want the caller to think that 'cos + * they are obviously trying to break-in. + * Perhaps, we should not respond at all, + * the client side would timeout. + */ + } + } + if (verbose) + (void) __npd_print_entry(prin); + return (TRUE); + } + } else { + if (is_admin == FALSE) { /* not privileged */ + (void) nis_freeresult(pass_res); + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = + NPD_PERMDENIED; + return (TRUE); + } + /* admin changing another users password */ + if (__authenticate_admin(prin, (char *)xpass) == FALSE) { + /* + * we have no idea where this admin's + * passwd record is stored BUT we do know + * where their PK cred(s) is stored from + * the netname, so lets try to decrypt + * the secret key(s) with the passwd that + * was sent across. + */ + (void) nis_freeresult(pass_res); + result->status = NPD_TRYAGAIN; + result->nispasswd_authresult_u.npd_verf.npd_xid = + htonl(cryptbuf.key.high); + result->nispasswd_authresult_u.npd_verf.npd_xrandval = + htonl(cryptbuf.key.low); + /* cache relevant info */ + if (entry == NULL) { + ans = add_upd_item(prin, argp->username, + same_user, argp->domain, ident, rval, + &deskeys[0], (char *)xpass); + if (ans <= 0) { + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = + NPD_SYSTEMERR; + } + } + return (TRUE); + } + } + /* done with pass_res */ + (void) nis_freeresult(pass_res); + /* cache relevant info */ + if (entry == NULL) { + ans = add_upd_item(prin, argp->username, same_user, + argp->domain, ident, rval, &deskeys[0], + (char *)xpass); + if (ans <= 0) { + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = + NPD_SYSTEMERR; + return (TRUE); + } + } else { + if (entry->ul_oldpass != NULL) + free(entry->ul_oldpass); + entry->ul_oldpass = strdup((char *)&xpass[0]); + if (entry->ul_oldpass == NULL) { + result->status = NPD_FAILED; + result->nispasswd_authresult_u.npd_err = + NPD_SYSTEMERR; + return (TRUE); + } + if (upd_entry == TRUE) { + entry->ul_ident = ident; + entry->ul_rval = rval; + entry->ul_key = deskeys[0]; + } + } + result->status = NPD_SUCCESS; + result->nispasswd_authresult_u.npd_verf.npd_xid = + htonl(cryptbuf.key.high); + result->nispasswd_authresult_u.npd_verf.npd_xrandval = + htonl(cryptbuf.key.low); + if (verbose) + (void) __npd_print_entry(prin); + return (TRUE); +} + +/* + * service routine for second part of the nispasswd update + * protocol. + */ +/* ARGSUSED2 */ +bool_t +nispasswd_update_1_svc(updreq, res, rqstp) +npd_update *updreq; +nispasswd_updresult *res; +struct svc_req *rqstp; +{ + struct update_item *entry; + npd_newpass cryptbuf; + passbuf pass; + char *newpass, buf[NIS_MAXNAMELEN]; + uint32_t rand; + struct nis_result *pass_res = NULL, *mod_res = NULL; + struct nis_object *pobj = NULL, *eobj = NULL; + entry_col ecol[8]; + char *old_gecos, *old_shell, *sp; + char shadow[80]; + static nispasswd_error errlist[3]; + int error = NPD_SUCCESS; + register int i; + char *old_pass; + entry_col * eobj_col; + uint_t eobj_col_len; + + /* set to success, and reset to error when warranted */ + res->status = NPD_SUCCESS; + + entry = (struct update_item *)__npd_item_by_key(updreq->ident); + if (entry == NULL) { + syslog(LOG_ERR, "invalid identifier: %ld", updreq->ident); + res->status = NPD_FAILED; + res->nispasswd_updresult_u.npd_err = NPD_IDENTINVALID; + return (TRUE); + } + + if (verbose) { + syslog(LOG_DEBUG, "received NIS+ passwd update request for %s", + entry->ul_user); + } + + /* + * iterate thru entry list and decrypt R sent and new passwd until + * we have a rand hit (or reach end of list) + */ + for (; entry != NULL; + entry = (struct update_item *)entry->ul_item.next) { + + /* decrypt R and new passwd */ + cryptbuf.npd_xrandval = ntohl(updreq->xnewpass.npd_xrandval); + for (i = 0; i < __NPD_MAXPASSBYTES; i++) + cryptbuf.pass[i] = updreq->xnewpass.pass[i]; + + if (! __npd_cbc_crypt(&rand, pass, __NPD_MAXPASSBYTES, + &cryptbuf, _NPD_PASSMAXLEN, DES_DECRYPT, + &entry->ul_key)) { + syslog(LOG_ERR, "failed to decrypt verifier"); + res->status = NPD_FAILED; + res->nispasswd_updresult_u.npd_err = NPD_DECRYPTFAIL; + return (TRUE); + } + + /* check if R sent/decrypted matches cached R */ + if (rand == entry->ul_rval) { + if (verbose) + syslog(LOG_DEBUG, "rand hit (%ld) for user '%s'", + rand, entry->ul_user); + break; + } else + if (verbose) + syslog(LOG_ERR, + "rand miss: entry rval=%ld; continue...", + entry->ul_rval); + } + + if (entry == NULL) { + if (verbose) + syslog(LOG_ERR, "update failed; no rand matches"); + res->status = NPD_FAILED; + res->nispasswd_updresult_u.npd_err = NPD_VERFINVALID; + return (TRUE); + } + + /* create passwd struct with this pass & gecos/shell */ + pass_res = nis_getpwdent(entry->ul_user, entry->ul_domain); + + /* encrypt new passwd */ + if (!(newpass = __npd_encryptpass(pass, NIS_RES_OBJECT(pass_res)))) { + syslog(LOG_ERR, "password encryption failed"); + res->status = NPD_FAILED; + res->nispasswd_updresult_u.npd_err = NPD_ENCRYPTFAIL; + return (TRUE); + } + + if (pass_res == NULL) { + syslog(LOG_ERR, "invalid args %s and %s", + entry->ul_user, entry->ul_domain); + res->status = NPD_FAILED; + res->nispasswd_updresult_u.npd_err = NPD_NOSUCHENTRY; + return (TRUE); + } + + switch (pass_res->status) { + case NIS_SUCCESS: + pobj = NIS_RES_OBJECT(pass_res); + break; + case NIS_NOTFOUND: + syslog(LOG_ERR, "no passwd entry found for %s", + entry->ul_user); + (void) nis_freeresult(pass_res); + res->status = NPD_FAILED; + res->nispasswd_updresult_u.npd_err = NPD_NOSUCHENTRY; + return (TRUE); + default: + syslog(LOG_ERR, + "NIS+ error (%d) getting passwd entry for %s", + pass_res->status, entry->ul_user); + (void) nis_freeresult(pass_res); + res->status = NPD_FAILED; + res->nispasswd_updresult_u.npd_err = NPD_NISERROR; + return (TRUE); + } + + old_pass = ENTRY_VAL(pobj, 1); + old_gecos = ENTRY_VAL(pobj, 4); + old_shell = ENTRY_VAL(pobj, 6); + + /* can change passwd, shell or gecos */ + (void) memset(ecol, 0, sizeof (ecol)); + ecol[1].ec_value.ec_value_val = newpass; + ecol[1].ec_value.ec_value_len = strlen(newpass) + 1; + ecol[1].ec_flags = EN_CRYPT|EN_MODIFIED; + + /* clear out the error list */ + (void) memset(errlist, 0, sizeof (errlist)); + + /* if a gecos field is provided... */ + if (*updreq->pass_info.pw_gecos != '\0') { + if (__npd_can_do(NIS_MODIFY_ACC, pobj, + entry->ul_item.name, 4) == FALSE) { + syslog(LOG_NOTICE, + "insufficient permission for %s to change the gecos", + entry->ul_user); + res->status = NPD_PARTIALSUCCESS; + errlist[0].npd_field = NPD_GECOS; + errlist[0].npd_code = NPD_PERMDENIED; + errlist[0].next = NULL; + } else if (old_gecos == NULL || + strcmp(updreq->pass_info.pw_gecos, old_gecos) != 0) { + + ecol[4].ec_value.ec_value_val = + updreq->pass_info.pw_gecos; + ecol[4].ec_value.ec_value_len = + strlen(updreq->pass_info.pw_gecos) + 1; + ecol[4].ec_flags = EN_MODIFIED; + } + } + + /* if a shell field is provided... */ + if (*updreq->pass_info.pw_shell != '\0') { + if (__npd_can_do(NIS_MODIFY_ACC, pobj, + entry->ul_item.name, 6) == FALSE) { + syslog(LOG_NOTICE, + "insufficient permission for %s to change the shell", + entry->ul_user); + + /* + * If already set to partial success, that means + * that gecos field and error was provided, so + * add the next item to the error list. + */ + if (res->status == NPD_PARTIALSUCCESS) { + errlist[0].next = &errlist[1]; + errlist[1].npd_field = NPD_SHELL; + errlist[1].npd_code = NPD_PERMDENIED; + errlist[1].next = NULL; + } else { + res->status = NPD_PARTIALSUCCESS; + errlist[0].npd_field = NPD_SHELL; + errlist[0].npd_code = NPD_PERMDENIED; + errlist[0].next = NULL; + } + } else if (old_shell == NULL || + strcmp(updreq->pass_info.pw_shell, old_shell) != 0) { + ecol[6].ec_value.ec_value_val = + updreq->pass_info.pw_shell; + ecol[6].ec_value.ec_value_len = + strlen(updreq->pass_info.pw_shell) + 1; + ecol[6].ec_flags = EN_MODIFIED; + } + } + + /* update lstchg field in the shadow area */ + sp = ENTRY_VAL(pobj, 7); + if (sp != NULL) { + if ((sp = strchr(ENTRY_VAL(pobj, 7), ':')) == NULL) { + syslog(LOG_ERR, "shadow column corrupted: user %s", + entry->ul_user); + (void) nis_freeresult(pass_res); + res->status = NPD_FAILED; + res->nispasswd_updresult_u.npd_err = NPD_SHDWCORRUPT; + goto end; + } + (void) sprintf(shadow, "%d%s", (int)DAY_NOW, sp); + ecol[7].ec_value.ec_value_val = shadow; + ecol[7].ec_value.ec_value_len = strlen(shadow) + 1; + ecol[7].ec_flags = EN_CRYPT|EN_MODIFIED; + } + + /* clone an entry object to update passwd entry */ + eobj = nis_clone_object(pobj, NULL); + if (eobj == NULL) { + syslog(LOG_CRIT, "out of memory"); + res->status = NPD_FAILED; + res->nispasswd_updresult_u.npd_err = NPD_SYSTEMERR; + goto end; + } + + /* save the old values for restoring before freeing the object */ + eobj_col = eobj->EN_data.en_cols.en_cols_val; + eobj_col_len = eobj->EN_data.en_cols.en_cols_len; + + /* set column value to entry column */ + eobj->EN_data.en_cols.en_cols_val = ecol; + eobj->EN_data.en_cols.en_cols_len = 8; + + /* strlen("[name=],passwd.") + null + "." = 17 */ + if ((strlen(entry->ul_user) + strlen(pobj->zo_domain) + 17) > + (size_t)NIS_MAXNAMELEN) { + syslog(LOG_ERR, "not enough buffer space"); + res->status = NPD_FAILED; + res->nispasswd_updresult_u.npd_err = NPD_BUFTOOSMALL; + goto end; + } + + /* put together table index info and object modification */ + (void) sprintf(buf, "[name=%s],passwd.%s", entry->ul_user, + pobj->zo_domain); + + /* add dot "." if necessary */ + if (buf[strlen(buf) - 1] != '.') + (void) strcat(buf, "."); + + /* update NIS+ passwd table */ + mod_res = nis_modify_entry(buf, eobj, 0); + + /* if NIS+ update fails, bail now */ + if (mod_res->status != NIS_SUCCESS) { + syslog(LOG_DEBUG, "could not update NIS+ passwd table"); + res->status = NPD_FAILED; + res->nispasswd_updresult_u.npd_err = NPD_NISERROR; + goto end; + } + + /* NIS+ master updated; if YP-forwarding turned on, do YP */ + if (ypfwd) { + + int try = 0; /* retry counter for YP & NIS+ updates */ + + /* + * Attempt the YP passwd map update; + * on failures use exponential backoff + */ + while ((update_authtok_nis_fwd(entry->ul_user, newpass, + entry->ul_oldpass, ypfwd, old_gecos, old_shell) + == -1) && (try < MAX_RETRY)) { + (void) sleep((unsigned)(1<<try)); + try++; + } + + /* + * On repeated failures (MAX_RETRY), + * give up & undo NIS+ table update + */ + if (try == MAX_RETRY) { + try = 0; /* clear for undo attempts */ + + syslog(LOG_ERR, + "unable to update NIS(YP) passwd \ +entry on %s for %s", ypfwd, entry->ul_user); + + /* + * The passwd tbl update failed, so set 'error' + * to an NPD failure. Here is where it gets + * tricky. The permissions on the passwd table + * must be turned off because the failover mech- + * anism will try to get NISD to make the update. + */ + error = res->status = NPD_FAILED; + res->nispasswd_updresult_u.npd_err = NPD_SYSTEMERR; + + /* set "new" passwd back to old password */ + ecol[1].ec_value.ec_value_val = old_pass; + ecol[1].ec_value.ec_value_len = strlen(old_pass) + 1; + + /* undo the NIS+ passwd table update */ + do { + + /* exponential backoff each try */ + if (try) (void) sleep((unsigned)(1<<try)); + + /* + * Update table with old passwd information, + * freeing the old 'mod_res' struct first. + * 'eobj' has already been set to 'ecol' above. + */ + if (mod_res) (void) nis_freeresult(mod_res); + mod_res = nis_modify_entry(buf, eobj, 0); + + /* On success, exit; otherwise increment ctr */ + if (mod_res->status == NIS_SUCCESS) break; + else try++; + + } while (try < MAX_RETRY); + + /* NIS+ update repeated failures, bail now and log */ + if (try == MAX_RETRY) { + syslog(LOG_ERR, "WARNING: unable to undo NIS+ \ +passwd update; maybe out-of-sync with YP map -- verify by hand"); + } + } + } + + /* + * The 'error' flag can only be changed from NPD_SUCCESS if + * YP updating was on and then, only in a failure scenario. + * In all other situations, update the credential! + */ + if (error == NPD_SUCCESS) { + + /* attempt to update PK cred(s) */ + (void) __npd_upd_all_pk_creds(entry->ul_user, entry->ul_domain, + entry->ul_oldpass, pass, &error); + if (error != NIS_SUCCESS) { + if (res->status == NPD_PARTIALSUCCESS) { + if (errlist[0].next == NULL) { + errlist[0].next = &errlist[1]; + errlist[1].npd_field = NPD_SECRETKEY; + errlist[1].npd_code = error; + errlist[1].next = NULL; + } else if (errlist[1].next == NULL) { + errlist[1].next = &errlist[2]; + errlist[2].npd_field = NPD_SECRETKEY; + errlist[2].npd_code = error; + errlist[2].next = NULL; + } + } else { + res->status = NPD_PARTIALSUCCESS; + errlist[0].npd_field = NPD_SECRETKEY; + errlist[0].npd_code = error; + errlist[0].next = NULL; + } + + /* + * Only set the reason union member if partial success; + * otherwise may wipe out npd_err union member. + */ + if (res->status == NPD_PARTIALSUCCESS) + res->nispasswd_updresult_u.reason = errlist[0]; + } + } + + /* Epilogue just consists of freeing up data */ +end: + /* Restore column stuff so that we can free eobj */ + if (eobj) { + eobj->EN_data.en_cols.en_cols_val = eobj_col; + eobj->EN_data.en_cols.en_cols_len = eobj_col_len; + (void) nis_destroy_object(eobj); + } + + /* + * The code to free pobj is not necessary as in + * yppasswdproc_update_1_svc() because pobj is + * freed when pass_res is just below. Otherwise + * if it's in here, it will dump core. + */ + if (mod_res) (void) nis_freeresult(mod_res); + if (pass_res) (void) nis_freeresult(pass_res); + if (entry) (void) free_upd_item(entry); + return (TRUE); +} + +/* + * yppasswd update service routine. + * The error codes returned are from the 4.x rpc.yppasswdd.c, + * it seems that the client side only checks if the result is + * non-zero in which case it prints a generic message ! + */ +/* ARGSUSED2 */ +bool_t +yppasswdproc_update_1_svc(yppass, result, rqstp) +struct yppasswd *yppass; +int *result; +struct svc_req *rqstp; +{ + char buf[NIS_MAXNAMELEN], *dom; + struct passwd *newpass; + struct nis_result *mod_res = NULL; + struct nis_object *pobj = NULL, *eobj = NULL; + entry_col ecol[8]; + char shadow[80], *sp, *p; + char *old_gecos, *old_shell, *old_pass; + nis_server *srv; + nis_tag tags[2], *tagres; + int status, ans; + entry_col * eobj_col; + uint_t eobj_col_len; + + /* set new password from YP passwd struct */ + newpass = &yppass->newpw; + + if (verbose) + syslog(LOG_ERR, + "received yp password update request from %s", + newpass->pw_name); + + /* fill-in NIS+ server information */ + srv = (nis_server *)__nis_host2nis_server(NULL, 0, &ans); + if (srv == NULL) { + syslog(LOG_ERR, "no host/addr information: %d", ans); + *result = -1; + return (TRUE); + } + + /* + * make the nis_stats call to check if the server is running + * in compat mode and get the list of directories this server + * is serving + */ + tags[0].tag_type = TAG_NISCOMPAT; + tags[0].tag_val = ""; + tags[1].tag_type = TAG_DIRLIST; + tags[1].tag_val = ""; + + status = nis_stats(srv, tags, 2, &tagres); + __free_nis_server(srv); + if (status != NIS_SUCCESS) { + syslog(LOG_ERR, "nis_error: %d", status); + *result = -1; + return (1); + } + + if ((strcmp(tagres[0].tag_val, "<Unknown Statistics>") == 0) || + (strcmp(tagres[1].tag_val, "<Unknown Statistics>") == 0)) { + /* old server */ + syslog(LOG_ERR, + "NIS+ server does not support the new statistics tags"); + nis_freetags(tagres, 2); + *result = -1; + return (1); + } + + /* check if server is running in NIS compat mode */ + if (strcasecmp(tagres[0].tag_val, "OFF") == 0) { + syslog(LOG_ERR, + "Local NIS+ server is not running in NIS compat mode"); + *result = -1; + nis_freetags(tagres, 2); + return (1); + } + /* + * find the dir that has a passwd entry for this user + * POLICY: if user has a passwd stored in more then one + * dir then do not make an update. if user has an entry + * in only one dir, then make an update in that dir. + */ + + if (! __npd_find_obj(newpass->pw_name, tagres[1].tag_val, &pobj)) { + *result = -1; + nis_freetags(tagres, 2); + return (1); + } + nis_freetags(tagres, 2); + + old_pass = ENTRY_VAL(pobj, 1); + old_gecos = ENTRY_VAL(pobj, 4); + old_shell = ENTRY_VAL(pobj, 6); + + if (!__npd_has_aged(pobj, &ans) && ans == NPD_NOTAGED) { + syslog(LOG_ERR, "password has not aged enough for %s", + newpass->pw_name); + *result = -1; + goto ypend; + } + /* if ans == NPD_NOSHDWINFO then aging cannot be enforced */ + + /* validate the old passwd */ + if (old_pass == NULL) { + syslog(LOG_ERR, "no passwd found for %s", newpass->pw_name); + *result = 7; /* password incorrect */ + goto ypend; + } + + /* mismatch on old passwd */ + if (strcmp(crypt((const char *)yppass->oldpass, + (const char *)old_pass), old_pass) != 0) { + syslog(LOG_ERR, "incorrect passwd for %s", newpass->pw_name); + *result = 7; + goto ypend; + } + + /* can change passwd, gecos or shell */ + (void) memset(ecol, 0, sizeof (ecol)); + + if (strcmp(old_pass, newpass->pw_passwd) != 0) { + ecol[1].ec_value.ec_value_val = newpass->pw_passwd; + ecol[1].ec_value.ec_value_len = + strlen(newpass->pw_passwd) + 1; + ecol[1].ec_flags = EN_CRYPT|EN_MODIFIED; + } + + if (strcmp(nis_leaf_of(pobj->zo_domain), "org_dir") == 0) { + /* need to strip org_dir part of the domain */ + dom = strchr(pobj->zo_domain, '.'); + if (dom != NULL) + dom++; + } else + dom = pobj->zo_domain; + + /* "." + null + "." = 3 */ + if ((strlen(newpass->pw_name) + strlen(dom) + 3) > + (size_t)NIS_MAXNAMELEN) { + syslog(LOG_ERR, "not enough buffer space"); + *result = -1; + goto ypend; + } + (void) sprintf(buf, "%s.%s", newpass->pw_name, dom); + if (buf[strlen(buf) - 1] != '.') + (void) strcat(buf, "."); + + if (newpass->pw_gecos != NULL && + (old_gecos == NULL || + strcmp(old_gecos, newpass->pw_gecos) != 0)) { + + if (__npd_can_do(NIS_MODIFY_ACC, pobj, buf, 4) == FALSE) { + syslog(LOG_NOTICE, + "insufficient permission for %s to change the gecos", + newpass->pw_name); + *result = 2; + goto ypend; + } + ecol[4].ec_value.ec_value_val = newpass->pw_gecos; + ecol[4].ec_value.ec_value_len = + strlen(newpass->pw_gecos) + 1; + ecol[4].ec_flags = EN_MODIFIED; + } + + if (newpass->pw_shell != NULL && + (old_shell == NULL || + strcmp(old_shell, newpass->pw_shell) != 0)) { + + if (! __npd_can_do(NIS_MODIFY_ACC, pobj, buf, 6)) { + syslog(LOG_NOTICE, + "insufficient permission for %s to change the shell", + newpass->pw_name); + *result = 2; + goto ypend; + } + ecol[6].ec_value.ec_value_val = newpass->pw_shell; + ecol[6].ec_value.ec_value_len = + strlen(newpass->pw_shell) + 1; + ecol[6].ec_flags = EN_MODIFIED; + } + /* + * from 4.x: + * This fixes a really bogus security hole, basically anyone can + * call the rpc passwd daemon, give them their own passwd and a + * new one that consists of ':0:0:Im root now:/:/bin/csh^J' and + * give themselves root access. With this code it will simply make + * it impossible for them to login again, and as a bonus leave + * a cookie for the always vigilant system administrator to ferret + * them out. + */ + + for (p = newpass->pw_name; (*p != '\0'); p++) + if ((*p == ':') || !(isprint(*p))) + *p = '$'; /* you lose ! */ + for (p = newpass->pw_passwd; (*p != '\0'); p++) + if ((*p == ':') || !(isprint(*p))) + *p = '$'; /* you lose ! */ + + /* update lstchg field */ + sp = ENTRY_VAL(pobj, 7); + if (sp != NULL) { + if ((sp = strchr(sp, ':')) == NULL) { + syslog(LOG_ERR, "shadow column corrupted: user %s", + newpass->pw_name); + *result = -1; + goto ypend; + } + (void) sprintf(shadow, "%d%s", (int)DAY_NOW, sp); + ecol[7].ec_value.ec_value_val = shadow; + ecol[7].ec_value.ec_value_len = strlen(shadow) + 1; + ecol[7].ec_flags = EN_CRYPT|EN_MODIFIED; + } + + /* clone an entry object to update passwd entry */ + eobj = nis_clone_object(pobj, NULL); + if (eobj == NULL) { + syslog(LOG_CRIT, "out of memory"); + *result = -1; + goto ypend; + } + + /* save the old values to restore while freeing the object */ + eobj_col = eobj->EN_data.en_cols.en_cols_val; + eobj_col_len = eobj->EN_data.en_cols.en_cols_len; + + /* set column value to entry column */ + eobj->EN_data.en_cols.en_cols_val = ecol; + eobj->EN_data.en_cols.en_cols_len = 8; + + /* strlen("[name=],passwd.") + null + "." = 17 */ + if ((strlen(newpass->pw_name) + strlen(pobj->zo_domain) + 17) > + (size_t)NIS_MAXNAMELEN) { + syslog(LOG_ERR, "not enough buffer space"); + *result = -1; + goto ypend; + } + + /* put together table index info and object modification */ + (void) sprintf(buf, "[name=%s],passwd.%s", newpass->pw_name, + pobj->zo_domain); + + /* add dot "." if necessary */ + if (buf[strlen(buf) - 1] != '.') + (void) strcat(buf, "."); + + /* update NIS+ passwd table */ + mod_res = nis_modify_entry(buf, eobj, 0); + + /* nisd in temp read-only mode (nisbackup(1M)) */ + if (mod_res->status == NIS_TRYAGAIN) { + syslog(LOG_INFO, + "could not update NIS+ passwd: %s", + nis_sperrno(mod_res->status)); + /* + * 8 is the magic number from way back in 4.1.x. + * User should see + * "Password file/table busy. Try again later." + */ + *result = 8; + goto ypend; + } + + + /* if NIS+ update fails, bail now */ + if (mod_res->status != NIS_SUCCESS) { + syslog(LOG_ERR, + "could not update NIS+ passwd: %s", + nis_sperrno(mod_res->status)); + *result = 13; + goto ypend; + } + + /* + * nothing happens here because we cannot re-encrypt the credential + * because we do not have the unencrypted new password .... :^( + */ + *result = 0; + + /* NIS+ master updated; if YP-forwarding turned on, do YP */ + if (ypfwd) { + + int try = 0; /* retry counter for YP & NIS+ updates */ + + /* + * Attempt the YP passwd map update; + * on failures use exponential backoff + */ + while ((update_authtok_nis_fwd(newpass->pw_name, + newpass->pw_passwd, yppass->oldpass, ypfwd, + old_gecos, old_shell) == -1) && + (try < MAX_RETRY)) { + (void) sleep((unsigned)(1<<try)); + try++; + } + + /* + * On repeated failures (MAX_RETRY), + * give up & undo NIS+ table update + */ + if (try == MAX_RETRY) { + try = 0; /* clear for undo attempts */ + syslog(LOG_ERR, + "unable to update NIS(YP) passwd \ +entry on %s for %s", ypfwd, newpass->pw_name); + *result = -1; + + /* set "new" passwd back to old password */ + ecol[1].ec_value.ec_value_val = old_pass; + ecol[1].ec_value.ec_value_len = strlen(old_pass) + 1; + + /* undo the NIS+ passwd table update */ + do { + /* exponential backoff each try */ + if (try) (void) sleep((unsigned)(1<<try)); + + /* + * Update table with old passwd information, + * freeing the old 'mod_res' struct first. + * 'eobj' has already been set to 'ecol' above. + */ + if (mod_res) (void) nis_freeresult(mod_res); + mod_res = nis_modify_entry(buf, eobj, 0); + + /* On success, exit; otherwise increment ctr */ + if (mod_res->status == NIS_SUCCESS) break; + else try++; + + } while (try < MAX_RETRY); + + /* NIS+ update repeated failures, bail now and log */ + if (try == MAX_RETRY) { + syslog(LOG_ERR, "WARNING: unable to undo NIS+ \ +passwd update; maybe out-of-sync with YP map -- verify by hand"); + } + } + } + + /* the epilogue just consists of freeing up data */ +ypend: + /* Restore column stuff so that we can free eobj/pobj */ + if (eobj) { + eobj->EN_data.en_cols.en_cols_val = eobj_col; + eobj->EN_data.en_cols.en_cols_len = eobj_col_len; + (void) nis_destroy_object(eobj); + } + if (pobj) + (void) nis_destroy_object(pobj); + if (mod_res) (void) nis_freeresult(mod_res); + return (1); +} + +/* ARGSUSED */ +int +nispasswd_prog_1_freeresult(transp, xdr_result, result) +SVCXPRT *transp; +xdrproc_t xdr_result; +caddr_t result; +{ + + /* + * (void) xdr_free(xdr_result, result); + * Insert additional freeing code here, if needed + */ + + return (1); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.c b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.c new file mode 100644 index 0000000000..b8296528a2 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.c @@ -0,0 +1,1377 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 1994-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * npd_svcsubr.c + * Contains the sub-routines required by the server. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <syslog.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <memory.h> +#include <shadow.h> +#include <crypt.h> +#include <rpc/rpc.h> +#include <rpc/xdr.h> +#include <rpc/key_prot.h> +#include <rpc/des_crypt.h> +#include <mp.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/nispasswd.h> +#include <rpcsvc/nis_dhext.h> +#include <unistd.h> +#include <pwd.h> +#include <limits.h> +#include "npd_svcsubr.h" +#include "nisplus_tables.h" + +extern int verbose; +extern bool_t debug; +extern bool_t generatekeys; + +bool_t __npd_prin2netname(char *, char []); +static struct passwd *nis_object2passwd(nis_object *obj); + +/* + * get the password information for this user + * the result should be freed using nis_freeresult() + */ +struct nis_result * +nis_getpwdent(user, domain) +char *user; +char *domain; +{ + char buf[NIS_MAXNAMELEN]; + + if ((user == NULL || *user == '\0') || + (domain == NULL || *domain == '\0')) + return (NULL); + + /* strlen("[name=],passwd.org_dir.") + null + "." = 25 */ + if ((25 + strlen(user) + strlen(domain)) > + (size_t)NIS_MAXNAMELEN) + return (NULL); + + (void) sprintf(buf, "[name=%s],passwd.org_dir.%s", user, domain); + if (buf[strlen(buf) - 1] != '.') + (void) strcat(buf, "."); + + return (nis_list(buf, USE_DGRAM+MASTER_ONLY, NULL, NULL)); +} + +/* + * get the credential information for this user + * the result should be freed using nis_freeresult() + */ +static struct nis_result * +nis_getcredent(principal, domain, auth_type) +char *principal; +char *domain; +char *auth_type; +{ + char buf[NIS_MAXNAMELEN]; + + if ((principal == NULL || *principal == '\0') || + (domain == NULL || *domain == '\0') || auth_type == NULL) + return (NULL); + + /* strlen("[cname=,auth_type=],cred.org_dir.") + null + "." = 35 */ + if ((35 + strlen(principal) + strlen(domain)) > + (size_t)NIS_MAXNAMELEN) + return (NULL); + + (void) sprintf(buf, "[cname=%s,auth_type=%s],cred.org_dir.%s", + principal, auth_type, domain); + + if (buf[strlen(buf) - 1] != '.') + (void) strcat(buf, "."); + + return (nis_list(buf, USE_DGRAM+MASTER_ONLY, NULL, NULL)); +} + +static bool_t +__npd_fill_spwd(obj, sp) +struct nis_object *obj; +struct spwd *sp; +{ + long val; + char *p = NULL, *ageinfo = NULL; + char *ep, *end; + + /* defaults */ + sp->sp_lstchg = -1; + sp->sp_min = -1; + sp->sp_max = -1; + sp->sp_warn = -1; + sp->sp_inact = -1; + sp->sp_expire = -1; + sp->sp_flag = 0; + + /* shadow information is in column 7 */ + if ((p = ENTRY_VAL(obj, 7)) == NULL) + return (FALSE); + if ((ageinfo = strdup(p)) == NULL) + return (FALSE); + + p = ageinfo; + end = ageinfo + ENTRY_LEN(obj, 7); + + /* format is: lstchg:min:max:warn:inact:expire:flag */ + + val = strtol(p, &ep, 10); /* base = 10 */ + if (*ep != ':' || ep >= end) { + (void) free(ageinfo); + return (FALSE); + } + if (ep != p) + sp->sp_lstchg = val; + p = ep + 1; + + val = strtol(p, &ep, 10); + if (*ep != ':' || ep >= end) { + (void) free(ageinfo); + return (FALSE); + } + if (ep != p) + sp->sp_min = val; + p = ep + 1; + + val = strtol(p, &ep, 10); + if (*ep != ':' || ep >= end) { + (void) free(ageinfo); + return (FALSE); + } + if (ep != p) + sp->sp_max = val; + p = ep + 1; + + val = strtol(p, &ep, 10); + if (*ep != ':' || ep >= end) { + (void) free(ageinfo); + return (FALSE); + } + if (ep != p) + sp->sp_warn = val; + p = ep + 1; + + val = strtol(p, &ep, 10); + if (*ep != ':' || ep >= end) { + (void) free(ageinfo); + return (FALSE); + } + if (ep != p) + sp->sp_inact = val; + p = ep + 1; + + val = strtol(p, &ep, 10); + if (*ep != ':' || ep >= end) { + (void) free(ageinfo); + return (FALSE); + } + if (ep != p) + sp->sp_expire = val; + p = ep + 1; + + val = strtol(p, &ep, 10); + if (*ep != ':' || ep >= end) { + (void) free(ageinfo); + return (FALSE); + } + if (ep != p) + sp->sp_flag = val; + + (void) free(ageinfo); + return (TRUE); +} +/* + * checks if the password has aged sufficiently + */ +bool_t +__npd_has_aged(obj, res) +struct nis_object *obj; +int *res; +{ + struct spwd sp; + long now; + + if (__npd_fill_spwd(obj, &sp) == TRUE) { + + now = DAY_NOW; + if (sp.sp_lstchg != 0 && sp.sp_lstchg <= now) { + /* password changed before or just now */ + if (now < (sp.sp_lstchg + sp.sp_min)) { + *res = NPD_NOTAGED; + return (FALSE); + } else + return (TRUE); + } + } + *res = NPD_NOSHDWINFO; + return (FALSE); +} + +/* + * Authenticate the admin by decrypting any of their secret keys with + * the passwd they sent across. If any of the secret keys, of mech types + * listed in the NIS+ security cf, are decrypted, then return TRUE. + */ +bool_t +__authenticate_admin(char *prin, char *pass) +{ + char *d; + struct nis_result *cres; + char netname[MAXNETNAMELEN+1]; + mechanism_t **mechs; + + if ((prin == NULL || *prin == '\0') || + (pass == NULL || *pass == '\0')) + return (FALSE); + + if (!__npd_prin2netname(prin, netname)) { + syslog(LOG_ERR, + "__authenticate_admin: cannot convert principal '%s' to netname", prin); + return (FALSE); + } + + d = strchr(prin, '.'); + if (d == NULL) + d = nis_local_directory(); + else + d++; + + if (mechs = __nis_get_mechanisms(FALSE)) { + mechanism_t **mpp; + char auth_type[MECH_MAXATNAME+1]; + + for (mpp = mechs; *mpp; mpp++) { + mechanism_t *mp = *mpp; + + if (verbose) + syslog(LOG_INFO, + "__authenticate_admin: trying mech '%s' for user '%s'", + mp->alias ? mp->alias : "NULL", prin); + + if (AUTH_DES_COMPAT_CHK(mp)) { + __nis_release_mechanisms(mechs); + goto try_auth_des; + } + if (! VALID_MECH_ENTRY(mp)) + continue; + + if (!__nis_mechalias2authtype(mp->alias, auth_type, + sizeof (auth_type))) + continue; + + cres = nis_getcredent(prin, d, auth_type); + if (cres != NULL && cres->status == NIS_SUCCESS) { + char *sp; + + if (debug) + syslog(LOG_DEBUG, + "__authenticate_admin: got cred entry"); + + sp = ENTRY_VAL(NIS_RES_OBJECT(cres), 4); + + if (sp != NULL) { + if (xdecrypt_g(sp, mp->keylen, + mp->algtype, pass, + netname, TRUE)) { + __nis_release_mechanisms( + mechs); + + if (debug) + syslog(LOG_DEBUG, + "__authenticate_admin: xdecrypt success"); + + (void) nis_freeresult(cres); + return (TRUE); + } + } + } + if (cres != NULL) + (void) nis_freeresult(cres); + + } + __nis_release_mechanisms(mechs); + } else { + /* + * No valid mechs in the NIS+ security cf file, + * so let's try AUTH_DES. + */ + try_auth_des: + cres = nis_getcredent(prin, d, AUTH_DES_AUTH_TYPE); + if (cres != NULL && cres->status == NIS_SUCCESS) { + char *sp; + sp = ENTRY_VAL(NIS_RES_OBJECT(cres), 4); + if (sp != NULL) { + if (xdecrypt_g(sp, AUTH_DES_KEYLEN, + AUTH_DES_ALGTYPE, pass, + NULL, TRUE)) { + (void) nis_freeresult(cres); + return (TRUE); + } + } + } + if (cres != NULL) + (void) nis_freeresult(cres); + } + + return (FALSE); +} + +/* + * build a netname given a nis+ principal name + */ +bool_t +__npd_prin2netname(prin, netname) +char *prin; +char netname[MAXNETNAMELEN]; +{ + nis_result *pass_res; + nis_object *pobj; + char name[NIS_MAXNAMELEN]; + char *domain; + uid_t uid; + + + if (prin == NULL || *prin == '\0') + return (FALSE); + if (strlen(prin) > (size_t)NIS_MAXNAMELEN) + return (FALSE); + (void) sprintf(name, "%s", prin); + + /* get the domain name */ + if (name[strlen(name) - 1] == '.') + name[strlen(name) - 1] = '\0'; + else + return (FALSE); + domain = strchr(name, '.'); + if (domain == NULL) + return (FALSE); + else + *domain++ = '\0'; + + /* nis_getpwdent() will fully qualify the domain */ + pass_res = nis_getpwdent(name, domain); + + if (pass_res == NULL) + return (FALSE); + + switch (pass_res->status) { + case NIS_SUCCESS: + pobj = NIS_RES_OBJECT(pass_res); + uid = atol(ENTRY_VAL(pobj, 2)); + (void) nis_freeresult(pass_res); + /* we cannot simply call user2netname() ! */ + if ((strlen(domain) + 7 + 11) > (size_t)MAXNETNAMELEN) + return (FALSE); + (void) sprintf(netname, "unix.%d@%s", uid, domain); + return (TRUE); + + case NIS_NOTFOUND: + /* + * assumption: hosts DO NOT have their passwords + * stored in NIS+ ==> not a user but a host + */ + if ((strlen(domain) + 7 + strlen(name)) > + (size_t)MAXNETNAMELEN) + return (FALSE); + (void) sprintf(netname, "unix.%s@%s", name, domain); + (void) nis_freeresult(pass_res); + return (TRUE); + + default: + syslog(LOG_ERR, "no passwd entry found for %s", prin); + (void) nis_freeresult(pass_res); + return (FALSE); + } +} + +/* + * make a new DH (classic "DES" or new "DH640-0") cred entry and add it + */ +bool_t +__npd_addcredent(char *prin, /* principal name */ + char *domain, /* domain name */ + char *auth_type, /* cred table auth type name */ + keylen_t keylen, /* DH key length */ + algtype_t algtype, /* DH algorithm type */ + char *newpass) /* new pw used to encrypt secret key */ +{ + char *public = NULL; + char *secret = NULL; + char *encrypted_secret = NULL; + nis_object obj; + entry_col ecol[5]; + struct nis_result *mod_res; + char buf[NIS_MAXNAMELEN]; + char nisdomain[NIS_MAXNAMELEN]; + char netname[MAXNETNAMELEN]; + int status; + bool_t ret; + + /* check args */ + if ((prin == NULL || *prin == '\0') || + (domain == NULL || *domain == '\0') || auth_type == NULL) { + ret = FALSE; + goto out; + } + + if (strlen(domain) > (size_t)NIS_MAXNAMELEN) { + ret = FALSE; + goto out; + } + (void) sprintf(nisdomain, "%s", domain); + if (nisdomain[strlen(buf) - 1] != '.') + (void) strcat(buf, "."); + + /* build netname from principal name */ + if (__npd_prin2netname(prin, netname) == FALSE) { + ret = FALSE; + goto out; + } + + /* initialize object */ + memset((char *)&obj, 0, sizeof (obj)); + memset((char *)ecol, 0, sizeof (ecol)); + + obj.zo_name = "cred"; + obj.zo_ttl = 43200; + obj.zo_data.zo_type = NIS_ENTRY_OBJ; + obj.zo_owner = prin; + obj.zo_group = nis_local_group(); + obj.zo_domain = nisdomain; + /* owner: r; group: rmcd */ + obj.zo_access = (NIS_READ_ACC<<16)| + ((NIS_READ_ACC|NIS_MODIFY_ACC| + NIS_CREATE_ACC|NIS_DESTROY_ACC)<<8); + + if (strcasecmp(auth_type, "LOCAL") != 0) { + + if ((public = malloc(BITS2NIBBLES(keylen) + 1)) == NULL) { + syslog(LOG_ALERT, "malloc failed"); + ret = FALSE; + goto out; + } + if ((secret = malloc(BITS2NIBBLES(keylen) + 1)) == NULL) { + syslog(LOG_ALERT, "malloc failed"); + ret = FALSE; + goto out; + } + + /* generate key-pair */ + if (! __gen_dhkeys_g(public, secret, keylen, algtype, + newpass)) { + syslog(LOG_ERR, + "could not generate DH key pair for %d-%d", + keylen, algtype); + ret = FALSE; + goto out; + } + if (! xencrypt_g(secret, keylen, algtype, newpass, netname, + &encrypted_secret, TRUE)) { + syslog(LOG_ERR, + "could not encrypt secret key for %d-%d", + keylen, algtype); + ret = FALSE; + goto out; + } + + /* build cred entry */ + ecol[0].ec_value.ec_value_val = prin; + ecol[0].ec_value.ec_value_len = strlen(prin) + 1; + + ecol[1].ec_value.ec_value_val = auth_type; + ecol[1].ec_value.ec_value_len = strlen(auth_type) + 1; + + ecol[2].ec_value.ec_value_val = netname; + ecol[2].ec_value.ec_value_len = strlen(netname) + 1; + + ecol[3].ec_value.ec_value_val = public; + ecol[3].ec_value.ec_value_len = strlen(public) + 1; + + ecol[4].ec_value.ec_value_val = encrypted_secret; + ecol[4].ec_value.ec_value_len = strlen(encrypted_secret) + 1; + ecol[4].ec_flags |= EN_CRYPT; + } + obj.EN_data.en_type = "cred_tbl"; + obj.EN_data.en_cols.en_cols_val = ecol; + obj.EN_data.en_cols.en_cols_len = 5; + + /* strlen("cred.org_dir.") + null = 14 */ + if ((strlen(nisdomain) + 14) > (size_t)NIS_MAXNAMELEN) + return (FALSE); + (void) sprintf(buf, "cred.org_dir.%s", (char *)&nisdomain[0]); + + if (debug == TRUE) + (void) nis_print_object(&obj); + + mod_res = nis_add_entry(buf, &obj, 0); + status = mod_res->status; + (void) nis_freeresult(mod_res); + switch (status) { + case NIS_SUCCESS: + if (verbose) + syslog(LOG_ERR, + "generated Diffie-Hellman key pair (type %d-%d) for '%s'", + keylen, algtype, prin); + ret = TRUE; + goto out; + + case NIS_PERMISSION: + if (verbose == TRUE) + syslog(LOG_ERR, + "permission denied to add a %s cred entry for %s", + auth_type, prin); + ret = FALSE; + goto out; + + default: + if (verbose == TRUE) + syslog(LOG_ERR, + "error creating %s cred for %s, NIS+ error: %s", + auth_type, prin, nis_sperrno(status)); + ret = FALSE; + goto out; + } + +out: + free(secret); + free(public); + free(encrypted_secret); + + return (ret); +} + +/* + * Reencrypt a DH cred tbl secret key and return a ptr to it's newly + * allocated memory on successful return. Also, if the global generatekeys + * is set, return a ptr of newly allocated memory containing a new public + * key too. + * + * Return NULL on any type of failure. + * + * Caller must free secret and public key memory on successful return. + */ +static char * +reencrypt_secret(char *oldcryptsecret, /* in */ + char *oldpass, /* in */ + char *newpass, /* in */ + keylen_t keylen, /* in */ + algtype_t algtype, /* in */ + char **public, /* out */ + bool_t *keyset, /* out */ + char *netname) /* in */ +{ + char *secret = NULL; + char *reencrypted_secret = NULL; + char *tmppk = NULL; + uint_t slen; + + if (!oldcryptsecret || !oldpass || !newpass || + (generatekeys && !public) || !keyset || !netname) + return (NULL); + + slen = strlen(oldcryptsecret); + + if ((secret = malloc(slen + 1)) == NULL) { + syslog(LOG_ALERT, + "reencrypte_secret: no memory!"); + return (NULL); + } + + (void) strcpy(secret, oldcryptsecret); + + if (xdecrypt_g(secret, keylen, algtype, oldpass, netname, TRUE) == 0) { + if (verbose) + syslog(LOG_ERR, + "could not decrypt keytype %d-%d for '%s'", + keylen, algtype, netname); + + if (generatekeys == TRUE) { + if ((tmppk = malloc(slen + 1)) + == NULL) { + syslog(LOG_ALERT, "no memory!"); + free(secret); + return (NULL); + } + if (!__gen_dhkeys_g(tmppk, secret, keylen, + algtype, newpass)) { + syslog(LOG_ERR, + "could not generate new Diffie-Hellman key pair for type %d-%d", + keylen, algtype); + free(secret); + free(tmppk); + return (NULL); + } + *keyset = TRUE; + if (verbose) + syslog(LOG_ERR, + "generated Diffie-Hellman key pair (type %d-%d) for '%s'", + keylen, algtype, netname); + } else { + free(secret); + return (NULL); + } + } + if (xencrypt_g(secret, keylen, algtype, newpass, netname, + &reencrypted_secret, TRUE) == 0) { + syslog(LOG_ERR, "could not encrypt keytype %d-%d", + keylen, algtype); + free(secret); + free(tmppk); + return (NULL); + } + + free(secret); + + if (generatekeys == TRUE && *keyset == TRUE) + *public = tmppk; + return (reencrypted_secret); +} + +/* + * reencrypt the secret key (or generate a new key-pair) + * and update the cred table (for 1 key of type 'authtype'). + */ +int +__npd_upd_cred(char *user, /* user name */ + char *domain, /* domainname */ + char *authtype, + keylen_t keylen, + algtype_t algtype, + char *oldpass, + char *newpass, + int *err) +{ + struct nis_result *cred_res, *mod_res; + nis_object *cobj, *eobj; + char prin[NIS_MAXNAMELEN]; + char netname[MAXNETNAMELEN]; + char *oldcryptsecret = NULL; + char *newcryptsecret = NULL; + char *public = NULL; + entry_col ecol[5]; + char buf[NIS_MAXNAMELEN]; + int status; + bool_t keyset = FALSE; + entry_col * eobj_col; + uint_t eobj_col_len; + char short_opass[sizeof (des_block)+1], *short_opassp = NULL; + char short_npass[sizeof (des_block)+1], *short_npassp = NULL; + + if ((user == NULL || *user == '\0') || + (domain == NULL || *domain == '\0') || authtype == NULL) { + *err = NPD_KEYNOTREENC; + return (NIS_SYSTEMERROR); + } + /* "." + "." + null = 3 */ + if ((strlen(user) + strlen(domain) + 3) > (size_t)NIS_MAXNAMELEN) { + *err = NPD_KEYNOTREENC; + return (NIS_SYSTEMERROR); + } + (void) sprintf(prin, "%s.%s", user, domain); + if (prin[strlen(prin) - 1] != '.') + (void) strcat(prin, "."); + + if (__npd_prin2netname(prin, netname) == FALSE) { + *err = NPD_KEYNOTREENC; + return (NIS_SYSTEMERROR); + } + + cred_res = nis_getcredent(prin, domain, authtype); + if (cred_res == NULL) { + *err = NPD_KEYNOTREENC; + return (NIS_SYSTEMERROR); + } + status = cred_res->status; + switch (status) { + case NIS_SUCCESS: + if (verbose == TRUE) + syslog(LOG_ERR, "found a %s cred entry for %s", + authtype, prin); + cobj = NIS_RES_OBJECT(cred_res); + if (cobj == NULL) { + *err = NPD_KEYNOTREENC; + break; + } + + oldcryptsecret = ENTRY_VAL(cobj, 4); + + if (oldpass) { + strlcpy(short_opass, oldpass, sizeof (short_opass)); + short_opassp = short_opass; + } + + if (newpass) { + strlcpy(short_npass, newpass, sizeof (short_npass)); + short_npassp = short_npass; + } + + if (!(newcryptsecret = reencrypt_secret(oldcryptsecret, + short_opassp, short_npassp, + keylen, algtype, + &public, &keyset, netname))) { + *err = NPD_KEYNOTREENC; + (void) nis_freeresult(cred_res); + return (NIS_SYSTEMERROR); + } + + memset((char *)ecol, 0, sizeof (ecol)); + + if (keyset == TRUE && generatekeys == TRUE) { + /* public key changed too */ + ecol[3].ec_value.ec_value_val = public; + ecol[3].ec_value.ec_value_len = strlen(public) + 1; + ecol[3].ec_flags = EN_MODIFIED; + } + ecol[4].ec_value.ec_value_val = newcryptsecret; + ecol[4].ec_value.ec_value_len = strlen(newcryptsecret) + 1; + ecol[4].ec_flags = EN_MODIFIED|EN_CRYPT; + + /* strlen("[cname=,auth_type=],cred.") + null + "." = 27 */ + if ((strlen(prin) + strlen(cobj->zo_domain) + strlen(authtype) + + 27) > (size_t)NIS_MAXNAMELEN) { + *err = NPD_KEYNOTREENC; + break; + } + (void) sprintf(buf, "[cname=%s,auth_type=%s],cred.%s", + prin, authtype, cobj->zo_domain); + if (buf[strlen(buf) - 1] != '.') + (void) strcat(buf, "."); + + eobj = nis_clone_object(cobj, NULL); + if (eobj == NULL) { + *err = NPD_KEYNOTREENC; + break; + } + + eobj_col = eobj->EN_data.en_cols.en_cols_val; + eobj_col_len = eobj->EN_data.en_cols.en_cols_len; + + eobj->EN_data.en_cols.en_cols_val = ecol; + eobj->EN_data.en_cols.en_cols_len = 5; + + mod_res = nis_modify_entry(buf, eobj, 0); + status = mod_res->status; + if (status != NIS_SUCCESS) + *err = NPD_KEYNOTREENC; + else + if (verbose) + syslog(LOG_ERR, + "Diffie-Hellman key (type %d-%d) reencrypted for '%s'", + keylen, algtype, prin); + + /* restore ecol so that we can free eobj */ + eobj->EN_data.en_cols.en_cols_val = eobj_col; + eobj->EN_data.en_cols.en_cols_len = eobj_col_len; + (void) nis_destroy_object(eobj); + (void) nis_freeresult(mod_res); + break; + + case NIS_NOTFOUND: + if (verbose == TRUE) + syslog(LOG_ERR, "no %s cred for %s", authtype, prin); + if ((generatekeys == TRUE) && + (__npd_addcredent(prin, domain, authtype, + keylen, algtype, short_npassp) == TRUE)) { + *err = NPD_KEYSUPDATED; + status = NIS_SUCCESS; + } else { + *err = NPD_KEYNOTREENC; + status = NIS_SYSTEMERROR; + } + break; + default: + *err = NPD_KEYNOTREENC; + syslog(LOG_ERR, + "NIS+ error (%d) getting cred entry for %s", + status, prin); + break; + } + + free(newcryptsecret); + free(public); + + (void) nis_freeresult(cred_res); + return (status); +} + +/* + * Loop thru configed DH mechs and if the cred entry exists + * and can be decrypted with old pw, then reencrypt it. If + * it cannot be decrypted or does not exist, and the generatekeys + * option is set, then create a new DH key pair. + * + * If any of the key possibilities are reencrypted or generated successfully, + * then return NIS_SUCCESS (and set 'err' to NIS_SUCCESS). + * + * On failure, return the NIS error code and set 'err' to the NPD error code. + */ +int +__npd_upd_all_pk_creds(char *user, /* in */ + char *domain, /* in */ + char *oldpass, /* in */ + char *newpass, /* in */ + int *err) /* out */ +{ + int ret; + int localerr; + int key_success = 0; + mechanism_t **mechs; + mechanism_t **mpp; + + if ((user == NULL || *user == '\0') || + (domain == NULL || *domain == '\0')) { + *err = NPD_KEYNOTREENC; + return (NIS_SYSTEMERROR); + } + /* "." + "." + null = 3 */ + if ((strlen(user) + strlen(domain) + 3) > (size_t)NIS_MAXNAMELEN) { + *err = NPD_KEYNOTREENC; + return (NIS_SYSTEMERROR); + } + + if (debug) + syslog(LOG_DEBUG, "__npd_upd_all_pk_creds: user = %s", + user); + if (mechs = __nis_get_mechanisms(FALSE)) { + /* + * Try all the mechs listed in the NIS+ security cf (or until + * AUTH_DES (192bit DH keys) entry is reached). + */ + for (mpp = mechs; *mpp; mpp++) { + mechanism_t *mp = *mpp; + char auth_type[MECH_MAXATNAME+1]; + + if (AUTH_DES_COMPAT_CHK(mp)) { + __nis_release_mechanisms(mechs); + goto try_auth_des; + } + + if (!VALID_MECH_ENTRY(mp)) + continue; + + if (!__nis_mechalias2authtype(mp->alias, auth_type, + sizeof (auth_type))) { + syslog(LOG_ERR, + "cannot convert mech alias '%s' to auth type", + mp->alias); + continue; + } + + ret = __npd_upd_cred(user, domain, auth_type, + mp->keylen, mp->algtype, + oldpass, newpass, &localerr); + if (ret == NIS_SUCCESS) { + key_success++; + } else { + syslog(LOG_ERR, + "cannot reencrypt ('%s') creds for '%s'", + auth_type, user); + if (debug) + syslog(LOG_DEBUG, + "__npd_upd_all_pk_creds: upd_cred fail; return = %d, localerr = %d", + ret, localerr); + } + } + __nis_release_mechanisms(mechs); + + } else { + if (debug) + syslog(LOG_DEBUG, +"__npd_upd_all_pk_creds: no valid GSS mechs found...trying AUTH_DES..."); + +try_auth_des: + ret = __npd_upd_cred(user, domain, AUTH_DES_AUTH_TYPE, + AUTH_DES_KEYLEN, AUTH_DES_ALGTYPE, + oldpass, newpass, &localerr); + if (ret == NIS_SUCCESS) { + key_success++; + } else { + syslog(LOG_ERR, + "cannot reencrypt ('%s') creds for '%s'", + AUTH_DES_AUTH_TYPE, user); + if (debug) + syslog(LOG_DEBUG, + "__npd_upd_all_pk_creds: upd_cred des fail; ret = %d, localerr = %d", + ret, localerr); + } + } + + /* + * Return success if any of the key possibilities were + * reencrypted or generated. + */ + if (key_success > 0) { + *err = NIS_SUCCESS; + return (NIS_SUCCESS); + } + + *err = NPD_KEYNOTREENC; + return (NIS_SYSTEMERROR); +} + +/* + * encrypt new passwd + */ +char * +__npd_encryptpass(char *pass, nis_object *user) +{ + char *newpass; + char *salt; + struct passwd *pw; + if (pass == NULL || *pass == '\0') + return (NULL); + + /* + * We can't call getpwnam_r in here because the search paths + * for the machine running rpc.nispasswdd may not actually find + * the user. It is for this reason we got the nis_result passed + * in - the output of nis_list(). We need to turn that into a + * struct passwd before passing it to crypt_gensalt(3c). + */ + pw = nis_object2passwd(user); + salt = crypt_gensalt(pass, pw); + newpass = crypt(pass, salt); + if (salt != NULL) { + free(salt); + salt = NULL; + } + if (pw != NULL) { + free(pw); + pw = NULL; + } + return (newpass); +} + + +/* + * find the passwd object for this user from the list + * of dirs given. If object is found in more than one + * place return an error, otherwise clone the object. + * Note, the object should be freed using nis_destroy_object(). + */ +bool_t +__npd_find_obj(user, dirlist, obj) +char *user; +char *dirlist; +nis_object **obj; /* returned */ +{ + char *tmplist; + char *curdir, *end = NULL; + char buf[NIS_MAXNAMELEN]; + nis_result *tmpres; + nis_object *tmpobj = NULL; + + if ((user == NULL || *user == '\0') || + (dirlist == NULL || *dirlist == '\0')) + return (FALSE); + + *obj = NULL; + if ((tmplist = strdup(dirlist)) == NULL) { + syslog(LOG_CRIT, "out of memory"); + return (FALSE); + } + for (curdir = tmplist; curdir != NULL; curdir = end) { + end = strchr(curdir, ' '); + if (end != NULL) + *end++ = NULL; + if (strncasecmp(curdir, "org_dir", 7) != 0) + continue; /* not an org_dir */ + + /* strlen("[name=],passwd." + null + "." = 17 */ + if ((strlen(user) + 17 + strlen(curdir)) > + (size_t)NIS_MAXNAMELEN) { + (void) free(tmplist); + return (FALSE); + } + + (void) sprintf(buf, "[name=%s],passwd.%s", user, curdir); + if (buf[strlen(buf) - 1] != '.') + (void) strcat(buf, "."); + tmpres = nis_list(buf, USE_DGRAM+MASTER_ONLY, NULL, NULL); + switch (tmpres->status) { + case NIS_NOTFOUND: /* skip */ + (void) nis_freeresult(tmpres); + continue; + + case NIS_SUCCESS: + if (NIS_RES_NUMOBJ(tmpres) != 1) { + /* should only have one entry */ + (void) nis_freeresult(tmpres); + (void) free(tmplist); + if (tmpobj != NULL) + (void) nis_destroy_object(tmpobj); + return (FALSE); + } + if (tmpobj != NULL) { + /* found in more than one dir */ + (void) nis_destroy_object(tmpobj); + (void) nis_freeresult(tmpres); + (void) free(tmplist); + *obj = NULL; + return (FALSE); + } + tmpobj = nis_clone_object(NIS_RES_OBJECT(tmpres), NULL); + if (tmpobj == NULL) { + syslog(LOG_CRIT, "out of memory"); + (void) nis_freeresult(tmpres); + (void) free(tmplist); + return (FALSE); + } + (void) nis_freeresult(tmpres); + continue; + + default: + /* some NIS+ error - quit processing */ + curdir = NULL; + break; + } + } + (void) nis_freeresult(tmpres); + (void) free(tmplist); + if (tmpobj == NULL) /* no object found */ + return (FALSE); + *obj = tmpobj; + return (TRUE); +} + +/* + * go thru' the list of dirs and see if the host is a + * master server for any 'org_dir'. + */ +bool_t +__npd_am_master(host, dirlist) +char *host; +char *dirlist; +{ + char *tmplist; + char *curdir, *end = NULL; + nis_server **srvs; + unsigned sleep_time; + unsigned total_sleep_time; + unsigned next_log_time; + + if ((host == NULL || *host == '\0') || + (dirlist == NULL || *dirlist == '\0')) + return (FALSE); + + if ((tmplist = strdup(dirlist)) == NULL) { + syslog(LOG_CRIT, "out of memory"); + return (FALSE); + } + for (curdir = tmplist; curdir != NULL; curdir = end) { + end = strchr(curdir, ' '); + if (end != NULL) + *end++ = NULL; + if (strncasecmp(curdir, "org_dir", 7) != 0) + continue; /* not an org_dir */ + + /* + * Attempt to get the nis serv list + * If no success after an hour, exit + */ + sleep_time = 1; + total_sleep_time = 0; + next_log_time = 60; + while ((srvs = nis_getservlist(curdir)) == NULL) { + if (total_sleep_time >= 3600) + break; /* give up after an hour */ + /* + * Log errors at 1 minute, 10 minutes, 20 minutes, + * 30 minutes, 40 minutes, and 50 minutes + */ + if (total_sleep_time >= next_log_time) { + syslog(LOG_ERR, + "cannot get a list of servers that serve '%s'. Retrying...", + curdir); + if (next_log_time == 60) + next_log_time = 600; + else + next_log_time += 600; + } + (void) sleep(sleep_time); + __nis_CacheRestart(); + total_sleep_time += sleep_time; + /* + * sleep times of 1,2,4,8,16,29 seconds (total 60) + * and once a minute thereafter + */ + if (sleep_time <= 8) + sleep_time += sleep_time; + else if (sleep_time <= 16) + sleep_time = 29; + else + sleep_time = 60; + } + + if (srvs == NULL) { + syslog(LOG_ERR, + "cannot get a list of servers that serve '%s'", curdir); + (void) free(tmplist); + return (FALSE); + } + if (strcasecmp(host, srvs[0]->name) == 0) { + (void) free(tmplist); + (void) nis_freeservlist(srvs); + return (TRUE); + } + (void) nis_freeservlist(srvs); + } + (void) free(tmplist); + return (FALSE); +} + + +/* + * check whether this principal has permission to + * add/delete/modify an entry object + */ +bool_t +__npd_can_do(right, obj, prin, column) +unsigned long right; /* access right seeked */ +nis_object *obj; /* entry object */ +char *prin; /* principal seeking access */ +int column; /* column being modified */ +{ + nis_result *res; + nis_object *tobj; + table_col *tc; + char buf[NIS_MAXNAMELEN]; + int mod_ok; + + /* strlen("passwd." + null + "." = 9 */ + if ((9 + strlen(obj->zo_domain)) > (size_t)NIS_MAXNAMELEN) { + return (FALSE); + } + (void) sprintf(buf, "passwd.%s", obj->zo_domain); + if (buf[strlen(buf) - 1] != '.') + (void) strcat(buf, "."); + + res = nis_lookup(buf, MASTER_ONLY); + + if (res->status == NIS_SUCCESS) { + tobj = NIS_RES_OBJECT(res); + if (__type_of(tobj) != NIS_TABLE_OBJ) { + (void) nis_freeresult(res); + return (FALSE); + } + } else { + (void) nis_freeresult(res); + return (FALSE); + } + + /* check the permission on the table */ + mod_ok = __nis_ck_perms(right, tobj->zo_access, tobj, prin, 1); + if (mod_ok == TRUE) { + (void) nis_freeresult(res); + return (TRUE); + } + + /* check the permission on the obj */ + mod_ok = __nis_ck_perms(right, obj->zo_access, obj, prin, 1); + if (mod_ok == TRUE) { + (void) nis_freeresult(res); + return (TRUE); + } + + if (column > tobj->TA_data.ta_maxcol) { + /* invalid column */ + (void) nis_freeresult(res); + return (FALSE); + } + tc = tobj->TA_data.ta_cols.ta_cols_val; /* table columns */ + + /* check the permission on column being modified */ + mod_ok = __nis_ck_perms(right, tc[column].tc_rights, obj, prin, 1); + + (void) nis_freeresult(res); + return (mod_ok); +} + +void +__npd_gen_rval(randval) +unsigned long *randval; +{ + int i, shift; + struct timeval tv; + unsigned int seed = 0; + + for (i = 0; i < 1024; i++) { + (void) gettimeofday(&tv, NULL); + shift = i % 8 * sizeof (int); + seed ^= (tv.tv_usec << shift) | (tv.tv_usec >> (32 - shift)); + } + (void) srandom(seed); + *randval = (ulong_t)random(); +} + +/* + * The following code is a modified version of nis_object2ent which was + * taken from usr/src/lib/nsswitch/nisplus/common/getpwnam.c. + * If you make fixes here you should check if they are also valid for the + * nss_nisplus.so version. + * + * The code had to be changed since the original was written for use inside + * the nsswitch code which wasn't suitable for direct inclusion here. + */ +static struct passwd * +nis_object2passwd(nis_object *obj) +{ + char *val, *endnum, *nullstring; + struct passwd *pw; + struct entry_col *ecol; + int len; + + nullstring = strdup(""); + /* + * If we got more than one nis_object, we just ignore object(s) + * except the first. Although it should never have happened. + * + * ASSUMPTION: All the columns in the NIS+ tables are + * null terminated. + */ + + if (obj->zo_data.zo_type != NIS_ENTRY_OBJ || + obj->EN_data.en_cols.en_cols_len < PW_COL) { + /* namespace/table/object is curdled */ + return (NULL); + } + ecol = obj->EN_data.en_cols.en_cols_val; + + pw = malloc(sizeof (struct passwd)); + + /* + * pw_name: user name + */ + EC_SET(ecol, PW_NDX_NAME, len, val); + if (len < 2 || (*val == '\0')) + return (NULL); + pw->pw_name = strdup(val); + + /* + * pw_uid: user id + */ + EC_SET(ecol, PW_NDX_UID, len, val); + if (len < 2) { + return (NULL); + } else { + pw->pw_uid = strtol(val, &endnum, 10); + if (*endnum != 0 || val == endnum) { + return (NULL); + } + } + + /* + * pw_passwd: user passwd. Do not HAVE to get this here + * because the caller would do a getspnam() anyway. + */ + EC_SET(ecol, PW_NDX_PASSWD, len, val); + if (len < 2) { + /* + * don't return NULL pointer, lot of stupid programs + * out there. + */ + pw->pw_passwd = nullstring; + } else { + pw->pw_passwd = strdup(val); + } + + /* + * pw_gid: user's primary group id. + */ + EC_SET(ecol, PW_NDX_GID, len, val); + if (len < 2) { + return (NULL); + } else { + pw->pw_gid = strtol(val, &endnum, 10); + if (*endnum != 0 || val == endnum) { + return (NULL); + } + } + + /* + * pw_gecos: user's real name. + */ + EC_SET(ecol, PW_NDX_GCOS, len, val); + if (len < 2) { + /* + * don't return NULL pointer, lot of stupid programs + * out there. + */ + pw->pw_gecos = nullstring; + } else { + pw->pw_gecos = strdup(val); + } + + /* + * pw_dir: user's home directory + */ + EC_SET(ecol, PW_NDX_HOME, len, val); + if (len < 2) { + /* + * don't return NULL pointer, lot of stupid programs + * out there. + */ + pw->pw_dir = nullstring; + } else { + pw->pw_dir = strdup(val); + } + + /* + * pw_shell: user's login shell + */ + EC_SET(ecol, PW_NDX_SHELL, len, val); + if (len < 2) { + /* + * don't return NULL pointer, lot of stupid programs + * out there. + */ + pw->pw_shell = nullstring; + } else { + pw->pw_shell = strdup(val); + } + + /* + * pw_age and pw_comment shouldn't be used anymore, but various things + * (allegedly in.ftpd) merrily do strlen() on them anyway, so we + * keep the peace by returning a zero-length string instead of a + * null pointer. + */ + pw->pw_age = pw->pw_comment = nullstring; + if (debug) + syslog(LOG_DEBUG, "nis_object2passwd:" + "pw->pw_name = %s pw->pw_uid = %d pw->pw_gid = %d " + "pw->pw_gecos = %s pw->pw_dir = %s pw->pw_shell = %s ", + pw->pw_name, pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir, + pw->pw_shell); + + return (pw); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.h b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.h new file mode 100644 index 0000000000..a3f7158e38 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.h @@ -0,0 +1,79 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 1994-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + */ + +#ifndef _NPD_SVCSUBR_H +#define _NPD_SVCSUBR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <rpcsvc/yppasswd.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct nis_result *nis_getpwdent(char *user, char *domain); +int __npd_upd_all_pk_creds(char *user, char *domain, char *oldpass, + char *newpass, int *err); +void __npd_gen_rval(unsigned long *randval); +bool_t __npd_has_aged(struct nis_object *obj, int *res); +bool_t __authenticate_admin(char *prin, char *pass); +char *__npd_encryptpass(char *pass, nis_object *user); +bool_t __npd_can_do(unsigned long right, nis_object *obj, char *prin, + int column); +bool_t __npd_find_obj(char *user, char *dirlist, nis_object **obj); +int update_authtok_nis_fwd(char *usrname, char *newpwe, + char *oldpwu, char *master, char *gecos, char *shell); +bool_t __npd_prin2netname(char *, char []); +bool_t __npd_am_master(char *host, char *dirlist); + +bool_t nispasswd_authenticate_1_svc(npd_request *argp, + nispasswd_authresult *result, struct svc_req *rqstp); +bool_t nispasswd_update_1_svc(npd_update *updreq, nispasswd_updresult *res, + struct svc_req *rqstp); +bool_t yppasswdproc_update_1_svc(struct yppasswd *yppass, int *result, + struct svc_req *rqstp); + +/* from libnsl */ +extern bool_t __nis_ismaster(char *host, char *domain); +extern bool_t __nis_isadmin(char *princ, char *table, char *domain); +extern bool_t __nis_ck_perms(unsigned int right, unsigned int mask, + nis_object *obj, nis_name pr, int level); +extern nis_server *__nis_host2nis_server(char *host, bool_t addpubkey, + int *errcode); +extern bool_t __npd_ecb_crypt(uint32_t *val1, uint32_t *val2, des_block *buf, + unsigned int bufsize, unsigned int mode, des_block *deskey); +extern bool_t __npd_cbc_crypt(uint32_t *val, char *str, unsigned int strsize, + npd_newpass *buf, unsigned int bufsize, unsigned int mode, + des_block *deskey); +extern void __free_nis_server(nis_server *server); + +#ifdef __cplusplus +} +#endif + +#endif /* _NPD_SVCSUBR_H */ diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_ypfwd.c b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_ypfwd.c new file mode 100644 index 0000000000..04c63e0837 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_ypfwd.c @@ -0,0 +1,117 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +#include <syslog.h> +#include <rpc/rpc.h> +#include <rpc/clnt.h> +#include <rpcsvc/yppasswd.h> + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * npd_ypfwd.c + * NPD routine to forward password update request to YP + * + * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved. + * + * This function is pretty much lifted from + * lib/scheme/pm_scheme/pam_update_authtok_nis.c:update_authtok_nis(). + * + * The only difference is that we are taking requests from NPD to + * forward a password change to a YP database after the NIS+ database + * has already been updated. If this change fails, then we must undo + * the NIS+ change as well. Only at Sun can something like this exist... + * + * This function is called only from npd_svc.c:nispasswd_update_1_svc() + * or npd_svc.c:yppasswd_update_1_svc(). The set of variables are similar + * so I've provided a translation table below: + * + * variable nispasswd yppasswd what is it? + * -------- --------- -------- ----------- + * usrname entry->ul_user newpass->pw_name user name + * newpwe newpass newpass->pw_passwd encr new passwd + * XX oldpwe old_pass old_pass encr old passwd + * oldpwu entry->ol_oldpass yppass->oldpass clear old passwd + * XX newpwu pass <N/A> clear new passwd + * master ypfwd ypfwd YP master to fwd + * gecos old_gecos old_gecos original gecos + * shell old_shell old_shell original shell + */ +int +update_authtok_nis_fwd( + char *usrname, /* user name */ + char *newpwe, /* encrypted new passwd */ + char *oldpwu, /* clear old passwd */ + char *master, /* passwd master YP machine */ + char *gecos, /* (unchanged) general comments */ + char *shell) /* (unchanged) login shell */ +{ + int retval = 0; /* value to return */ + int ok; /* update return status */ + enum clnt_stat ans; /* RPC return status */ + CLIENT *client; /* RPC client handle */ + const char *fnam = "update_authok_nis_fwd"; + /* function name */ + const struct timeval timeout = { 55, 0 }; + /* NPD uses 55 seconds */ + static struct yppasswd yppwstruct; /* YP passwd struct */ + static struct passwd pwstruct; /* passwd struct */ + + /* + * ck_passwd() already checked the old passwd. It won't get here + * if the old passwd is not matched. We are just preparing the + * yppasswd update packet here. + */ + pwstruct.pw_name = usrname; /* username is changing passwd */ + pwstruct.pw_passwd = newpwe; /* encrypted new passwd */ + pwstruct.pw_gecos = gecos; /* unchanged general comments */ + pwstruct.pw_shell = shell; /* unchanged login shell */ + yppwstruct.oldpass = oldpwu; /* unencrypted old passwd */ + yppwstruct.newpw = pwstruct; /* copy in yppasswd struct */ + + if (!(client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, + "udp"))) { + syslog(LOG_INFO, "%s: can't create YP client handle\n", fnam); + return (-1); + } + + ans = CLNT_CALL(client, YPPASSWDPROC_UPDATE, xdr_yppasswd, + (char *)&yppwstruct, xdr_int, (char *)&ok, timeout); + + if ((ok != 0) || (ans != RPC_SUCCESS)) { + syslog(LOG_ERR, + "%s: can't change NIS(YP) passwd for %s on %s (err: %d)\n", + fnam, usrname, master, ok); + if (ans != RPC_SUCCESS) { + clnt_perror(client, + "RPC call failed -- client may have timed-out."); + syslog(LOG_INFO, + "%s: client could not make RPC call.\n", fnam); + retval = -1; + } + } else { + syslog(LOG_DEBUG, "%s: NIS(YP) passwd changed for %s on %s\n", + fnam, usrname, master); + } + + (void) clnt_destroy(client); + return (retval); +} diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/req.flg b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/req.flg new file mode 100644 index 0000000000..b3b75227c4 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/req.flg @@ -0,0 +1,28 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +#pragma ident "%Z%%M% %I% %E% SMI" +# + + +echo_file usr/src/lib/libnsl/include/rpcsvc/nis_dhext.h diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/rpc.nispasswdd.c b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/rpc.nispasswdd.c new file mode 100644 index 0000000000..92f950d0ab --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/rpc.nispasswdd.c @@ -0,0 +1,553 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> /* getenv, exit */ +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <memory.h> +#include <syslog.h> +#include <stropts.h> +#include <netdir.h> +#include <synch.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/yppasswd.h> +#include <rpcsvc/nispasswd.h> +#include <rpcsvc/nis_dhext.h> +#include "npd_svcsubr.h" + +int verbose = 0; +bool_t debug = FALSE; +/* + * generate keys if none exist or if admin. is changing another + * users password. + */ +bool_t generatekeys = FALSE; +ulong_t cache_time = 1800; /* cache failed attempts for 30 mins */ +int max_attempts = 3; /* max # of failed attempts allowed */ +NIS_HASH_TABLE upd_list; /* cache of updates */ +char *ypfwd = (char *)NULL; /* passwd map YP master machine */ + +static void __msgout(const char *msg); +static int __svc_priv_port_create(void); +static void nispasswd_prog(struct svc_req *rqstp, register SVCXPRT *transp); +static void yppasswd_prog(struct svc_req *rqstp, register SVCXPRT *transp); +static void set_rpc_gss_svc_names(void); + + +void +main(argc, argv) +int argc; +char **argv; +{ + pid_t pid; + int i; + int c; + int mins; + int status; + nis_tag tags[2], *tagres; + nis_server *srv; + int connmaxrec = RPC_MAXDATASIZE; + + (void) memset((char *)&upd_list, 0, sizeof (upd_list)); + (void) chdir("/var/nis"); /* drop core here */ + while ((c = getopt(argc, argv, "a:c:DgvY:")) != -1) { + switch (c) { + case 'a': + max_attempts = atoi(optarg); + if (max_attempts < 0) { + fprintf(stderr, + "%s: invalid number of maximum attempts\n", + argv[0]); + exit(1); + } + break; + case 'c': + mins = atoi(optarg); + if (mins <= 0) { + fprintf(stderr, + "%s: invalid cache time\n", argv[0]); + exit(1); + } + cache_time = (ulong_t)mins * 60; + break; + case 'D': /* debug mode */ + debug = TRUE; + break; + case 'g': /* generate new keys if none exist */ + generatekeys = TRUE; + break; + case 'v': /* verbose mode */ + verbose++; + break; + case 'Y': /* YP forward mode */ + if (!(ypfwd = strdup(optarg))) { + fprintf(stderr, "%s: out of memory\n", argv[0]); + exit(1); + } + break; + case '?': + fprintf(stderr, + "Usage: %s [-a attempts] [-c minutes] [-D] [-g] [-v]\n", + argv[0]); + exit(1); + } + } + + /* + * this check should be removed if 'root' is not the + * owner of the table or if 'root' is not a member of + * the nis+ admins. group. + * of course: you also don't want someone to spoof the + * the daemon. + */ + if (geteuid() != (uid_t)0) { + char err_string[256]; + snprintf(err_string, sizeof (err_string), + "must be superuser to run %s", argv[0]); + __msgout(err_string); + exit(1); + } + + if (debug == FALSE) { + pid = fork(); + if (pid < 0) { + perror("cannot fork"); + exit(1); + } + if (pid) + exit(0); + closefrom(0); + i = open("/dev/console", 2); + (void) dup2(i, 1); + (void) dup2(i, 2); + (void) setsid(); + } + openlog("rpc.nispasswdd", LOG_PID, LOG_DAEMON); + + /* + * should I be running ? + * Get the list of directories the local NIS+ server serves and + * check if it is the master server of any 'org_dir' listed. + */ + srv = __nis_host2nis_server(NULL, 0, &status); + if (srv == NULL) { + if (debug == TRUE) + (void) fprintf(stderr, + "no host/address information for local host, %d\n", + status); + else + syslog(LOG_ERR, + "no host/address information for local host, %d", + status); + __msgout("exiting ..."); + exit(1); + } + tags[0].tag_type = TAG_DIRLIST; + tags[0].tag_val = ""; + tags[1].tag_type = TAG_NISCOMPAT; + tags[1].tag_val = ""; + + status = nis_stats(srv, tags, 2, &tagres); + __free_nis_server(srv); + if (status != NIS_SUCCESS) { + if (verbose) + nis_perror(status, "rpc.nispasswdd"); + else + nis_lerror(status, "rpc.nispasswdd"); + __msgout(" ... exiting ..."); + exit(1); + } + if ((strcmp(tagres[0].tag_val, "<Unknown Statistics>") == 0) || + (strcmp(tagres[1].tag_val, "<Unknown Statistics>") == 0)) { + + /* old server */ + __msgout("NIS+ server does not support the new statistics tags"); + exit(1); + } + + if (__npd_am_master(nis_local_host(), tagres[0].tag_val) == FALSE) { + __msgout("Local NIS+ server is not a master server"); + __msgout(" ... exiting ..."); + exit(1); + } + + /* Specify maximum connection oriented RPC record size */ + if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) { + __msgout("unable to set maximum RPC record size"); + } + + /* + * Check if NIS+ server is running in NIS compat mode. + * Register YPD only if this is the case. + */ + if (strcasecmp(tagres[1].tag_val, "ON") == 0) { + if (rpcb_unset(YPPASSWDPROG, YPPASSWDVERS, 0) == FALSE) { + __msgout("unable to de-register (YPPASSWDPROG, YPPASSWDVERS)"); + __msgout(" ... exiting ..."); + exit(1); + } + if (__svc_priv_port_create() == FALSE) { + __msgout("unable to create (YPPASSWDPROG, YPPASSWDVERS)"); + /* continue */ + } + } + nis_freetags(tagres, 2); + + set_rpc_gss_svc_names(); + + if (rpcb_unset(NISPASSWD_PROG, NISPASSWD_VERS, 0) == FALSE) { + __msgout("unable to de-register (NISPASSWD_PROG, NISPASSWD_VERS)"); + __msgout(" ... exiting ..."); + exit(1); + } + if (svc_create(nispasswd_prog, NISPASSWD_PROG, NISPASSWD_VERS, + "circuit_v") == 0) { + __msgout("unable to create (NISPASSWD_PROG, NISPASSWD_VERS)"); + __msgout(" ... exiting ..."); + exit(1); + } + + if (verbose == TRUE) + __msgout("starting rpc.nispasswdd ..."); + svc_run(); + __msgout("svc_run returned"); + exit(1); + + /* NOTREACHED */ +} + +static void +__msgout(const char *msg) +{ + if (debug == TRUE) + (void) fprintf(stderr, "%s\n", msg); + else + syslog(LOG_ERR, msg); +} + +static bool_t +__svc_priv_port_create() +{ + struct netconfig *nconf, *nconf4 = 0, *nconf6 = 0; + void *handlep; + int fd4 = -1, fd6 = -1; + struct t_info tinfo4, tinfo6; + SVCXPRT *transp4 = 0, *transp6 = 0; + int svc4 = 0, svc6 = 0; + + if ((handlep = setnetconfig()) == (void *) NULL) { + (void) nc_perror("cannot get any transport information"); + return (FALSE); + } + + while ((nconf = getnetconfig(handlep)) != 0 && + (nconf4 == 0 || nconf6 == 0)) { + if ((nconf->nc_semantics == NC_TPI_CLTS) && + strcmp(nconf->nc_proto, NC_UDP) == 0) { + if (strcmp(nconf->nc_protofmly, NC_INET) == 0) + nconf4 = nconf; + else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) + nconf6 = nconf; + } + } + + if (nconf4 == 0 && nconf6 == 0) { + (void) endnetconfig(handlep); + __msgout("no NC_INET/NC_INET6 NC_TPI_CLTS/NC_UPD transports"); + return (FALSE); + } + + if (nconf4 != 0) + fd4 = t_open(nconf4->nc_device, O_RDWR, &tinfo4); + if (nconf6 != 0) + fd6 = t_open(nconf6->nc_device, O_RDWR, &tinfo6); + if (fd4 == -1 && fd6 == -1) { + __msgout("unable to open connection for NIS requests"); + (void) endnetconfig(handlep); + return (FALSE); + } + + if (fd4 != -1) { + if (netdir_options(nconf4, ND_SET_RESERVEDPORT, fd4, 0) == -1) { + __msgout("unable to get reserved port for NC_INET"); + netdir_perror(""); + (void) endnetconfig(handlep); + return (FALSE); + } + transp4 = svc_tli_create(fd4, nconf4, NULL, 0, 0); + } + + if (fd6 != -1) { + if (netdir_options(nconf6, ND_SET_RESERVEDPORT, fd6, 0) == -1) { + __msgout("unable to get reserved port for NC_INET6"); + netdir_perror(""); + (void) endnetconfig(handlep); + return (FALSE); + } + transp6 = svc_tli_create(fd6, nconf6, NULL, 0, 0); + } + + if (transp4 == 0 && transp6 == 0) { + __msgout("unable to create (YPPASSWDPROG, YPPASSWDVERS)"); + (void) endnetconfig(handlep); + return (FALSE); + } + + if (transp4 != 0) + svc4 = svc_reg(transp4, YPPASSWDPROG, YPPASSWDVERS, + yppasswd_prog, nconf4); + if (transp6 != 0) + svc6 = svc_reg(transp6, YPPASSWDPROG, YPPASSWDVERS, + yppasswd_prog, nconf6); + if (svc4 == 0 && svc6 == 0) { + __msgout("unable to register (YPPASSWDPROG, YPPASSWDVERS)"); + if (transp4 != 0) + (void) svc_destroy(transp4); + if (transp6 != 0) + (void) svc_destroy(transp6); + (void) endnetconfig(handlep); + return (FALSE); + } + (void) endnetconfig(handlep); + return (TRUE); +} + +static void +nispasswd_prog(rqstp, transp) +struct svc_req *rqstp; +register SVCXPRT *transp; +{ + union { + npd_request nispasswd_authenticate_1_arg; + npd_update nispasswd_update_1_arg; + } argument; + union { + nispasswd_authresult nispasswd_authenticate_1_res; + nispasswd_updresult nispasswd_update_1_res; + } result; + bool_t retval; + xdrproc_t xdr_argument, xdr_result; + bool_t (*local)(char *, void *, struct svc_req *); + + switch (rqstp->rq_proc) { + case NULLPROC: + __msgout("received NIS+ null proc call"); + (void) svc_sendreply(transp, + (xdrproc_t)xdr_void, (char *)NULL); + return; + + case NISPASSWD_AUTHENTICATE: + xdr_argument = (xdrproc_t)xdr_npd_request; + xdr_result = (xdrproc_t)xdr_nispasswd_authresult; + local = (bool_t (*) (char *, void *, struct svc_req *)) + nispasswd_authenticate_1_svc; + break; + + case NISPASSWD_UPDATE: + xdr_argument = (xdrproc_t)xdr_npd_update; + xdr_result = (xdrproc_t)xdr_nispasswd_updresult; + local = (bool_t (*) (char *, void *, struct svc_req *)) + nispasswd_update_1_svc; + break; + + default: + svcerr_noproc(transp); + return; + } + (void) memset((char *)&argument, 0, sizeof (argument)); + if (svc_getargs(transp, xdr_argument, (caddr_t)&argument) == 0) { + svcerr_decode(transp); + return; + } + (void) memset((char *)&result, 0, sizeof (result)); + retval = (bool_t)(*local)((char *)&argument, (void *)&result, rqstp); + if (retval == TRUE && (svc_sendreply(transp, xdr_result, + (char *)&result) == 0)) { + svcerr_systemerr(transp); + } + if (svc_freeargs(transp, xdr_argument, (caddr_t)&argument) == 0) { + __msgout("unable to free arguments"); + exit(1); + } + if (nispasswd_prog_1_freeresult(transp, xdr_result, + (caddr_t)&result) == 0) + __msgout("unable to free results"); + + /* NOTREACHED */ +} + +static void +yppasswd_prog(rqstp, transp) +struct svc_req *rqstp; +register SVCXPRT *transp; +{ + union { + struct yppasswd yppasswdproc_update_1_arg; + } argument; + union { + int yppasswdproc_update_1_res; + } result; + bool_t retval; + xdrproc_t xdr_argument, xdr_result; + bool_t (*local)(char *, void *, struct svc_req *); +#ifdef SHD_WE_ALWAYS + struct netconfig *nconf; +#endif + + switch (rqstp->rq_proc) { + case NULLPROC: + __msgout("received NIS null proc call"); + (void) svc_sendreply(transp, + (xdrproc_t)xdr_void, (char *)NULL); + return; + + case YPPASSWDPROC_UPDATE: + xdr_argument = (xdrproc_t)xdr_yppasswd; + xdr_result = (xdrproc_t)xdr_int; + local = (bool_t (*) (char *, void *, struct svc_req *)) + yppasswdproc_update_1_svc; + break; + + default: + svcerr_noproc(transp); + return; + } +#ifdef SHD_WE_ALWAYS + /* + * update request received, lets just check the port + */ + nconf = (struct netconfig *)getnetconfigent(transp->xp_netid); + if (nconf == (struct netconfig *)NULL) { + (void) nc_perror("could not get transport information"); + svcerr_systemerr(transp); + return; + } + + if ((strcmp(nconf->nc_protofmly, NC_INET) != 0) || + (strcmp(nconf->nc_proto, NC_UDP) != 0) || + (netdir_options(nconf, ND_CHECK_RESERVEDPORT, transp->xp_fd, + (void *) &(transp->xp_rtaddr)) != 0)) { + + if (nconf) + (void) freenetconfigent(nconf); + svcerr_weakauth(transp); + return; + } + if (nconf) + (void) freenetconfigent(nconf); +#endif + (void) memset((char *)&argument, 0, sizeof (argument)); + if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) { + svcerr_decode(transp); + return; + } + (void) memset((char *)&result, 0, sizeof (result)); + retval = (bool_t)(*local)((char *)&argument, (void *)&result, rqstp); + if (retval == TRUE && !svc_sendreply(transp, xdr_result, + (char *)&result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) { + __msgout("unable to free arguments"); + exit(1); + } +} + +/* + * Loop thru mech list from security conf file and set + * the RPC GSS service name(s). Stop processing list if + * the classic AUTH_DES compat entry is encountered. + */ +static void +set_rpc_gss_svc_names() +{ + mechanism_t **mechs; + + if (mechs = __nis_get_mechanisms(FALSE)) { + mechanism_t **mpp; + /* "nispasswd" + 1 = 10 */ + char svc_name[NIS_MAXNAMELEN+10]; + int slen; + char *lh = nis_local_host(); + + if (! lh) { + syslog(LOG_ERR, + "can't set RPC GSS service name: can't get local host name"); + __nis_release_mechanisms(mechs); + return; + } + + /* '@' + NUL = 2 */ + if (strlen(lh) + strlen(NIS_SVCNAME_NISPASSWD) + 2 > + sizeof (svc_name)) { + syslog(LOG_ERR, + "can't set RPC GSS service name: svc_name bufsize too small"); + __nis_release_mechanisms(mechs); + return; + } + /* service names are of the form svc@server.dom */ + (void) sprintf(svc_name, "%s@%s", NIS_SVCNAME_NISPASSWD, lh); + /* remove trailing '.' */ + slen = strlen(svc_name); + if (svc_name[slen - 1] == '.') + svc_name[slen - 1] = '\0'; + + for (mpp = mechs; *mpp; mpp++) { + mechanism_t *mp = *mpp; + + if (AUTH_DES_COMPAT_CHK(mp)) + break; + + if (! VALID_MECH_ENTRY(mp)) + continue; + + if (rpc_gss_set_svc_name(svc_name, mp->mechname, + 0, NISPASSWD_PROG, + NISPASSWD_VERS)) { + if (verbose) + syslog(LOG_INFO, + "RPC GSS service name for mechanism '%s' set", + mp->mechname); + } else { + rpc_gss_error_t err; + + rpc_gss_get_error(&err); + syslog(LOG_ERR, +"can't set RPC GSS svc name '%s' for mech '%s': RPC GSS err = %d, sys err = %d", + svc_name, mp->mechname, + err.rpc_gss_error, err.system_error); + } + } + __nis_release_mechanisms(mechs); + return; + } +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/Makefile b/usr/src/cmd/rpcsvc/nis/utils/Makefile new file mode 100644 index 0000000000..d04ff89a44 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/Makefile @@ -0,0 +1,173 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# cmd/rpcsvc/nis/utils/Makefile +# +# Makefile for the nis_plus utility programs. +# + +all:= TARGET= all +install:= TARGET= install +clean:= TARGET= clean +clobber:= TARGET= clobber +lint:= TARGET= lint + +SUBDIRS= nisaddcred + +# links to nischmod +LPROG= nischgrp nischown nischttl + +PROG= niscat nismatch nistest nisdefaults nischmod nisrm \ + nisgrep nismkdir nisrmdir nistbladm nisgrpadm nisln \ + nisls niserror nispath nisprefadm + +SRCS= niscat.c nismatch.c nistest.c nisdefaults.c nischmod.c nisrm.c \ + nisgrep.c nismkdir.c nisrmdir.c nistbladm.c nisgrpadm.c nisln.c \ + nisls.c niserror.c nispath.c nisprefadm.c + +OBJS= niscat.o nismatch.o nistest.o nisdefaults.o nischmod.o nisrm.o \ + nisgrep.o nismkdir.o nisrmdir.o nistbladm.o nisgrpadm.o nisln.o \ + nisls.o niserror.o nispath.o nisprefadm.o + +RPROG= nisaddent nisauthconf +RSRCS= nisaddent.c nisauthconf.c +ROBJS= nisaddent.o nisauthconf.o + +UTILSRCS= nis_util.c +UTILOBJS= nis_util.o + +BACKUP_PROG= nisbackup +BACKUP_SRCS= nisbackup.cc ../rpc.nisd/nis_log_common.c \ + ../rpc.nisd/nis_mt.c ../rpc.nisd/nis_cleanup.c +BACKUP_OBJS= nisbackup.o nis_log_common.o nis_mt.o nis_cleanup.o + +RESTORE_PROG= nisrestore +RESTORE_SRCS= nisrestore.cc ../rpc.nisd/nis_log_common.c \ + ../rpc.nisd/nis_mt.c ../rpc.nisd/nis_cleanup.c +RESTORE_OBJS= nisrestore.o nis_log_common.o nis_mt.o nis_cleanup.o + +BKRSTPROG= $(BACKUP_PROG) $(RESTORE_PROG) + +# XX64 nisrestore and nisbackup require C++ +__GNUC:sh= echo \\043 +$(__GNUC)BKRSTPROG= + +DERIVED_FILES= ../rpc.nisd/nis_svc.h + +include ../../../Makefile.cmd + +ROOTNSLIB= $(ROOT)/usr/lib/nis +ROOTUSRSBIN= $(ROOT)/usr/sbin +ROOTNSPROG= $(RPROG:%=$(ROOTNSLIB)/%) +ROOTLPROG= $(LPROG:%=$(ROOTBIN)/%) +ROOTUSBPROG= $(BKRSTPROG:%=$(ROOTUSRSBIN)/%) +CLOBBERFILES += $(RPROG) $(LPROG) $(BACKUP_PROG) $(RESTORE_PROG)\ + $(DERIVED_FILES) + +PROTOCOL_DIR= $(ROOT)/usr/include/rpcsvc +CPPFLAGS += -I$(SRC)/lib/libnsl/include -I$(SRC)/lib/libnisdb +LDLIBS += -lnsl util.a +nispasswd := LDLIBS = $(LDLIBS.cmd) -lnsl + +LDLIBS += -lc + +.KEEP_STATE: + +all: $(DERIVED_FILES) util.a $(SUBDIRS) $(PROG) $(RPROG) $(LPROG) \ + $(BKRSTPROG) + +install: all $(SUBDIRS) $(ROOTPROG) $(ROOTNSPROG) $(ROOTLPROG) $(ROOTUSBPROG) + +clean: + $(RM) $(OBJS) $(ROBJS) $(UTILOBJS) $(BACKUP_OBJS) $(RESTORE_OBJS) util.a + +lint: lint_SRCS + +nispasswd: + @$(ECHO) "\`\`nispasswd'' is now a link to \`\`passwd''." + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +# +# build an archive library so that those programs that need stuff can +# get it, but others won't link in unneeded routines. +# + +util.a: $(UTILOBJS) + $(AR) -crv $@ $(UTILOBJS) + +$(PROG:%=%): $$@.o util.a + $(LINK.c) -o $@ $@.o $(LDLIBS) + $(POST_PROCESS) + +$(LPROG): nischmod + -$(RM) $@ ; $(LN) nischmod $@ + +$(ROOTLPROG): $(ROOTBIN)/nischmod + -$(RM) $@ ; $(LN) $(ROOTBIN)/nischmod $@ + +$(ROOTNSLIB): + $(INS.dir) + +$(ROOTNSLIB)/%: % $(ROOTNSLIB) + $(INS.file) + +$(RESTORE_PROG): $(DERIVED_FILES) $(RESTORE_OBJS) util.a + $(LINK.cc) -o $@ $(RESTORE_OBJS) $(LDLIBS) -lnisdb + $(POST_PROCESS) + +$(BACKUP_PROG): $(DERIVED_FILES) $(BACKUP_OBJS) util.a + $(LINK.cc) -o $@ $(BACKUP_OBJS) $(LDLIBS) -lnisdb + $(POST_PROCESS) + +nis_log_common.o: ../rpc.nisd/nis_log_common.c $(DERIVED_FILES) + $(COMPILE.c) ../rpc.nisd/nis_log_common.c + $(POST_PROCESS_O) + +nis_mt.o: ../rpc.nisd/nis_mt.c $(DERIVED_FILES) + $(COMPILE.c) ../rpc.nisd/nis_mt.c + $(POST_PROCESS_O) + +nis_cleanup.o: ../rpc.nisd/nis_cleanup.c $(DERIVED_FILES) + $(COMPILE.c) ../rpc.nisd/nis_cleanup.c + $(POST_PROCESS_O) + +# +# Rules for building the derived files : +# +$(DERIVED_FILES): $(PROTOCOL_DIR)/nis.x $(PROTOCOL_DIR)/nis_object.x + $(RPCGEN) -C -h $(PROTOCOL_DIR)/nis.x |\ + sed -n -e '/EDIT_START/,$$ p' |\ + sed -e 's/_2_svc/_svc/' |\ + sed -e 's/_3_svc/_svc/' |\ + sed -e 's/_3/_clnt/' |\ + sed -e 's/_2/_clnt/' >../rpc.nisd/nis_svc.h + +include ../../../Makefile.targ + +FRC: diff --git a/usr/src/cmd/rpcsvc/nis/utils/nis_bkrst.h b/usr/src/cmd/rpcsvc/nis/utils/nis_bkrst.h new file mode 100644 index 0000000000..aba79e1049 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nis_bkrst.h @@ -0,0 +1,89 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1996 Sun Microsystems, Inc. + */ + +/* + * Definitions for nisbackup(1M) and nisrestore(1M). These are private + * definitions and may change without notice. + */ + +#ifndef _NIS_BKRST_H +#define _NIS_BKRST_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +extern int optind; +extern char *optarg; + +#if defined(__STDC__) || defined(__cplusplus) + +extern bool_t db_copy_file(char *, char *); +extern bool_t db_extract_dict_entries(char *, char **, u_int); +extern bool_t db_in_dict_file(char *); +extern db_status db_begin_merge_dict(char *, char *, char *); +extern db_status db_end_merge_dict(); +extern db_status db_abort_merge_dict(); +extern char *db_perror(db_status); +extern void sync_header(); + +#else /* Non-prototype definitions */ + +extern bool_t db_copy_file(); +extern bool_t db_extract_dict_entries(); +extern bool_t db_in_dict_file(); +extern db_status db_begin_merge_dict(); +extern db_status db_end_merge_dict(); +extern db_status db_abort_merge_dict(); +extern char *db_perror(); +extern void sync_header(); + +#endif /* defined(__STDC__) || defined(__cplusplus) */ + +/* + * verbose is must be defined for nis_log_common, but is not used, since + * the trans.log merge routines output way too much verbiage. Hence, the + * static flag, verbiage. + */ +bool_t verbose = FALSE; + +/* + * BACKUPREV is the current revision number for the backup snap shot. If + * something changes with the NIS+ database or the nisbackup/nisrestore + * snapshot, which effects backwards compatibility, this needs to be rev'd + */ +#define BACKUPREV 0xBACDEF01 +#define BACKUPLIST "backup_list" +#define LASTUPDATE "last.upd" +#define MAXRETRIES 10 /* Max number of retries for contacting the server */ +#define DOMAIN_ALL 1 /* Mask for -a option */ + +#ifdef __cplusplus +} +#endif + +#endif /* _NIS_BKRST_H */ diff --git a/usr/src/cmd/rpcsvc/nis/utils/nis_util.c b/usr/src/cmd/rpcsvc/nis/utils/nis_util.c new file mode 100644 index 0000000000..f1907da713 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nis_util.c @@ -0,0 +1,869 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nis_util.c + * + * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <rpcsvc/nis.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <pwd.h> +#include <shadow.h> +#include <stdlib.h> + +/* + * nisname_index() + * + * Return pointer to place in string s of first match with char c. + * Do not match between quotes ("..."). + * + * Totally rewritten as part of the fix for bug# 1263305. + * XXX - This can now probably be replaced by strchr_quotes(). + */ +char * +nisname_index(char *s, char c) +{ + do { + if (*s == '"') { + s++; + while (*s && *s != '"') + s++; + } else if (*s == c) + return (s); + } while (*s++); + return (NULL); +} + +/* + * Parse a passed name into a basename and search criteria. + * if there is no criteria present the *crit == 0. You must + * pass in allocated data for the three strings. + * + * Returns 0 if successful, non-zero otherwise. + */ +int +nisname_split(name, base, crit, max_len) + char *name; + char *base; + char *crit; + int max_len; +{ + register char *p, *q; + + p = name; + while (*p && (isspace(*p))) + p++; + if (*p != '[') { + *crit = 0; + return (strlcpy(base, p, max_len) >= max_len); + } + + /* it has a criteria, copy the whole thing in */ + if (strlcpy(crit, p, max_len) >= max_len) + return (1); + q = nisname_index(crit, ']'); + if (! q) { + *crit = 0; /* error condition */ + *base = 0; + return (1); + } + q++; + if (*q == ',') { + *q = 0; + q++; + } + if (strlcpy(base, q, max_len) >= max_len) + return (1); + *q = 0; /* just in case there wasn't a comma */ + + return (0); +} + +bool_t +nis_verifycred(n, flags) + nis_name n; + uint_t flags; +{ + nis_result *res; + int err; + char dname[NIS_MAXNAMELEN]; + + (void) snprintf(dname, sizeof (dname), "[cname=%s],cred.org_dir.%s", n, + nis_domain_of(n)); + res = nis_list(dname, flags, NULL, NULL); + err = (res->status == NIS_SUCCESS); + nis_freeresult(res); + return (err); +} + +#define NIS_ALL_ACC (NIS_READ_ACC|NIS_MODIFY_ACC|NIS_CREATE_ACC|NIS_DESTROY_ACC) + +static int +parse_rights_field(rights, shift, p) + uint_t *rights; + int shift; + char *p; +{ + int set; + + while (*p && (*p != ',')) { + switch (*p) { + case '=': + *rights &= ~(NIS_ALL_ACC << shift); + case '+': + set = 1; + break; + case '-': + set = 0; + break; + default: + return (0); + } + for (p++; *p && (*p != ',') && (*p != '=') && (*p != '+') && + (*p != '-'); p++) { + switch (*p) { + case 'r': + if (set) + *rights |= (NIS_READ_ACC << shift); + else + *rights &= ~(NIS_READ_ACC << shift); + break; + case 'm': + if (set) + *rights |= (NIS_MODIFY_ACC << shift); + else + *rights &= ~(NIS_MODIFY_ACC << shift); + break; + case 'c': + if (set) + *rights |= (NIS_CREATE_ACC << shift); + else + *rights &= ~(NIS_CREATE_ACC << shift); + break; + case 'd': + if (set) + *rights |= (NIS_DESTROY_ACC << shift); + else + *rights &= ~(NIS_DESTROY_ACC << shift); + break; + default: + return (0); + } + } + } + return (1); +} + +#define NIS_NOBODY_FLD 1 +#define NIS_OWNER_FLD 2 +#define NIS_GROUP_FLD 4 +#define NIS_WORLD_FLD 8 +#define NIS_ALL_FLD NIS_OWNER_FLD|NIS_GROUP_FLD|NIS_WORLD_FLD + +int +parse_rights(rights, p) + uint_t *rights; + char *p; +{ + uint_t f; + + if (p) + while (*p) { + for (f = 0; (*p != '=') && (*p != '+') && (*p != '-'); + p++) + switch (*p) { + case 'n': + f |= NIS_NOBODY_FLD; + break; + case 'o': + f |= NIS_OWNER_FLD; + break; + case 'g': + f |= NIS_GROUP_FLD; + break; + case 'w': + f |= NIS_WORLD_FLD; + break; + case 'a': + f |= NIS_ALL_FLD; + break; + default: + return (0); + } + if (f == 0) + f = NIS_ALL_FLD; + + if ((f & NIS_NOBODY_FLD) && + !parse_rights_field(rights, 24, p)) + return (0); + + if ((f & NIS_OWNER_FLD) && + !parse_rights_field(rights, 16, p)) + return (0); + + if ((f & NIS_GROUP_FLD) && + !parse_rights_field(rights, 8, p)) + return (0); + + if ((f & NIS_WORLD_FLD) && + !parse_rights_field(rights, 0, p)) + return (0); + + while (*(++p)) + if (*p == ',') { + p++; + break; + } + } + return (1); +} + + +int +parse_flags(flags, p) + uint_t *flags; + char *p; +{ + if (p) { + while (*p) { + switch (*(p++)) { + case 'B': + *flags |= TA_BINARY; + break; + case 'X': + *flags |= TA_XDR; + break; + case 'S': + *flags |= TA_SEARCHABLE; + break; + case 'I': + *flags |= TA_CASE; + break; + case 'C': + *flags |= TA_CRYPT; + break; + default: + return (0); + } + } + return (1); + } else { + fprintf(stderr, + "Invalid table schema: At least one column must be searchable.\n"); + exit(1); + } +} + + +int +parse_time(time, p) + uint32_t *time; + char *p; +{ + char *s; + uint32_t x; + + *time = 0; + + if (p) + while (*p) { + if (!isdigit(*p)) + return (0); + x = strtol(p, &s, 10); + switch (*s) { + case '\0': + (*time) += x; + p = s; + break; + case 's': + case 'S': + (*time) += x; + p = s+1; + break; + case 'm': + case 'M': + (*time) += x*60; + p = s+1; + break; + case 'h': + case 'H': + (*time) += x*(60*60); + p = s+1; + break; + case 'd': + case 'D': + (*time) += x*(24*60*60); + p = s+1; + break; + default: + return (0); + } + } + + return (1); +} + + +static int +nis_getsubopt(optionsp, tokens, sep, valuep) + char **optionsp; + char * const *tokens; + const int sep; /* if this is a char we get an alignment error */ + char **valuep; +{ + register char *s = *optionsp, *p, *q; + register int i, optlen; + + *valuep = NULL; + if (*s == '\0') + return (-1); + q = strchr(s, (char)sep); /* find next option */ + if (q == NULL) { + q = s + strlen(s); + } else { + *q++ = '\0'; /* mark end and point to next */ + } + p = strchr(s, '='); /* find value */ + if (p == NULL) { + optlen = strlen(s); + *valuep = NULL; + } else { + optlen = p - s; + *valuep = ++p; + } + for (i = 0; tokens[i] != NULL; i++) { + if ((optlen == strlen(tokens[i])) && + (strncmp(s, tokens[i], optlen) == 0)) { + /* point to next option only if success */ + *optionsp = q; + return (i); + } + } + /* no match, point value at option and return error */ + *valuep = s; + return (-1); +} + + +nis_object nis_default_obj; + +/* + * We record the source of the defaults. + * 0 => default + * 1 => from NIS_DEFAULTS env variable + * 2 => from arg passed to nis_defaults_init + */ +#define NIS_SRC_DEFAULT 0 +#define NIS_SRC_ENV 1 +#define NIS_SRC_ARG 2 + +int nis_default_owner_src = NIS_SRC_DEFAULT; +int nis_default_group_src = NIS_SRC_DEFAULT; +int nis_default_access_src = NIS_SRC_DEFAULT; +int nis_default_ttl_src = NIS_SRC_DEFAULT; + +static char *nis_defaults_tokens[] = { + "owner", + "group", + "access", + "ttl", + 0 +}; + +#define T_OWNER 0 +#define T_GROUP 1 +#define T_ACCESS 2 +#define T_TTL 3 + +static int +nis_defaults_set(optstr, src) + char *optstr; + int src; +{ + char str[1024], *p, *v; + int i; + + if (strlcpy(str, optstr, sizeof (str)) >= sizeof (str)) + return (0); + p = str; + + while ((i = nis_getsubopt(&p, nis_defaults_tokens, ':', &v)) != -1) { + switch (i) { + case T_OWNER: + if (v == 0 || v[strlen(v)-1] != '.') + return (0); + nis_default_obj.zo_owner = strdup(v); + nis_default_owner_src = src; + break; + case T_GROUP: + if (v == 0 || v[strlen(v)-1] != '.') + return (0); + nis_default_obj.zo_group = strdup(v); + nis_default_group_src = src; + break; + case T_ACCESS: + if ((v == 0) || + (!parse_rights(&(nis_default_obj.zo_access), v))) + return (0); + nis_default_access_src = src; + break; + case T_TTL: + if ((v == 0) || + !(parse_time(&(nis_default_obj.zo_ttl), v))) + return (0); + nis_default_ttl_src = src; + break; + } + } + + if (*p) + return (0); + + return (1); +} + +extern char *getenv(); + +int +nis_defaults_init(optstr) + char *optstr; +{ + char *envstr; + + /* XXX calling this multiple times may leak memory */ + memset((char *)&nis_default_obj, 0, sizeof (nis_default_obj)); + + nis_default_obj.zo_owner = nis_local_principal(); + nis_default_obj.zo_group = nis_local_group(); + nis_default_obj.zo_access = DEFAULT_RIGHTS; + nis_default_obj.zo_ttl = 12 * 60 * 60; + + if (envstr = getenv("NIS_DEFAULTS")) + if (!nis_defaults_set(envstr, NIS_SRC_ENV)) { + fprintf(stderr, + "can't parse NIS_DEFAULTS environment variable.\n"); + return (0); + } + + if (optstr) + if (!nis_defaults_set(optstr, NIS_SRC_ARG)) { + fprintf(stderr, "can't parse nis_defaults argument.\n"); + return (0); + } + + return (1); +} + + + +/* + * Converts an NIS+ entry object for a passwd table to its + * pwent structure. + * XXX: This function returns a pointer to a static structure. + */ +static struct passwd * +nis_object_to_pwent(obj, error) + nis_object *obj; + nis_error *error; +{ + static struct passwd pw; + static char spacebuf[1024]; /* The pwent structure points to this */ + static char nullstring; /* used for NULL data */ + char *tmp; + char *end; + + memset((void *)&pw, 0, sizeof (struct passwd)); + memset((void *)&spacebuf[0], 0, 1024); + tmp = &spacebuf[0]; + end = tmp + sizeof (spacebuf); + + if ((obj->zo_data.zo_type != NIS_ENTRY_OBJ) || + (obj->EN_data.en_cols.en_cols_len < 8)) { + *error = NIS_INVALIDOBJ; + return (NULL); + } + if (ENTRY_LEN(obj, 0) == 0) { + *error = NIS_INVALIDOBJ; + return (NULL); + } else { + (void) strlcpy(tmp, ENTRY_VAL(obj, 0), + end > tmp ? end - tmp : 0); + pw.pw_name = tmp; + tmp += strlen(pw.pw_name) + 1; + } + + if (ENTRY_LEN(obj, 1) == 0) { + pw.pw_passwd = &nullstring; + } else { + /* XXX: Should I be returning X here? */ + (void) strlcpy(tmp, ENTRY_VAL(obj, 1), + end > tmp ? end - tmp : 0); + pw.pw_passwd = tmp; + tmp += strlen(pw.pw_passwd) + 1; + } + + if (ENTRY_LEN(obj, 2) == 0) { + *error = NIS_INVALIDOBJ; + return (NULL); + } + pw.pw_uid = atoi(ENTRY_VAL(obj, 2)); + + if (ENTRY_LEN(obj, 3) == 0) + pw.pw_gid = 0; /* Is this default value? */ + else + pw.pw_gid = atoi(ENTRY_VAL(obj, 3)); + + if (ENTRY_LEN(obj, 4) == 0) { + pw.pw_gecos = &nullstring; + } else { + (void) strlcpy(tmp, ENTRY_VAL(obj, 4), + end > tmp ? end - tmp : 0); + pw.pw_gecos = tmp; + tmp += strlen(pw.pw_gecos) + 1; + } + + if (ENTRY_LEN(obj, 5) == 0) { + pw.pw_dir = &nullstring; + } else { + (void) strlcpy(tmp, ENTRY_VAL(obj, 5), + end > tmp ? end - tmp : 0); + pw.pw_dir = tmp; + tmp += strlen(pw.pw_dir) + 1; + } + + if (ENTRY_LEN(obj, 6) == 0) { + pw.pw_shell = &nullstring; + } else { + (void) strlcpy(tmp, ENTRY_VAL(obj, 6), + end > tmp ? end - tmp : 0); + pw.pw_shell = tmp; + tmp += strlen(pw.pw_shell) + 1; + } + + pw.pw_age = &nullstring; + pw.pw_comment = &nullstring; + *error = NIS_SUCCESS; + return (&pw); +} + + +/* + * This will go to the NIS+ master to get the data. This code + * is ugly because the internals of the switch had to be opened + * up here. Wish there was a way to pass a MASTER_ONLY flag + * to getpwuid() and all such getXbyY() calls. Some of this code + * is being copied from the NIS+ switch backend. + * + * XXX: We will not bother to make this MT-safe. If any of the callers + * for this function want to use getpwuid_r(), then a corresponding + * function will have to written. + */ +struct passwd * +getpwuid_nisplus_master(domain, uid, error) + char *domain; + uid_t uid; + nis_error *error; +{ + struct passwd *passwd_ent; + nis_result *res; + char namebuf[NIS_MAXNAMELEN]; + uint_t flags; + + (void) snprintf(namebuf, sizeof (namebuf), + "[uid=%ld],passwd.org_dir.%s", uid, domain); + flags = EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH|USE_DGRAM|MASTER_ONLY; + res = nis_list(namebuf, flags, 0, 0); + if (res == NULL) { + *error = NIS_NOMEMORY; + return (NULL); + } + if (res->status != NIS_SUCCESS) { + nis_freeresult(res); + *error = res->status; + return (NULL); + } + if (NIS_RES_NUMOBJ(res) == 0) { + nis_freeresult(res); + *error = NIS_NOTFOUND; + return (NULL); + } + + passwd_ent = nis_object_to_pwent(NIS_RES_OBJECT(res), error); + nis_freeresult(res); + return (passwd_ent); +} + +/* + * Converts an NIS+ entry object for a shadow table to its + * spwent structure. We only fill in the sp_namp and sp_pwdp fields. + * XXX: This function returns a pointer to a static structure. + */ +static struct spwd * +nis_object_to_spwent(obj, error) + nis_object *obj; + nis_error *error; +{ + static struct spwd spw; + static char spacebuf[1024]; /* The pwent structure points to this */ + static char nullstring; /* used for NULL data */ + char *tmp; + char *end; + + memset((void *)&spw, 0, sizeof (struct spwd)); + memset((void *)&spacebuf[0], 0, 1024); + tmp = &spacebuf[0]; + end = tmp + sizeof (spacebuf); + + if ((obj->zo_data.zo_type != NIS_ENTRY_OBJ) || + (obj->EN_data.en_cols.en_cols_len < 8)) { + *error = NIS_INVALIDOBJ; + return (NULL); + } + if (ENTRY_LEN(obj, 0) == 0) { + *error = NIS_INVALIDOBJ; + return (NULL); + } else { + (void) strlcpy(tmp, ENTRY_VAL(obj, 0), + end > tmp ? end - tmp : 0); + spw.sp_namp = tmp; + tmp += strlen(spw.sp_namp) + 1; + } + + if (ENTRY_LEN(obj, 1) == 0) { + spw.sp_pwdp = &nullstring; + } else { + (void) strlcpy(tmp, ENTRY_VAL(obj, 1), + end > tmp ? end - tmp : 0); + spw.sp_pwdp = tmp; + tmp += strlen(spw.sp_pwdp) + 1; + } + + *error = NIS_SUCCESS; + return (&spw); +} + + +/* + * This will go to the NIS+ master to get the data. This code + * is ugly because the internals of the switch had to be opened + * up here. Wish there was a way to pass a MASTER_ONLY flag + * to getspnam() and all such getXbyY() calls. Some of this code + * is being copied from the NIS+ switch backend. + * + * XXX: We will not bother to make this MT-safe. If any of the callers + * for this function want to use getpwuid_r(), then a corresponding + * function will have to written. + */ +struct spwd * +getspnam_nisplus_master(domain, name, error) + char *domain; + char *name; + nis_error *error; +{ + struct spwd *shadow_ent; + nis_result *res; + char namebuf[NIS_MAXNAMELEN]; + uint_t flags; + + (void) snprintf(namebuf, sizeof (namebuf), + "[name=%s],passwd.org_dir.%s", name, domain); + flags = EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH|USE_DGRAM|MASTER_ONLY; + res = nis_list(namebuf, flags, 0, 0); + if (res == NULL) { + *error = NIS_NOMEMORY; + return (NULL); + } + if (res->status != NIS_SUCCESS) { + nis_freeresult(res); + *error = res->status; + return (NULL); + } + if (NIS_RES_NUMOBJ(res) == 0) { + nis_freeresult(res); + *error = NIS_NOTFOUND; + return (NULL); + } + + shadow_ent = nis_object_to_spwent(NIS_RES_OBJECT(res), error); + nis_freeresult(res); + return (shadow_ent); +} + +/* begin bug# 1263305 */ + +/* + * __nis_quote_key() + * + * Enclose NIS+ terminating characters ']' and ',' within '"' and + * escape '"' by doubling it so the string is safe to pass to + * nis_list(3N) and associated routines. For example, a src string + * of "foo[]bar" will result in a dst string of ""foo["]"bar"" + */ +char * +__nis_quote_key(const char *src, char *dst, int dstsize) +{ + + char *dstorig = dst; + int dstleft; + + dstleft = dstsize - 4; + for (; *src != '\0' && dstleft > 0; src++) { + switch (*src) { + case ']': + case ',': + /* quote the character */ + *dst++ = '"'; + *dst++ = *src; + *dst++ = '"'; + dstleft -= 3; + break; + + case '"': + /* double the quote */ + *dst++ = '"'; + dstleft--; + /* fall through... */ + + default: + *dst++ = *src; + dstleft--; + break; + } + } + *dst = '\0'; + + if (*src) + fprintf(stderr, + "nis_quote_key: warning: src string too long\n"); + + return (dstorig); +} + +/* + * *str* routines below originated in libc. + */ + +/* + * strpbrk_quotes() + * + * Like strpbrk except it will not match target chars inside quoted + * strings ("..."). + */ +char * +strpbrk_quotes(char *string, char *brkset) +{ + register const char *p; + + do { + for (p = brkset; *p != '\0' && *string != '"' && *p != *string; + ++p) + ; + + if (*string == '"') { + string++; + while (*string != '\0' && *string != '"') + string++; + } else if (*p != '\0') + return ((char *)string); + } + while (*string++); + return (NULL); +} + + +/* + * strtok_r_quotes() + * + * uses strpbrk_quotes and strspn to break string into tokens on + * sequentially subsequent calls. returns NULL when no + * non-separator characters remain. + * `subsequent' calls are calls with first argument NULL. + * + */ + +static char * +strtok_r_quotes(char *string, char *sepset, char **lasts) +{ + char *q, *r; + + /* first or subsequent call */ + if (string == NULL) + string = *lasts; + + if (string == 0) /* return if no tokens remaining */ + return (NULL); + + q = string + strspn(string, sepset); /* skip leading separators */ + + if (*q == '\0') /* return if no tokens remaining */ + return (NULL); + + if ((r = strpbrk_quotes(q, sepset)) == NULL) /* move past token */ + *lasts = 0; /* indicate this is last token */ + else { + *r = '\0'; + *lasts = r+1; + } + return (q); +} + + +/* + * strtok_quotes() + * + * Like strtok except it will not match target chars within quoted + * strings ("..."). + */ +char * +strtok_quotes(char *string, char *sepset) +{ + static char *lasts; + + return (strtok_r_quotes(string, sepset, &lasts)); +} + +/* + * strchr_quotes() + * + * Like strchr but will not match within quoted strings ("..."). + */ +char * +strchr_quotes(char *s, char c) +{ + do { + if (*s == '"') { + s++; + while (*s && *s != '"') + s++; + } else if (*s == c) + return (s); + } while (*s++); + return (NULL); +} + +/* end bug# 1263305 */ diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/Makefile b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/Makefile new file mode 100644 index 0000000000..4157496a81 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/Makefile @@ -0,0 +1,65 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# Makefile for the nisaddcred program +# + +PROG= nisaddcred + +SRCS= \ + nisaddcred.c makelocalcred.c makedescred.c \ + makersacred.c makekerbcred.c makedhextcred.c +OBJS= \ + nisaddcred.o makelocalcred.o makedescred.o \ + makersacred.o makekerbcred.o makedhextcred.o +UTIL= ../util.a + +include $(SRC)/cmd/Makefile.cmd + +CPPFLAGS += -I$(SRC)/lib/libnsl/include +CFLAGS += -I$(ROOT)/usr/include +LDLIBS += -lnsl $(UTIL) + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTPROG) + +lint: lint_SRCS + +clean: + $(RM) $(OBJS) + +$(UTIL): + @cd ..; pwd; $(MAKE) util.a + +$(PROG): $(OBJS) $(UTIL) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedescred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedescred.c new file mode 100644 index 0000000000..106df31888 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedescred.c @@ -0,0 +1,288 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * makedescred.c + * + * Copyright (c) 1988-1992,2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * makedescred.c + * + * Make a "AUTH_DES" credential. This is the old secure rpc credentials from + * SunOS 4.0 and Vanilla System V release 4.0. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <pwd.h> +#include <shadow.h> +#include <string.h> +#include <ctype.h> +#include <nsswitch.h> +#include <netdb.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/nis_dhext.h> +#include <rpc/key_prot.h> +#include "nisaddcred.h" +#include <assert.h> + +extern char *getpass(); +extern char *crypt(); +extern void __gen_dhkeys(char *, char *, char *); +extern int add_cred_obj(nis_object *, char *); +extern int check_switch_policy(char *, char *, struct __nsw_switchconfig *, + char *, char *); +extern nis_error cred_exists(char *, char *, char *); +extern char *get_password(uid_t, int, char *, char *); +extern int is_switch_policy(struct __nsw_switchconfig *, char *); +extern int key_setnet(struct key_netstarg *arg); +extern int make_dhext_cred(char *, char *, char *, char *); +extern int modify_cred_obj(nis_object *, char *); +extern int no_switch_policy(struct __nsw_switchconfig *); +extern int sanity_checks(char *, char *, char *, char *); +extern char *switch_policy_str(struct __nsw_switchconfig *); +extern void write_rootkey(char *, char *, keylen_t, algtype_t); +extern int xencrypt(char *, char *); + +static const char *OPSYS = "unix"; +#define OPSYS_LEN 4 + +/* ************************ switch functions *************************** */ + +/* NSW_NOTSUCCESS NSW_NOTFOUND NSW_UNAVAIL NSW_TRYAGAIN */ +#define DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE} + + +/* ***************************** keylogin stuff *************************** */ +int +keylogin_des(char *netname, char *secret) +{ + struct key_netstarg netst; + + netst.st_pub_key[0] = 0; + memcpy(netst.st_priv_key, secret, HEXKEYBYTES); + netst.st_netname = netname; + +#ifdef NFS_AUTH + nra.authtype = AUTH_DES; /* only revoke DES creds */ + nra.uid = getuid(); /* use the real uid */ + if (_nfssys(NFS_REVAUTH, &nra) < 0) { + perror("Warning: NFS credentials not destroyed"); + err = 1; + } +#endif /* NFS_AUTH */ + + + /* do actual key login */ + if (key_setnet(&netst) < 0) { + fprintf(stderr, "Could not set %s's secret key\n", netname); + fprintf(stderr, "May be the keyserv is down?\n"); + return (0); + } + + return (1); +} + + +/* + * Definitions of the credential table. + * + * Column Name Contents + * ------ ---- -------- + * 0 cname nis principal name + * 1 auth_type DES + * 2 auth_name netname + * 3 public_auth_data public key + * 4 private_auth_data encrypted secret key with checksum + */ + +/* + * Function for building DES credentials. + * + * The domain may be the local domain or some remote domain. + * 'domain' should be the same as the domain found in netname, + * which should be the home domain of nis+ principal. + */ + + +int +make_des_cred_be(char *nis_princ, char *netname, char *domain) +{ + nis_object *obj = init_entry(); + char *pass; + uid_t uid; + char public[HEXKEYBYTES + 1]; + char secret[HEXKEYBYTES + 1]; + char crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE + 1]; + char target_host[MAXHOSTNAMELEN+1]; + int same_host = 0; + int status, len, addition; + + if (nis_princ == NULL) + nis_princ = default_principal(domain); + + if (sanity_checks(nis_princ, netname, domain, "DES") == 0) + return (0); + + addition = (cred_exists(nis_princ, "DES", domain) == NIS_NOTFOUND); + + /* Extract user/host information from netname */ + if (! isdigit(netname[OPSYS_LEN+1])) { + uid = 0; /* root */ + netname2host(netname, target_host, MAXHOSTNAMELEN); + len = strlen(my_host)-1; /* ignore trailing dot in my_host */ + if (len == strlen(target_host) && + strncasecmp(target_host, my_host, len) == 0) + same_host = 1; + } else { + uid = (uid_t)atoi(netname+OPSYS_LEN+1); + } + + pass = get_password(uid, same_host, target_host, domain); + if (pass == 0) + return (0); + + /* Get password with which to encrypt secret key. */ + (void) printf("%s key pair for %s (%s).\n", + addition? "Adding" : "Updating", netname, nis_princ); + + + /* Encrypt secret key */ + (void) __gen_dhkeys(public, secret, pass); + memcpy(crypt1, secret, HEXKEYBYTES); + memcpy(crypt1 + HEXKEYBYTES, secret, KEYCHECKSUMSIZE); + crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0; + xencrypt(crypt1, pass); + + + /* Now we have a key pair, build up the cred entry */ + ENTRY_VAL(obj, 0) = nis_princ; + ENTRY_LEN(obj, 0) = strlen(nis_princ) + 1; + + ENTRY_VAL(obj, 1) = "DES"; + ENTRY_LEN(obj, 1) = 4; + + ENTRY_VAL(obj, 2) = netname; + ENTRY_LEN(obj, 2) = strlen(netname) + 1; + + ENTRY_VAL(obj, 3) = public; + ENTRY_LEN(obj, 3) = strlen(public) + 1; +#ifdef OLD_MODE + strcat(ENTRY_VAL(obj, 3), ":"); + ENTRY_LEN(obj, 3)++; +#endif + + ENTRY_VAL(obj, 4) = crypt1; + ENTRY_LEN(obj, 4) = strlen(crypt1) + 1; + + if (addition) { + obj->zo_owner = nis_princ; + obj->zo_group = my_group; + obj->zo_domain = domain; + /* owner: r, group: rmcd */ + obj->zo_access = ((NIS_READ_ACC<<16)| + (NIS_READ_ACC|NIS_MODIFY_ACC|NIS_CREATE_ACC| + NIS_DESTROY_ACC)<<8); + status = add_cred_obj(obj, domain); + } else { + obj->EN_data.en_cols.en_cols_val[3].ec_flags |= EN_MODIFIED; + obj->EN_data.en_cols.en_cols_val[4].ec_flags |= EN_MODIFIED; + status = modify_cred_obj(obj, domain); + } + + + /* attempt keylogin if appropriate */ + if (status) { + if ((uid == my_uid) && ((uid != 0) || same_host)) + keylogin_des(netname, secret); + if ((uid == 0) && same_host) + write_rootkey(secret, "des", 192, 0); + } + return (status); +} + + +int +make_des_cred(char *nis_princ, char *netname, char *domain, char *flavor) +{ + mechanism_t **mechlist; + int status = 0; + int i = 0; + + if (mechlist = (mechanism_t **)__nis_get_mechanisms(FALSE)) { + while (mechlist[i]) { + status = make_dhext_cred(nis_princ, netname, + domain, + mechlist[i]->alias); + if (!status) + return (status); + i++; + } + } else + status = make_des_cred_be(nis_princ, netname, domain); + + return (status); +} + + +char * +get_des_cred(domain, flavor) +char *domain; +char *flavor; /* Ignored. */ +{ + int uid, status; + static char netname[MAXNETNAMELEN+1]; + + uid = my_uid; + + if (uid == 0) + status = host2netname(netname, (char *)NULL, domain); + else { + /* generate netname using uid and domain information. */ + int len; + len = strlen(domain); + if ((len + OPSYS_LEN + 3 + MAXIPRINT) > MAXNETNAMELEN) { + printf("Domain name too long: \"%s\"\n", domain); + goto not_found; + } + (void) sprintf(netname, "%s.%d@%s", OPSYS, uid, domain); + len = strlen(netname); + if (netname[len-1] == '.') + netname[len-1] = '\0'; + + status = 1; + } + + if (status == 1) { + printf("DES principal name : \"%s\"\n", netname); + return (netname); + } + +not_found: + printf("DES principal name for %d not found\n", uid); + return (NULL); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedhextcred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedhextcred.c new file mode 100644 index 0000000000..ff8a432863 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedhextcred.c @@ -0,0 +1,677 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * makedescred.c + * + * Copyright (c) 1997 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * makedescred.c + * + * Make extended diffie-hellman GSS credential. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <pwd.h> +#include <shadow.h> +#include <nsswitch.h> +#include <netdb.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/nis_dhext.h> +#include <rpc/key_prot.h> +#include "nisaddcred.h" + +extern char *getpass(); +extern char *crypt(); +extern int add_cred_obj(nis_object *, char *); +extern nis_error auth_exists(char *, char *, char *, char *); +extern nis_error cred_exists(char *, char *, char *); +extern int keylogin_des(char *, char *); +extern int make_des_cred_be(char *, char *, char *); +extern int modify_cred_obj(nis_object *, char *); + +static int force = 1; /* Eventually, this will be an option */ + +static const char *OPSYS = "unix"; +#define OPSYS_LEN 4 +#define ROOTKEY_FILE "/etc/.rootkey" + +/* ************************ switch functions *************************** */ + +/* NSW_NOTSUCCESS NSW_NOTFOUND NSW_UNAVAIL NSW_TRYAGAIN */ +#define DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE} + +static struct __nsw_lookup lookup_files = {"files", DEF_ACTION, NULL, NULL}, + lookup_nis = {"nis", DEF_ACTION, NULL, &lookup_files}; +static struct __nsw_switchconfig publickey_default = + {0, "publickey", 2, &lookup_nis}; + + +char * +switch_policy_str(struct __nsw_switchconfig *conf) +{ + struct __nsw_lookup *look; + static char policy[256]; + int previous = 0; + + memset((char *)policy, 0, 256); + + for (look = conf->lookups; look; look = look->next) { + if (previous) + strcat(policy, " "); + strcat(policy, look->service_name); + previous = 1; + } + + return (policy); +} + + +int +no_switch_policy(struct __nsw_switchconfig *conf) +{ + return (conf == NULL || conf->lookups == NULL); +} + + +int +is_switch_policy(struct __nsw_switchconfig *conf, char *target) +{ + return (conf && conf->lookups && + strcmp(conf->lookups->service_name, target) == 0 && + conf->lookups->next == NULL); +} + + +int +check_switch_policy(char *policy, char *target_service, + struct __nsw_switchconfig *default_conf, + char *head_msg, char *tail_msg) +{ + struct __nsw_switchconfig *conf; + enum __nsw_parse_err perr; + int policy_correct = 1; + + conf = __nsw_getconfig(policy, &perr); + if (no_switch_policy(conf)) { + if (!is_switch_policy(default_conf, target_service)) { + fprintf(stderr, + "\n%s\n There is no publickey entry in %s.\n", + head_msg, __NSW_CONFIG_FILE); + fprintf(stderr, + " The default publickey policy is \"publickey: %s\".\n", + switch_policy_str(default_conf)); + policy_correct = 0; + } + } else if (!is_switch_policy(conf, target_service)) { + fprintf(stderr, + "\n%s\n The publickey entry in %s is \"publickey: %s\".\n", + head_msg, __NSW_CONFIG_FILE, + switch_policy_str(conf)); + policy_correct = 0; + } + /* should we exit ? */ + if (policy_correct == 0) + fprintf(stderr, + " It should be \"publickey: %s\"%s.\n\n", + target_service, tail_msg); + if (conf) + __nsw_freeconfig(conf); + + return (policy_correct); +} + +/* ******************************************************************** */ +/* Check that data to be entered makes sense */ +int +sanity_checks(char *nis_princ, char *netname, char *domain, char *authtype) +{ + char netdomainaux[MAXHOSTNAMELEN+1]; + char *princdomain, *netdomain; + + /* Sanity check 0. Do we have a nis+ principal name to work with? */ + if (nis_princ == NULL) { + fprintf(stderr, + "%s: you must create a \"local\" credential first.\n", + program_name); + fprintf(stderr, + "rerun this command as : %s local \n", program_name); + return (0); + } + + /* Sanity check 1. We only deal with one type of netnames. */ + if (strncmp(netname, OPSYS, OPSYS_LEN) != 0) { + fprintf(stderr, "%s: unrecognized netname type: '%s'.\n", + program_name, netname); + return (0); + } + + /* Sanity check 2. Should only add DES cred in home domain. */ + princdomain = nis_domain_of(nis_princ); + if (strcasecmp(princdomain, domain) != 0) { + fprintf(stderr, +"%s: domain of principal '%s' does not match destination domain '%s'.\n", + program_name, nis_princ, domain); + fprintf(stderr, + "Should only add DES credential of principal in its home domain\n"); + return (0); + } + + /* + * Sanity check 3: Make sure netname's domain same as principal's + * and don't have extraneous dot at the end. + */ + netdomain = (char *)strchr(netname, '@'); + if (! netdomain) { + fprintf(stderr, "%s: invalid netname, missing @: '%s'. \n", + program_name, netname); + return (0); + } + if (netname[strlen(netname)-1] == '.') + netname[strlen(netname)-1] = '\0'; + netdomain++; /* skip '@' */ + strcpy(netdomainaux, netdomain); + strcat(netdomainaux, "."); + + if (strcasecmp(princdomain, netdomainaux) != 0) { + fprintf(stderr, "%s: domain of netname \"%s\" should " + "be same as that of principal \"%s\"\n", + program_name, netname, nis_princ); + return (0); + } + + /* Check publickey policy and warn user if it is not NIS+ */ + check_switch_policy("publickey", "nisplus", &publickey_default, + "WARNING:", " when using NIS+"); + + /* Another principal owns same credentials? (exits if that happens) */ + (void) auth_exists(nis_princ, netname, authtype, domain); + + return (1); /* all passed */ +} + +/* ***************************** keylogin stuff *************************** */ +static int +keylogin(char *netname, char *secret, char *flavor, + keylen_t keylen, algtype_t algtype) +{ + mechanism_t **mechs; + int mcount; + + if (mechs = __nis_get_mechanisms(FALSE)) { + for (mcount = 0; mechs[mcount]; mcount++) { + if (keylen == mechs[mcount]->keylen && + algtype == mechs[mcount]->algtype) { + if (key_setnet_g(netname, secret, + mechs[mcount]->keylen, + NULL, 0, + mechs[mcount]->algtype) + < 0) { + fprintf(stderr, + "Could not set %s's %s secret key\n", + netname, flavor); + fprintf(stderr, + "May be the keyserv is down?\n"); + return (0); + } + } + } + } else { + if (keylen == 192 && algtype == 0) + return (keylogin_des(netname, secret)); + } + return (1); +} + +/* + * fgets is broken in that if it reads a NUL character it will always return + * EOF. This replacement can deal with NULs + */ +static char * +fgets_ignorenul(char *s, int n, FILE *stream) +{ + int fildes = fileno(stream); + int i = 0; + int rs = 0; + char c; + + if (fildes < 0) + return (NULL); + + while (i < n - 1) { + rs = read(fildes, &c, 1); + switch (rs) { + case 1: + break; + case 0: + /* EOF */ + if (i > 0) + s[i] = '\0'; + return (NULL); + break; + default: + return (NULL); + } + switch (c) { + case '\0': + break; + case '\n': + s[i] = c; + s[++i] = '\0'; + return (s); + default: + if (c != '\0') + s[i++] = c; + } + } + s[i] = '\0'; + return (s); +} + +#define ROOTKEY_FILE_BACKUP "/etc/.rootkey.bak" +/* Should last until 16384-bit DH keys */ +#define MAXROOTKEY_LINE_LEN 4224 +#define MAXROOTKEY_LEN 4096 + +/* write unencrypted secret key into root key file */ +void +write_rootkey(char *secret, char *flavor, keylen_t keylen, algtype_t algtype) +{ + char line[MAXROOTKEY_LINE_LEN]; + char keyent[MAXROOTKEY_LEN]; + algtype_t atent; + int rootfd, bakfd, hexkeybytes; + bool_t lineone = TRUE; + bool_t gotit = FALSE; + FILE *rootfile, *bakfile; + + unlink(ROOTKEY_FILE_BACKUP); + if ((rename(ROOTKEY_FILE, ROOTKEY_FILE_BACKUP)) < 0) { + if ((bakfd = creat(ROOTKEY_FILE_BACKUP, 0600)) < 0) { + perror("Could not create /etc/.rootkey.bak"); + goto rootkey_err; + } + close(bakfd); + } + + if ((rootfd = open(ROOTKEY_FILE, O_WRONLY+O_CREAT, 0600)) < 0) { + perror("Could not open /etc/.rootkey for writing"); + fprintf(stderr, + "Attempting to restore original /etc/.rootkey\n"); + rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE); + goto rootkey_err; + } + if (!(rootfile = fdopen(rootfd, "w"))) { + perror("Could not open /etc/.rootkey for writing"); + fprintf(stderr, + "Attempting to restore original /etc/.rootkey\n"); + close(rootfd); + unlink(ROOTKEY_FILE); + rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE); + goto rootkey_err; + } + if (!(bakfile = fopen(ROOTKEY_FILE_BACKUP, "r"))) { + perror("Could not open /etc/.rootkey.bak for reading"); + fprintf(stderr, + "Attempting to restore original /etc/.rootkey\n"); + fclose(rootfile); + unlink(ROOTKEY_FILE); + rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE); + goto rootkey_err; + } + + hexkeybytes = ((keylen + 7) / 8) * 2; + + while (fgets_ignorenul(line, MAXROOTKEY_LINE_LEN, bakfile)) { + sscanf(line, "%s %d", keyent, &atent); + /* + * 192-bit keys always go on the first line + */ + if (lineone) { + lineone = FALSE; + if (keylen == 192) { + gotit = TRUE; + fprintf(rootfile, "%s\n", secret); + } else + fprintf(rootfile, "%s", line); + fflush(rootfile); + } else { + if ((strlen(keyent) == hexkeybytes) && + (atent == algtype)) { + /* + * Silently remove lines with the same + * keylen/algtype + */ + if (gotit) + continue; + else + gotit = TRUE; + + fprintf(rootfile, "%s %d\n", secret, algtype); + } else + fprintf(rootfile, "%s", line); + fflush(rootfile); + } + } + + /* Append key to rootkey file */ + if (!gotit) { + if (keylen == 192) + fprintf(rootfile, "%s\n", secret); + else { + if (lineone) + fprintf(rootfile, "\n"); + fprintf(rootfile, "%s %d\n", secret, algtype); + } + } + fflush(rootfile); + fclose(rootfile); + fclose(bakfile); + unlink(ROOTKEY_FILE_BACKUP); + return; + +rootkey_err: + fprintf(stderr, "WARNING: Could not write %s key to /etc/.rootkey\n", + flavor); +} + + +char * +get_password(uid_t uid, int same_host, char *target_host, char *domain) +{ + static char password[256]; + char prompt[256]; + char *encrypted_password, *login_password = NULL, *pass; + struct passwd *pw; + int passwords_matched = 0; + struct passwd *domain_getpwuid(); + struct spwd *domain_getspnam(); + + struct spwd *spw; + + /* ignore password checking when the -l option is used */ + if (nispasswd[0] != '\0') + return (nispasswd); + + if (uid == 0) { + if (same_host) { + /* + * The root user is never in the NIS+ + * data base. Get it locally. + */ + pw = getpwuid(0); + if (! pw) { + fprintf(stderr, + "%s: unable to locate password record for uid %d\n", + program_name, uid); + return (0); + } + spw = getspnam(pw->pw_name); + if (!spw) { + fprintf(stderr, + "%s: unable to locate password record for uid 0\n", + program_name); + return (0); + } + login_password = spw->sp_pwdp; + } + } else { + pw = domain_getpwuid(domain, uid); + if (pw) { + /* get password from shadow */ + spw = domain_getspnam(domain, pw->pw_name); + if (spw) { + login_password = spw->sp_pwdp; + } + } else { + return (0); + } + } + + if ((uid == my_uid) && ((uid != 0) || same_host)) + sprintf(prompt, "Enter login password:"); + else if (uid == 0) { + sprintf(prompt, "Enter %s's root login password:", + target_host); + } else + sprintf(prompt, "Enter %s's login password:", + pw->pw_name); + pass = getpass(prompt); + if (strlen(pass) == 0) { + (void) fprintf(stderr, "%s: Password unchanged.\n", + program_name); + return (0); + } + strcpy(password, pass); + + + /* Verify that password supplied matches login password */ + if (login_password && (strlen(login_password) != 0)) { + encrypted_password = crypt(password, login_password); + if (strcmp(encrypted_password, login_password) == 0) + passwords_matched = 1; + else { + fprintf(stderr, + "%s: %s: password differs from login password.\n", + program_name, force? "WARNING" : "ERROR"); + if (!force) + return (0); + } + } + + /* Check for mis-typed password */ + if (!passwords_matched) { + pass = getpass("Retype password:"); + if (strcmp(password, pass) != 0) { + (void) fprintf(stderr, "%s: password incorrect.\n", + program_name); + return (0); + } + } + + return (password); +} + + +/* + * Definitions of the credential table. + * + * Column Name Contents + * ------ ---- -------- + * 0 cname nis principal name + * 1 auth_type DES + * 2 auth_name netname + * 3 public_auth_data public key + * 4 private_auth_data encrypted secret key with checksum + */ + +/* + * Function for building DES credentials. + * + * The domain may be the local domain or some remote domain. + * 'domain' should be the same as the domain found in netname, + * which should be the home domain of nis+ principal. + */ + +int +make_dhext_cred(nis_princ, netname, domain, flavor) + char *nis_princ; /* NIS+ principal name */ + char *netname; /* AUTH_DES netname */ + char *domain; /* Domain name */ + char *flavor; /* Mech alias */ +{ + nis_object *obj = init_entry(); + uid_t uid; + static char *pass; + char *public, *secret, *crypt1; + char authtype[MECH_MAXATNAME]; + char target_host[MAXHOSTNAMELEN+1]; + int same_host = 0; + int status, len, addition; + keylen_t kl; + algtype_t at; + size_t hexkeybytes; + + if (nis_princ == NULL) + nis_princ = default_principal(domain); + + if (strcmp(flavor, "dh192-0") == 0) + return (make_des_cred_be(nis_princ, netname, domain)); + + __nis_mechalias2authtype(flavor, authtype, MECH_MAXATNAME); + + if (__nis_translate_mechanism(flavor, &kl, &at) == -1) + goto badstatus; + + if (sanity_checks(nis_princ, netname, domain, authtype) == 0) + goto badstatus; + + addition = (cred_exists(nis_princ, authtype, domain) == NIS_NOTFOUND); + + /* Extract user/host information from netname */ + if (! isdigit(netname[OPSYS_LEN+1])) { + uid = 0; /* root */ + netname2host(netname, target_host, MAXHOSTNAMELEN); + len = strlen(my_host)-1; /* ignore trailing dot in my_host */ + if (len == strlen(target_host) && + strncasecmp(target_host, my_host, len) == 0) + same_host = 1; + } else { + uid = (uid_t)atoi(netname+OPSYS_LEN+1); + } + + if (!pass) + pass = get_password(uid, same_host, target_host, domain); + if (!pass) + goto badstatus; + + /* Get password with which to encrypt secret key. */ + (void) printf("%s %s key pair for %s (%s).\n", + addition? "Adding" : "Updating", flavor, + netname, nis_princ); + + hexkeybytes = ((kl + 7) / 8) * 2; + if (!(public = (char *)malloc(hexkeybytes + 1))) + goto badstatus; + if (!(secret = (char *)malloc(hexkeybytes + 1))) + goto badstatus; + + /* Encrypt secret key */ + if (!(__gen_dhkeys_g(public, secret, kl, at, pass))) + goto badstatus; + if (!(xencrypt_g(secret, kl, at, pass, netname, &crypt1, TRUE))) + goto badstatus; + + /* Now we have a key pair, build up the cred entry */ + ENTRY_VAL(obj, 0) = nis_princ; + ENTRY_LEN(obj, 0) = strlen(nis_princ) + 1; + + ENTRY_VAL(obj, 1) = authtype; + ENTRY_LEN(obj, 1) = strlen(authtype) + 1; + + ENTRY_VAL(obj, 2) = netname; + ENTRY_LEN(obj, 2) = strlen(netname) + 1; + + ENTRY_VAL(obj, 3) = public; + ENTRY_LEN(obj, 3) = strlen(public) + 1; + + ENTRY_VAL(obj, 4) = crypt1; + ENTRY_LEN(obj, 4) = strlen(crypt1) + 1; + + if (addition) { + obj->zo_owner = nis_princ; + obj->zo_group = my_group; + obj->zo_domain = domain; + /* owner: r, group: rmcd */ + obj->zo_access = ((NIS_READ_ACC<<16)| + (NIS_READ_ACC|NIS_MODIFY_ACC|NIS_CREATE_ACC| + NIS_DESTROY_ACC)<<8); + status = add_cred_obj(obj, domain); + } else { + obj->EN_data.en_cols.en_cols_val[3].ec_flags |= EN_MODIFIED; + obj->EN_data.en_cols.en_cols_val[4].ec_flags |= EN_MODIFIED; + status = modify_cred_obj(obj, domain); + } + + + /* attempt keylogin if appropriate */ + if (status) { + if ((uid == my_uid) && ((uid != 0) || same_host)) + keylogin(netname, secret, flavor, kl, at); + if ((uid == 0) && same_host) + write_rootkey(secret, flavor, kl, at); + } + + goto cleanup; +badstatus: + status = 0; + +cleanup: + + return (status); +} + + +char * +get_dhext_cred(char *domain, char *flavor) +{ + int uid, status; + static char netname[MAXNETNAMELEN+1]; + + uid = my_uid; + + if (uid == 0) + status = host2netname(netname, (char *)NULL, domain); + else { + /* generate netname using uid and domain information. */ + int len; + len = strlen(domain); + if ((len + OPSYS_LEN + 3 + MAXIPRINT) > MAXNETNAMELEN) { + printf("Domain name too long: \"%s\"\n", domain); + goto not_found; + } + (void) sprintf(netname, "%s.%d@%s", OPSYS, uid, domain); + len = strlen(netname); + if (netname[len-1] == '.') + netname[len-1] = '\0'; + + status = 1; + } + + if (status == 1) { + printf("DES principal name : \"%s\"\n", netname); + return (netname); + } + +not_found: + printf("DES principal name for %d not found\n", uid); + return (NULL); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makekerbcred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makekerbcred.c new file mode 100644 index 0000000000..3ea21a7d5d --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makekerbcred.c @@ -0,0 +1,47 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * makekerbcred.c + * + * Copyright (c) 1988-1992 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +int +make_kerb_cred(np, p, d, flavor) + char *np; + char *p; + char *d; + char *flavor; +{ + return (0); +} + +char * +get_kerb_cred(domain, flavor) +char *domain; +char *flavor; +{ + return (0); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makelocalcred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makelocalcred.c new file mode 100644 index 0000000000..be352ed3dd --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makelocalcred.c @@ -0,0 +1,296 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * makelocalcred.c + * + * Copyright (c) 1988-1992 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * makelocalcred.c + * + * Make a "local" credential. The local credential is used to map from + * a UID in the nis_local_directory() to a principal name in some other + * NIS+ directory. Needless to say the principal name is required and + * this function must be run as the NIS+ administrator. + */ + +#include <stdio.h> +#include <pwd.h> +#include <limits.h> +#include <rpcsvc/nis.h> +#include "nisaddcred.h" + +/* + * _getgroupsbymember(uname, gid_array, maxgids, numgids): + * This function can be found in libc/port/gen/getgrnam_r.c. + * It's a private interface mainly for initgroups(). It returns the + * group ids of groups of which the specified user is a member. + */ +extern int _getgroupsbymember(const char *, gid_t[], int, int); + +#define EVAL(e, c) (ENTRY_LEN(e, c) > 0 ? ENTRY_VAL(e, c) : "") + +struct cback_info { + char *username; + int gidcnt; + int maxgids; + gid_t *gidlist; +}; + +static +int +add_gidval(info, gid) + struct cback_info *info; + gid_t gid; +{ + int i; + + for (i = 0; i < info->gidcnt; i++) { + if (info->gidlist[i] == gid) + return (1); /* don't insert dup, but not error */ + } + if (info->gidcnt >= info->maxgids) + return (0); /* no room */ + + info->gidlist[info->gidcnt] = gid; + info->gidcnt++; + + return (1); +} + +int +cback(table, entry, udata) + nis_name table; + nis_object *entry; + void *udata; +{ + struct cback_info *info = (struct cback_info *)udata; + char *username = info->username; + int len; + char *p; + char *members; + char *gid; + char *gname; + int gidval; + + len = strlen(username); + + members = EVAL(entry, 3); + while (*members) { + while (*members && isspace(*members)) + members++; + if (*members == '\0') + break; + + p = members; + while (*members && *members != ',') + members++; + + if (members - p == len && strncmp(p, username, len) == 0) { + gid = EVAL(entry, 2); + if (isdigit(gid[0])) { + gidval = atoi(gid); + if (! add_gidval(info, gidval)) + return (1); /* no more room */ + } + } + + if (*members == ',') + members++; + } + + return (0); +} + +/* + * Get groups from an NIS+ domain. If that doesn't yield any + * groups and the domain was not specified on the command line, + * then we try the local routines for getting group ids. + */ +static +int +__getnisgroupsbymember(domain, uid, username, basegid, maxgids, gidlist) + char *domain; + uid_t uid; + char *username; + gid_t basegid; + int maxgids; + gid_t *gidlist; +{ + struct cback_info info; + nis_result *res; + char name[1024]; + u_long flags = EXPAND_NAME|MASTER_ONLY|FOLLOW_PATH|FOLLOW_LINKS; + + info.username = username; + info.gidcnt = 0; + info.maxgids = NGROUPS_MAX; + info.gidlist = gidlist; + + add_gidval(&info, basegid); + + sprintf(name, "group.org_dir.%s", domain); + res = nis_list(name, flags, cback, (void *)&info); + nis_freeresult(res); + +#ifdef USE_LOCAL_INFO + /* + * If no domain was specified on the command line and we didn't get + * any extra gids from NIS+, then try getting them locally. + */ + if (! explicit_domain && info.gidcnt == 1) { + if (uid == my_uid) + info.gidcnt = getgroups(info.maxgids, info.gidlist); + else + info.gidcnt = _getgroupsbymember(username, info.gidlist, + info.maxgids, 1); + + /* if getgroups failed, put basegid back in list */ + if (info.gidcnt <= 0) { + info.gidcnt = 0; + add_gidval(&info, basegid); + } + } +#endif /* USE_LOCAL_INFO */ + + return (info.gidcnt); +} + +int +make_local_cred(nisprinc, uidstr, domain, flavor) + char *nisprinc; + char *uidstr; + char *domain; + char *flavor; /* Ignored. */ +{ + nis_object *obj = init_entry(); + nis_error err; + int i, gidlen; + uid_t uid; + gid_t gidlist[NGROUPS_MAX]; + struct passwd *pw; + char nisname[NIS_MAXNAMELEN+1], + pdata[NIS_MAXATTRVAL+1], + gidstr[MAXIPRINT+1]; + int status, addition; + struct passwd *domain_getpwuid(); + + if (!isdigit(*uidstr)) { + fprintf(stderr, + "%s: invalid local principal '%s' (must be number)\n", + program_name, uidstr); + return (0); + } + + uid = (uid_t)atoi(uidstr); + if (uid == 0) { + fprintf(stderr, "%s: need not add LOCAL entry for root\n", + program_name); + return (0); + } + + pw = domain_getpwuid(domain, uid); + if (!pw) + return (0); + + if (nisprinc == 0) + sprintf(nisname, "%s.%s", pw->pw_name, domain); + else + strcpy(nisname, nisprinc); + + /* Another principal owns same credentials? (exits if that happens) */ + (void) auth_exists(nisname, uidstr, "LOCAL", domain); + + addition = (cred_exists(nisname, "LOCAL", domain) == NIS_NOTFOUND); + + /* build up list of group ids */ + if ((gidlen = __getnisgroupsbymember(domain, uid, + pw->pw_name, pw->pw_gid, NGROUPS_MAX, gidlist)) <= 0) { + fprintf(stderr, + "Failed to get group information for user %s\n", pw->pw_name); + return (0); + } + pdata[0] = '\0'; + for (i = 0; i < gidlen; i++) { + if ((i + 1) < gidlen) + sprintf(gidstr, "%d,", gidlist[i]); + else + sprintf(gidstr, "%d", gidlist[i]); + strcat(pdata, gidstr); + } + + ENTRY_VAL(obj, 0) = nisname; + ENTRY_LEN(obj, 0) = strlen(nisname) + 1; + + ENTRY_VAL(obj, 1) = "LOCAL"; + ENTRY_LEN(obj, 1) = 6; + + ENTRY_VAL(obj, 2) = uidstr; + ENTRY_LEN(obj, 2) = strlen(uidstr)+1; + + ENTRY_VAL(obj, 3) = pdata; + ENTRY_LEN(obj, 3) = strlen(pdata) + 1; + + ENTRY_VAL(obj, 4) = ""; + ENTRY_LEN(obj, 4) = 0; + + if (addition) { + obj->zo_owner = my_nisname; + obj->zo_group = my_group; + obj->zo_domain = domain; + /* owner: rmcd, group: rmcd */ + obj->zo_access = ((NIS_READ_ACC|NIS_MODIFY_ACC| + NIS_CREATE_ACC|NIS_DESTROY_ACC)<<8) | + ((NIS_READ_ACC|NIS_MODIFY_ACC| + NIS_CREATE_ACC|NIS_DESTROY_ACC)<<16); + status = add_cred_obj(obj, domain); + } else { + /* columns that could have changed */ + obj->EN_data.en_cols.en_cols_val[2].ec_flags |= EN_MODIFIED; + obj->EN_data.en_cols.en_cols_val[3].ec_flags |= EN_MODIFIED; + + status = modify_cred_obj(obj, domain); + } + return (status); +} + + + + +/* + * Return a string representation of the "LOCAL" authentication name. + * In this case it is easy, its our uid. + */ +char * +get_local_cred(domain, flavor) + char *domain; /* ignored for local case */ + char *flavor; /* ignored */ +{ + static char myname[MAXIPRINT]; + + sprintf(myname, "%d", my_uid); + return (myname); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makersacred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makersacred.c new file mode 100644 index 0000000000..2f6b66c82b --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makersacred.c @@ -0,0 +1,47 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * makersacred.c + * + * Copyright (c) 1988-1992 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +int +make_rsa_cred(np, p, d, flavor) + char *np; + char *p; + char *d; + char *flavor; +{ + return (0); +} + +char * +get_rsa_cred(domain, flavor) +char *domain; +char *flavor; +{ + return (0); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makesyscred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makesyscred.c new file mode 100644 index 0000000000..32d21ea780 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makesyscred.c @@ -0,0 +1,191 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * makesyscred.c + * + * This module makes the "AUTH_SYS" credential and stores it in the credential + * table. + */ + +#include <stdio.h> +#include <pwd.h> +#include <rpcsvc/nis.h> +#include "nisaddcred.h" + +extern char *strchr(); + +int +make_sys_cred(np, p, d, flavor) + char *np; + char *p; + char *d; + char *flavor; /* Ignored. */ +{ + nis_result *res; + nis_object *obj = init_entry(); + uid_t uid; + gid_t gid; + int grplist[32]; + int ngrps, i; + struct passwd *pw; + char sysname[32], + *s, + luid[16], + lgids[64], + buf[16], + pname[1024], + tname[1024]; + nis_error err; + + uid = atoi(p); + + if ((uid != geteuid()) && (geteuid() != 0)) { + fprintf(stderr, "must be root to add anothers credentials.\n"); + return (0); + } + + s = strchr(p, ','); + if (! s) { + fprintf(stderr, "badly formed AUTH_SYS principal name.\n"); + return (0); + } + s++; + gid = atoi(s); + + pw = getpwuid(uid); + if (! pw) { + /* + * If NIS+ is the name service for the passwd data, + * maybe this answer came from an out of date replica + * server. So lets try the NIS+ Master server. + */ + pw = getpwuid_nisplus_master(uid, &err); + if (pw == NULL) { + if (err == NIS_NOTFOUND) + fprintf(stderr, + "%s: no password entry for uid %d\n", + program_name, uid); + else + fprintf(stderr, + "%s: could not get the password entry for uid %d: %s\n", + program_name, uid, nis_sperrno(err)); + return (0); + } + } + + ENTRY_VAL(obj, 0) = np; + ENTRY_LEN(obj, 0) = strlen(np) + 1; + + ENTRY_VAL(obj, 1) = "SYS"; + ENTRY_LEN(obj, 1) = 4; + + ENTRY_VAL(obj, 2) = p; + ENTRY_LEN(obj, 2) = strlen(p)+1; + + if ((uid != geteuid()) && (pw->pw_uid != uid)) { + ngrps = initgroups(pw->pw_name, pw->pw_gid); + if (ngrps == -1) { + perror("initgroups:"); + return (0); + } + } + ngrps = getgroups(32, grplist); + if (ngrps == -1) { + perror("getgroups:"); + return (0); + } + + lgids[0] = '\0'; + for (i = 0; i < ngrps; i++) { + if (i+1 < ngrps) + sprintf(buf, "%d,", grplist[i]); + else + sprintf(buf, "%d", grplist[i]); + strcat(lgids, buf); + } + + ENTRY_VAL(obj, 3) = lgids; + ENTRY_LEN(obj, 3) = strlen(lgids) + 1; + + ENTRY_VAL(obj, 4) = ""; /* no private data */ + ENTRY_LEN(obj, 4) = 0; + + sprintf(tname, "%s.%s", CRED_TABLE, d); + obj->zo_owner = pname; + obj->zo_domain = d; + res = nis_add_entry(tname, obj, 0); + switch (res->status) { + case NIS_TRYAGAIN : + fprintf(stderr, "NIS+ server busy, try again later.\n"); + i = 0; + break; + case NIS_PERMISSION : + fprintf(stderr, + "Insufficent permission to update/create credentials\n"); + i = 0; + break; + case NIS_SUCCESS : + i = 1; + break; + default : + fprintf(stderr, "Error creating credential, NIS+ error %s\n", + nis_sperrno(res->status)); + break; + } + nis_freeresult(res); + return (i); +} + +char * +get_sys_cred(char *domain, /* Ignored. */ + char *flavor) /* Ignored. */ +{ + static char myname[64]; + struct passwd *pw; + int uid; + nis_error err; + + uid = geteuid(); + pw = getpwuid(uid); + if (! pw) { + /* + * If NIS+ is the name service for the passwd data, + * maybe this answer came from an out of date replica + * server. So lets try the NIS+ Master server. + */ + pw = getpwuid_nisplus_master(uid, &err); + if (pw == NULL) { + if (err == NIS_NOTFOUND) + fprintf(stderr, + "%s: no password entry for uid %d\n", + program_name, uid); + else + fprintf(stderr, + "%s: could not get the password entry for uid %d: %s\n", + program_name, uid, nis_sperrno(err)); + return (NULL); + } + } + + sprintf(myname, "%d,%d", pw->pw_uid, pw->pw_gid); + return (myname); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.c new file mode 100644 index 0000000000..110fde9654 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.c @@ -0,0 +1,742 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisaddcred.c + * + * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisaddcred.c + * + * This utility is used to add credentials for a user to the NIS+ databases. + * Its syntax is as follows : + * nisaddcred [-p principal] [-P nis+_principal] [-l login_passwd] flavor + * nisaddcred -r [nis+_principal] + */ + +/* + * The code is set up so that it may be possible to pass 'domain' + * as an optional argument. However, it is not clear whether it + * makes any sense to addcreds to other domains, and whether + * the code itself would work. + */ + +#include <stdio.h> +#include <malloc.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/systeminfo.h> +#include <pwd.h> +#include <limits.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/nis_dhext.h> +#define CRED_TABLE "cred.org_dir" + +void +usage(cmd) + char *cmd; +{ + fprintf(stderr, +"usage:\n\t%s [-p principal] [-P NIS+_principal] [-l login_password] ", + cmd); + fprintf(stderr, "flavor\n"); + fprintf(stderr, + "\t%s -r [NIS+_principal]\n", cmd); + exit(1); +} + +extern int make_des_cred(), make_kerb_cred(), + make_rsa_cred(), make_local_cred(), make_dhext_cred(); +extern char *get_des_cred(), *get_kerb_cred(), + *get_rsa_cred(), *get_local_cred(), *get_dhext_cred(); + +static +struct { + char *name; + int (*makecred)(); + char *(*pname)(); +} known_flavors[] = { + { "local", make_local_cred, get_local_cred }, /* LOCAL ALWAYS 0! */ + { "des", make_des_cred, get_des_cred }, +#ifdef KERB_RSA_CREDS + { "kerb", make_kerb_cred, get_kerb_cred }, + { "rsa", make_rsa_cred, get_rsa_cred }, +#endif /* KERB_RSA_CREDS */ + { NULL, NULL}}; + +extern int optind; +extern char *optarg; +extern nis_name nis_local_host(); + +char *program_name = "nisaddcred"; +uid_t my_uid; +nis_name my_nisname = 0; +char *my_host; +char *my_group; +char nispasswd[sizeof (des_block)+1]; +int explicit_domain; /* if true, then a domain was specified on command line */ + +/* + * Similar to nis_local_principal (nis_subr.c) except + * this gets the results from the MASTER_ONLY and no FOLLOW_PATH. + * We only want the master because we'll be making updates there, + * and also the replicas may not have seen the 'nisaddacred local' + * that may have just occurred. + * Returns NULL if not found. + */ +char * +default_principal(directory) +char *directory; +{ + nis_result *res; + char buf[NIS_MAXNAMELEN+1]; + static char principal_name[NIS_MAXNAMELEN+1]; + uid_t uid; + + + uid = my_uid; + + if (uid == 0) + return (nis_local_host()); + + sprintf(buf, "[auth_name=%d,auth_type=LOCAL],%s.%s", + uid, CRED_TABLE, directory); + + if (buf[strlen(buf)-1] != '.') + strcat(buf, "."); + + res = nis_list(buf, MASTER_ONLY+USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS, + NULL, NULL); + + if (res == NULL) { + fprintf(stderr, + "%s: unable to get result from NIS+ server.", + program_name); + exit(1); + } + switch (res->status) { + case NIS_SUCCESS: + if (res->objects.objects_len > 1) { + /* + * More than one principal with same uid? + * something wrong with cred table. Should be unique + * Warn user and continue. + */ + fprintf(stderr, + "%s: LOCAL entry for %d in directory \"%s\" not unique", + program_name, uid, directory); + } + strcpy(principal_name, ENTRY_VAL(res->objects.objects_val, 0)); + nis_freeresult(res); + return (principal_name); + + case NIS_NOTFOUND: + nis_freeresult(res); + return (NULL); + + case NIS_TRYAGAIN : + fprintf(stderr, + "%s: NIS+ server busy, try again later.\n", + program_name); + exit(1); + + case NIS_PERMISSION : + fprintf(stderr, + "%s: insufficent permission to update credentials.\n", + program_name); + exit(1); + + default: + fprintf(stderr, + "%s: error talking to server, NIS+ error: %s.\n", + program_name, nis_sperrno(res->status)); + exit(1); + } + return (NULL); +} + +/* Check whether this principal already has this type of credentials */ +nis_error +cred_exists(char *nisprinc, char *flavor, char *domain) +{ + char sname[NIS_MAXNAMELEN+1]; + nis_result *res; + nis_error status; + + sprintf(sname, "[cname=\"%s\",auth_type=%s],%s.%s", + nisprinc, flavor, CRED_TABLE, domain); + if (sname[strlen(sname)-1] != '.') + strcat(sname, "."); + + /* Don't want FOLLOW_PATH here */ + res = nis_list(sname, MASTER_ONLY+USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS, + NULL, NULL); + + status = res->status; + switch (status) { + case NIS_NOTFOUND: + break; + case NIS_TRYAGAIN : + fprintf(stderr, "%s: NIS+ server busy, try again later.\n", + program_name); + exit(1); + case NIS_PERMISSION : + fprintf(stderr, + "%s: insufficent permission to look at credentials table\n", + program_name); + exit(1); + case NIS_SUCCESS: + case NIS_S_SUCCESS: + break; + default: + fprintf(stderr, + "%s: error looking at cred table, NIS+ error: %s\n", + program_name, nis_sperrno(res->status)); + exit(1); + } + nis_freeresult(res); + return (status); +} + +/* Check that someone else don't have the same auth information already */ +nis_error +auth_exists(char *princname, char *auth_name, char *auth_type, char *domain) +{ + char sname[NIS_MAXNAMELEN+1]; + nis_result *res; + nis_error status; + char *foundprinc; + + sprintf(sname, "[auth_name=%s,auth_type=%s],%s.%s", + auth_name, auth_type, CRED_TABLE, domain); + if (sname[strlen(sname)-1] != '.') + strcat(sname, "."); + /* Don't want FOLLOW_PATH here */ + res = nis_list(sname, MASTER_ONLY+USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS, + NULL, NULL); + + status = res->status; + switch (res->status) { + case NIS_NOTFOUND: + break; + case NIS_TRYAGAIN : + fprintf(stderr, + "%s: NIS+ server busy, try again later.\n", + program_name); + exit(1); + case NIS_PERMISSION : + fprintf(stderr, + "%s: insufficent permission to look up old credentials.\n", + program_name); + exit(1); + case NIS_SUCCESS: + foundprinc = ENTRY_VAL(res->objects.objects_val, 0); + if (strcmp(foundprinc, princname) != 0) { + fprintf(stderr, + "%s: %s credentials with auth_name '%s' already belongs to '%s'.\n", + program_name, auth_type, auth_name, foundprinc); + exit(1); + } + break; + default: + fprintf(stderr, + "%s: error looking at cred table, NIS+ error: %s\n", + program_name, nis_sperrno(res->status)); + exit(1); + } + nis_freeresult(res); + return (status); +} + +int +modify_cred_obj(obj, domain) + char *domain; + nis_object *obj; +{ + int status = 0; + char sname[NIS_MAXNAMELEN+1]; + nis_result *res; + + sprintf(sname, "%s.%s", CRED_TABLE, domain); + res = nis_modify_entry(sname, obj, 0); + switch (res->status) { + case NIS_TRYAGAIN : + fprintf(stderr, + "%s: NIS+ server busy, try again later.\n", + program_name); + exit(1); + case NIS_PERMISSION : + fprintf(stderr, + "%s: insufficent permission to update credentials.\n", + program_name); + exit(1); + case NIS_SUCCESS : + status = 1; + break; + default: + fprintf(stderr, + "%s: error creating credential, NIS+ error: %s.\n", + program_name, nis_sperrno(res->status)); + exit(1); + } + nis_freeresult(res); + return (status); +} + + +int +add_cred_obj(obj, domain) + char *domain; + nis_object *obj; +{ + int status = 0; + char sname[NIS_MAXNAMELEN+1]; + nis_result *res; + + /* Assume check for cred_exists performed already */ + + sprintf(sname, "%s.%s", CRED_TABLE, domain); + res = nis_add_entry(sname, obj, 0); + switch (res->status) { + case NIS_TRYAGAIN : + fprintf(stderr, + "%s: NIS+ server busy, try again later.\n", + program_name); + exit(1); + case NIS_PERMISSION : + fprintf(stderr, + "%s: insufficent permission to update credentials.\n", + program_name); + exit(1); + case NIS_SUCCESS : + status = 1; + break; + default: + fprintf(stderr, + "%s: error creating credential, NIS+ error: %s.\n", + program_name, nis_sperrno(res->status)); + exit(1); + } + nis_freeresult(res); + return (status); +} + +void +unknown_flavor(char *flavor) +{ + mechanism_t **mechlist; + int i; + + mechlist = __nis_get_mechanisms(FALSE); + fprintf(stderr, "%s: unknown flavor '%s'\n", program_name, flavor); + + fprintf(stderr, "known flavors are: "); + for (i = 0; known_flavors[i].name; i++) + if (known_flavors[i+1].name) + fprintf(stderr, "%s, ", known_flavors[i].name); + else { + if (mechlist && + !(AUTH_DES_COMPAT_CHK(mechlist[0]) && + !mechlist[1])) { + int count; + + fprintf(stderr, "%s, ", known_flavors[i].name); + for (count = 0; mechlist[count]; count++) { + if (mechlist[count+1]) + fprintf(stderr, "%s\n", + mechlist[count]->alias); + else + fprintf(stderr, "%s.\n", + mechlist[count]->alias); + } + __nis_release_mechanisms(mechlist); + } else + fprintf(stderr, "%s.\n", + known_flavors[i].name); + } + exit(1); +} + +void +perform_add(flavor, domain, nisprinc, princ) + char *flavor; + char *domain; + char *nisprinc; + char *princ; +{ + int i, status, fl; + int (*mc)(); + char *(*gp)(); + + fl = strlen(flavor); + for (i = 0; i < fl; i++) + *(flavor+i) = tolower(*(flavor+i)); + + /* See if given flavor is one that we know */ + for (i = 0, mc = NULL; !mc && known_flavors[i].name; i++) + if (strcmp(known_flavors[i].name, flavor) == 0) { + mc = known_flavors[i].makecred; + gp = known_flavors[i].pname; + break; + } + + if (! mc) { + keylen_t kl; + algtype_t at; + + if (strcmp(flavor, "dh192-0")) { + /* It could be a GSS/DHEXT type */ + if (__nis_translate_mechanism(flavor, &kl, &at) == -1) + unknown_flavor(flavor); + } else { + mc = make_dhext_cred; + gp = get_des_cred; + } + + mc = make_dhext_cred; + gp = get_dhext_cred; + } + + /* + * Call the function that will build the appropriate credential. + * When called, both the principal *and* the domain must be specified. + * some functions will complain if the domain and the domain of the + * principal (if fully qualified) differ. + */ + if (! princ) { + princ = (*gp)(domain, flavor); + if (princ == NULL) { + fprintf(stderr, + "%s: unable to determine your principal name for this flavor.\n", + program_name); + exit(1); + } + } + + status = (*mc)(nisprinc, princ, domain, flavor); + if (! status) { + fprintf(stderr, "%s: unable to create credential.\n", + program_name); + exit(1); + } +} + +void +perform_remove(domain, nis_princ) +char *domain; +char *nis_princ; +{ + char sname[NIS_MAXNAMELEN+1]; + nis_result *res; + + if (nis_princ == NULL) + nis_princ = default_principal(domain); + + sprintf(sname, "[cname=\"%s\"],%s.%s", nis_princ, CRED_TABLE, domain); + if (sname[strlen(sname)-1] != '.') + strcat(sname, "."); + res = nis_remove_entry(sname, 0, REM_MULTIPLE); + + if (res->status != NIS_SUCCESS) { + nis_perror(res->status, "could not remove entry"); + exit(1); + } + nis_freeresult(res); + +/* Should do keylogout here if removing my own des credentials */ +} + + +nis_object * +init_entry() +{ + static nis_object obj; + static entry_col cred_data[10]; + entry_obj *eo; + + memset((char *)(&obj), 0, sizeof (obj)); + memset((char *)(cred_data), 0, sizeof (entry_col) * 10); + + obj.zo_name = "cred"; + obj.zo_group = ""; + obj.zo_ttl = 43200; + obj.zo_data.zo_type = NIS_ENTRY_OBJ; + eo = &(obj.EN_data); + eo->en_type = "cred_tbl"; + eo->en_cols.en_cols_val = cred_data; + eo->en_cols.en_cols_len = 5; + cred_data[4].ec_flags |= EN_CRYPT; + return (&obj); +} + +void +main(argc, argv) + int argc; + char *argv[]; +{ + char *princ = NULL; + char *nisprinc = NULL; + char *domain = nis_local_directory(); + char *flavor = NULL; + int len; + int c; + char *p; + int add_op = 1; /* whether this operation is an add */ + + program_name = argv[0]; + nispasswd[0] = '\0'; + + if (argc == 1) + usage(program_name); + + while ((c = getopt(argc, argv, "p:P:l:r")) != -1) { + switch (c) { + case 'p' : + if (add_op == 0) { + fprintf(stderr, + "%s: cannot combine any other option with -r.\n", + program_name); + usage(program_name); + } + princ = optarg; + break; + case 'P' : + if (add_op == 0) { + fprintf(stderr, + "%s: cannot combine any other option with -r.\n", + program_name); + usage(program_name); + } + nisprinc = optarg; + break; + case 'r': + if ((princ != NULL) || (nisprinc != NULL) || + (nispasswd[0] != '\0')) { + fprintf(stderr, + "%s: cannot combine any other option with -r.\n", + program_name); + usage(program_name); + } + add_op = 0; + if ((argc == 2) && getuid() == 0) { + struct stat buf; + int ret; + char *ptr, ansbuf[BUFSIZ]; + + ret = stat("/var/nis/data", &buf); + if (ret == 0) { + fputs("\n\tThis machine appears to be a NIS+ server!!!\n", + stderr); + fputs("Are you sure you want to REMOVE it's credential?(Y/N) ", + stderr); + ansbuf[0] = '\0'; + ptr = gets(ansbuf); + if (ansbuf[0] != 'y' && + ansbuf[0] != 'Y') { + fputs("\nOkay not REMOVING this NIS+ server's credential\n", + stderr); + exit(1); + } + } + } + break; + case 'l': + if (add_op == 0) { + fprintf(stderr, + "%s: cannot combine any other option with -r.\n", + program_name); + usage(program_name); + } + strncpy(nispasswd, optarg, sizeof (des_block)); + nispasswd[sizeof (des_block)] = '\0'; + break; + case '?' : + default : + fprintf(stderr, "%s: unrecognized option.\n", + program_name); + usage(program_name); + break; + } + } + + + if (add_op) { + if (optind == argc) { + fprintf(stderr, + "%s: Authentication Flavor name required.\n", + program_name); + usage(program_name); + } + flavor = argv[optind++]; + + if (optind < argc) { + domain = argv[optind++]; + explicit_domain++; + } + } else { + if (optind < argc) + nisprinc = argv[optind++]; + if (optind < argc) { + domain = argv[optind++]; + explicit_domain++; + } + } + + if (optind < argc) { + fprintf(stderr, "%s: too many parameters\n", program_name); + usage(program_name); + } + + /* Information about user running nisaddcred */ + my_uid = geteuid(); + my_nisname = default_principal(nis_local_directory()); + my_host = nis_local_host(); + my_group = nis_local_group(); + + /* + * Do a centralized check of nisprinc to make sure that + * it has a trailing ".", which is a common mistake that + * we can easily correct. + */ + if (nisprinc) { + len = strlen(nisprinc); + if (len == 0 || nisprinc[len-1] != '.') { + p = malloc(len + 2); /* 1 for '.' and 1 for '\0' */ + if (p == NULL) { + fprintf(stderr, + "%s: out of memory\n", program_name); + exit(1); + } + strcpy(p, nisprinc); + strcat(p, "."); + nisprinc = p; + } + } + + /* + * Do the same checking for domain, if it was specified on + * the command line. + */ + if (explicit_domain) { + len = strlen(domain); + if (len == 0 || domain[len-1] != '.') { + p = malloc(len + 2); /* 1 for '.' and 1 for '\0' */ + if (p == NULL) { + fprintf(stderr, + "%s: out of memory\n", program_name); + exit(1); + } + strcpy(p, domain); + strcat(p, "."); + domain = p; + } + } + + if (add_op) { + perform_add(flavor, domain, nisprinc, princ); + } else + perform_remove(domain, nisprinc); + exit(0); +} + +/* + * Get the password entry corresponding to 'uid' in the specified + * domain. If the domain is NULL and we can't get the information, + * then we get it locally (if USE_LOCAL_INFO is defined). + */ +struct passwd * +domain_getpwuid(domain, uid) + char *domain; + int uid; +{ + struct passwd *pw; + nis_error err; + struct passwd *getpwuid_nisplus_master(); + + pw = getpwuid_nisplus_master(domain, uid, &err); + +#ifdef USE_LOCAL_INFO + /* + * If no domain was specified on the command line and we didn't + * get a password entry from NIS+, then try getting it locally. + */ + if (! explicit_domain && pw == 0) + pw = getpwuid(uid); +#endif /* USE_LOCAL_INFO */ + + if (pw == 0) { + if (err == NIS_NOTFOUND) + fprintf(stderr, + "%s: no password entry found for uid %d\n", + program_name, uid); + else + fprintf(stderr, + "%s: could not get the password entry for uid %d: %s\n", + program_name, uid, nis_sperrno(err)); + } + + return (pw); +} + +/* + * Get the shadow entry corresponding to 'name' in the specified + * domain. If the domain is NULL and we can't get the information, + * then we get it locally (if USE_LOCAL_INFO is defined). + */ +struct spwd * +domain_getspnam(domain, name) + char *domain; + char *name; +{ + struct spwd *spw; + nis_error err; + struct spwd *getspnam_nisplus_master(); + + spw = getspnam_nisplus_master(domain, name, &err); + +#ifdef USE_LOCAL_INFO + /* + * If no domain was specified on the command line and we didn't + * get a shadow entry from NIS+, then try getting it locally. + */ + if (! explicit_domain && spw == 0) + spw = getspnam(name); +#endif /* USE_LOCAL_INFO */ + + if (spw == 0) { + if (err == NIS_NOTFOUND) + fprintf(stderr, + "%s: no password entry found for user %s\n", + program_name, name); + else + fprintf(stderr, + "%s: could not get the password entry for user %s: %s\n", + program_name, name, nis_sperrno(err)); + } + + return (spw); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.h b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.h new file mode 100644 index 0000000000..8e8f99b2cf --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.h @@ -0,0 +1,44 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisaddcred.h + * + * Copyright (c) 1988-1995 Sun Microsystems, Inc. + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#define MAXIPRINT (11) /* max length of printed integer */ +#define CRED_TABLE "cred.org_dir" + +extern nis_object *init_entry(void); +extern char *default_principal(char *); +extern char *program_name; +extern uid_t my_uid; +extern nis_name my_nisname; +extern char *my_host; +extern char *my_group; +extern char nispasswd[]; +extern int explicit_domain; +extern struct passwd *getpwuid_nisplus_master(uid_t, nis_error *); +extern int addonly; diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddent.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddent.c new file mode 100644 index 0000000000..da972cd52f --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddent.c @@ -0,0 +1,5226 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 1988-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisaddent.c + * + * utility to add/merge /etc files and YP dbm files into nis+ tables + */ + +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <ndbm.h> +#include <sys/param.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/nis_dhext.h> +#include <pwd.h> +#include <auth_attr.h> +#include <exec_attr.h> +#include <prof_attr.h> +#include <user_attr.h> +#undef GROUP +#undef opaque +#include <bsm/libbsm.h> +#include <nss_dbdefs.h> + +#define NIS_ALL_ACC (NIS_READ_ACC|NIS_MODIFY_ACC|NIS_CREATE_ACC|NIS_DESTROY_ACC) +#define NIS_WORLD_MASK(a) (a) +#define NIS_GROUP_MASK(a) (a << 8) +#define NIS_OWNER_MASK(a) (a << 16) +#define NIS_NOBODY_MASK(a) (a << 24) + +/* same as in nis_util.c (we really need a .h) */ +#define NIS_SRC_DEFAULT 0 +#define NIS_SRC_ENV 1 +#define NIS_SRC_ARG 2 + +#define BIGBUF 8192 + + +extern int optind; +extern char *optarg; + +extern nis_object nis_default_obj; +extern int nis_default_access_src; + +extern char *__nis_quote_key(const char *, char *, int); +extern char *strpbrk_quotes(char *, char *); +extern char *strtok_quotes(char *, char *); +extern char *strchr_quotes(char *, char); + +extern char *_strtok_escape(char *, char *, char **); /* from libnsl */ + +/* + * Temporary files used by merge_file. The 'created' variables + * record whether or not we created the file. An exit routine + * will unlink the files if they were created. + */ +char tmpdirbuf[MAXPATHLEN]; +char tmppagbuf[MAXPATHLEN]; +int created_dir; +int created_pag; + +struct file_loc { + off_t offset; + size_t size; +}; + +struct line_buf { + char *str; + int len; + int alloc; +}; + + +struct ttypelist_t { + char *ttype; /* type tag */ + char *ypmap; /* nis map to use */ + char *nistbl; /* nis+ table to use */ + char *ta_type; /* nis+ table type */ + char *clrsrch; /* nis+ search critera to clear table */ + int niskeycol; /* nis+ key column */ + int filekeycol; /* file key column */ + char *filesep; /* chars marking columns in files */ + char *(*dbmniskey)(); /* routine to turn dbm key into nis key */ + datum (*nisdbmkey)(); /* routine to turn nis entry into dbm key */ + datum (*filedbmkey)(); /* routine to turn file line into dbm key */ + int (*genent)(); /* routine to turn line into nis+ entries */ + void (*dump)(); /* routine to print nis+ table */ + nis_result *(*dump_match)(); /* routine to match entries */ + int (*filedbmline)(); /* routine to turn file line into dbm line */ + void (*filetodbm)(); /* routine to convert to dbm key/content */ + char *(*fetchdbm)(); /* routine to fetch a nis map line */ + void (*printfkeystr)(); /* routine to print keystr for -v */ +} *tt; + +char *nisdomain; +mechanism_t **mechs; /* A list of mechs */ + +char ta_name[NIS_MAXNAMELEN]; +char ta_type[BUFSIZ]; +nis_object *ta_obj; + +FILE *etcf = 0; +DBM *dbmf = 0; + +#define OP_ADD 0 +#define OP_REPLACE 1 +#define OP_MERGE 2 +#define OP_DUMP 3 + +#define F_VERBOSE 0x1 +#define F_PASSWD 0x2 +#define F_QUICK 0x4 + +int exit_val = 0; + +unsigned flags = 0; +int oldpubkeymode = 0; + +unsigned nent_add = 0; +unsigned nent_del = 0; + +ulong_t fpath = 0, allres = 0, master = 0; + +int user_attr_keycol[USERATTR_DB_NKEYCOL] = { + USERATTR_KEYCOL0, USERATTR_KEYCOL1 +}; + +int exec_attr_keycol[EXECATTR_DB_NKEYCOL] = { + EXECATTR_KEYCOL0, EXECATTR_KEYCOL1, EXECATTR_KEYCOL2 +}; + +#define PARSE_ERR_MSG_LEN 512 +static char parse_err_msg [PARSE_ERR_MSG_LEN]; + +char null_string[] = ""; + +/* the followings are defined to fix cstyle complain of line too long */ +#define ENTRY_COL(c) \ + zo_data.objdata_u.en_data.en_cols.en_cols_val[(c)] + +#define OBJ_COL_VAL(i, c) \ + res->objects.objects_val[(i)].ENTRY_COL(c).ec_value.ec_value_val + +/* + * The following is a version of OBJ_COL_VALUE that returns a pointer + * to an empty string, rather than a NULL pointer, if the entry does not + * exist. Involves two derefs but this only a cmd line app so performance + * should not be an issue. The null string is defined as a constant so a new + * copy of it is not created for each macro invocation. + */ +char *nullStr = ""; +#define OBJ_COL_VAL_NO_NULL(i, c) \ + OBJ_COL_VAL(i, c) == NULL ? nullStr : OBJ_COL_VAL(i, c) + +#define KEYVAL(c) \ + entry->ENTRY_COL(c).ec_value.ec_value_val + +/* get either key value or, if it is null, an empty string */ +#define NKEYVAL(c) ((KEYVAL(c))?(KEYVAL(c)):"") + +#define OBJ_COL_NAME(c) \ + ta_obj->zo_data.objdata_u.ta_data.ta_cols.ta_cols_val[(c)].tc_name + +void +usage() +{ + fprintf(stderr, "usage:\tnisaddent [-D defaults] [-Parv]"); + fprintf(stderr, " [-t table] type [nisdomain]\n\n"); + + fprintf(stderr, "\tnisaddent [-D defaults] [-Parmv] -f file"); + fprintf(stderr, " [-t table] type [nisdomain]\n\n"); + + fprintf(stderr, "\tnisaddent [-D defaults] [-Parmv] -y ypdomain"); + + fprintf(stderr, " [-Y map] [-t table] type\n\t\t[nisdomain]\n\n"); + fprintf(stderr, + " nisaddent [-AM] -d [-t table] type [nisdomain]\n"); + exit(1); +} + +void +line_buf_expand(line) + struct line_buf *line; +{ + if (line->alloc == 0) { + line->alloc = BUFSIZ; + line->str = (char *)malloc(line->alloc); + } else { + line->alloc += BUFSIZ; + line->str = (char *)realloc(line->str, line->alloc); + } + + if (line->str == 0) { + fprintf(stderr, "line_buf_expand: out of memory\n"); + exit(1); + } +} + +void +line_buf_init(line) + struct line_buf *line; +{ + memset((char *)line, 0, sizeof (*line)); + line_buf_expand(line); +} + +void +print2buf(struct line_buf *line, char *toprint) +{ + int toprintlen = 0; + + /* has print buffer line been exhausted */ + if ((toprintlen = strlen(toprint)) + line->len > (line->alloc - 1)) { + do { + if (line->alloc == 0) { + line->alloc = BUFSIZ; + line->str = (char *)malloc(line->alloc); + } else { + line->alloc += BUFSIZ; + line->str = (char *)realloc(line->str, + line->alloc); + } + if (line->str == 0) { + fprintf(stderr, "print2buf: out of memory\n"); + exit(1); + } + } while (toprintlen > line->alloc); + } + /* now add new 'toprint' data to buffer */ + strcat(line->str, toprint); + line->len += toprintlen; +} + +void +print2buf_init(struct line_buf *line) +{ + memset((char *)line, 0, sizeof (*line)); + line->str = NULL; + line->alloc = 0; + line->len = 0; + line_buf_expand(line); +} + +void +print2buf_destroy(struct line_buf *line) +{ + free(line->str); + line->str = NULL; + line->alloc = 0; + line->len = 0; +} + + +int +col_equal(tc, ec1, ec2, mod) + table_col *tc; + entry_col *ec1; + entry_col *ec2; + int mod; +{ + /* ignore unset columns in modified entries */ + if (mod && (ec1->ec_value.ec_value_val == 0)) + return (1); + + if (ec1->ec_value.ec_value_len != ec2->ec_value.ec_value_len) + return (0); + + if (ec1->ec_value.ec_value_val == 0 || + ec2->ec_value.ec_value_val == 0) { + if (ec1->ec_value.ec_value_val == 0 && + ec2->ec_value.ec_value_val == 0) + return (1); + else + return (0); + } + + if (tc->tc_flags & TA_CASE) + return (strncasecmp(ec1->ec_value.ec_value_val, + ec2->ec_value.ec_value_val, + ec1->ec_value.ec_value_len) == 0); + else + return (strncmp(ec1->ec_value.ec_value_val, + ec2->ec_value.ec_value_val, + ec1->ec_value.ec_value_len) == 0); +} + +int +entry_equal(e1, e2, mod) + nis_object *e1; + nis_object *e2; + int mod; +{ + int i; + table_col *tc; + entry_col *ec1, *ec2; + + for (i = e1->zo_data.objdata_u.en_data.en_cols.en_cols_len, + tc = ta_obj->zo_data.objdata_u.ta_data.ta_cols.ta_cols_val, + ec1 = e1->zo_data.objdata_u.en_data.en_cols.en_cols_val, + ec2 = e2->zo_data.objdata_u.en_data.en_cols.en_cols_val; + i > 0; + tc++, ec1++, ec2++, i--) + if (!col_equal(tc, ec1, ec2, mod)) + return (0); + + return (1); +} + +int +entry_inresult(e, r, mod) + nis_object *e; + nis_result *r; + int mod; +{ + int i; + + for (i = r->objects.objects_len-1; i >= 0; i--) { + if (entry_equal(e, &(r->objects.objects_val[i]), mod)) + return (1); + } + + return (0); +} + +int +addentry(table, entry, udata, mod) + nis_name table; + nis_object *entry; + void *udata; + int mod; +{ + nis_result *ares; + + if (udata && entry_inresult(entry, (nis_result*)udata, mod)) + return (0); + + if (flags & F_VERBOSE) + (*(tt->printfkeystr))("adding/updating \"%s\"\n", entry); + + if (mod) { + ares = nis_modify_entry(table, entry, 0); + if (ares->status == NIS_NOTFOUND) + ares = nis_add_entry(table, entry, 0); + } else + ares = nis_add_entry(table, entry, ADD_OVERWRITE); + if (ares->status == NIS_SUCCESS) + nent_add++; + else { + if (!(flags & F_VERBOSE)) + (*(tt->printfkeystr))("adding/updating " + "\"%s\"\n", entry); + if (mod) + nis_perror(ares->status, "can't modify entry"); + else + nis_perror(ares->status, "can't add entry"); + } + nis_freeresult(ares); + + return (0); +} + +int +matchentry(table, entry, udata, mod) + nis_name table; + nis_object *entry; + void *udata; + int mod; +{ + datum key, val; + + if (udata && entry_inresult(entry, (nis_result*)udata, mod)) + return (1); + + return (0); +} + + +int +removeentry(table, entry) + nis_name table; + nis_object *entry; +{ + nis_result *rres; + + if (flags & F_VERBOSE) + (*(tt->printfkeystr))("removing %s\n", entry); + + rres = nis_remove_entry(table, entry, 0); + if (rres->status == NIS_SUCCESS) + nent_del++; + else { + if (!(flags & F_VERBOSE)) + (*(tt->printfkeystr))("removing \"%s\"\n", entry); + nis_perror(rres->status, "can't remove entry"); + } + nis_freeresult(rres); + + return (0); +} + + +/* + * return a string of tab characters that when concatenated with the + * specified string leave the cursor at the beginning of the specified + * column. if the string is too long, then return a single space. the + * specified column must be less than 10. + */ + +char * +tabtocol(str, col) + char *str; + int col; +{ + int c, l; + char *p; + + for (c = l = 0, p = str; *p; p++) { + if (*p == '\t' || ++l == 8) { + c++; + l = 0; + } + } + + if (c < col) + return (&("\t\t\t\t\t\t\t\t\t\t"[10-col+c])); + + return (" "); +} + +char * +dbmniskey_publickey(datum key) +{ + static char buf[NIS_MAXNAMELEN]; + char netname[MAXNETNAMELEN], *tmp; + + strcpy(netname, key.dptr); + if (tmp = strchr(netname, ':')) + *tmp = '\0'; + + sprintf(buf, "%s=%.*s", OBJ_COL_NAME(tt->niskeycol), + strlen(netname), netname); + + return (buf); +} + +char * +dbmniskey_attr(datum key) +{ + int i, numkeycol; + int *keycol; + char *p, *s; + static char buf[NIS_MAXNAMELEN]; + + s = key.dptr; + buf[0] = 0; + + if (strcmp(tt->ttype, NSS_DBNAM_EXECATTR) == 0) { + numkeycol = EXECATTR_DB_NKEYCOL; + keycol = exec_attr_keycol; + } else if (strcmp(tt->ttype, NSS_DBNAM_USERATTR) == 0) { + numkeycol = USERATTR_DB_NKEYCOL; + keycol = user_attr_keycol; + } + + for (i = 0; i < numkeycol; i++) { + p = strpbrk(s, tt->filesep); + if (p == NULL) { + (void) snprintf(buf, NIS_MAXNAMELEN, "%s%s=%.*s", buf, + OBJ_COL_NAME(keycol[i]), key.dsize, s); + break; + } + (void) snprintf(buf, NIS_MAXNAMELEN, "%s%s=%.*s%s", buf, + OBJ_COL_NAME(keycol[i]), (p - s), s, tt->filesep); + s = ++p; + } + if (p = strrchr(buf, ':')) + *p = 0; + + return (buf); +} + +char * +dbmniskey(key) + datum key; +{ + static char buf[NIS_MAXNAMELEN]; + + sprintf(buf, "%s=%.*s", OBJ_COL_NAME(tt->niskeycol), + key.dsize, key.dptr); + + return (buf); +} + +datum +nisdbmkey(entry) + nis_object *entry; +{ + static char buf[BUFSIZ+1]; + datum key; + + strcpy(buf, NKEYVAL(tt->niskeycol)); + + key.dptr = buf; + key.dsize = strlen(buf); + + return (key); +} + +datum +nisdbmkey_attr(nis_object *entry) +{ + int i; + int numkeycol; + int *keycol; + char *p, *s; + datum key; + static char buf[BUFSIZ+1]; + + buf[0] = 0; + + if (strcmp(tt->ttype, NSS_DBNAM_EXECATTR) == 0) { + numkeycol = EXECATTR_DB_NKEYCOL; + keycol = exec_attr_keycol; + } else if (strcmp(tt->ttype, NSS_DBNAM_USERATTR) == 0) { + numkeycol = USERATTR_DB_NKEYCOL; + keycol = user_attr_keycol; + } + + for (i = 0; i < numkeycol; i++) { + s = NKEYVAL(keycol[i]); + if ((s == NULL) || (strcmp(s, "") == 0)) + continue; + (void) snprintf(buf, BUFSIZ, "%s%s%s", buf, s, tt->filesep); + } + if (p = strrchr(buf, ':')) + *p = 0; + + key.dptr = buf; + key.dsize = strlen(buf); + + return (key); +} + +datum +nisdbmkey_publickey(entry) + nis_object *entry; +{ + static char buf[BUFSIZ+1]; + datum key; + + if (mechs) { + char mechalias[MECH_MAXALIASNAME], keylen[256]; + keylen_t bitlen; + algtype_t algtype; + + if (!__nis_authtype2mechalias(NKEYVAL(1), mechalias, + MECH_MAXALIASNAME)) + goto nomechs; + + if (__nis_translate_mechanism(mechalias, &bitlen, + &algtype) < 0) + goto nomechs; + + if (bitlen == 192) + strcpy(keylen, "DES"); + else + snprintf(keylen, 256, "%d", bitlen); + snprintf(buf, BUFSIZ, "%s:%s:%d", NKEYVAL(2), keylen, algtype); + } else { +nomechs: + strcpy(buf, NKEYVAL(2)); + } + key.dptr = buf; + key.dsize = strlen(buf); + + return (key); +} + +datum +filedbmkey_attr(char *line) +{ + int i, j, skip; + int numkeycol; + int *keycol; + char *p = NULL; + char *q = NULL; + char *s = NULL; + static char buf[BUFSIZ+1]; + datum key; + + s = line; + buf[0] = 0; + + if (strcmp(tt->ttype, NSS_DBNAM_EXECATTR) == 0) { + numkeycol = EXECATTR_DB_NKEYCOL; + keycol = exec_attr_keycol; + } else if (strcmp(tt->ttype, NSS_DBNAM_USERATTR) == 0) { + numkeycol = USERATTR_DB_NKEYCOL; + keycol = user_attr_keycol; + } + + for (i = 0; i < numkeycol; i++) { + for (j = keycol[i]; j >= 0; j--) { + p = strpbrk(s, tt->filesep); + if (j > 0) { + if (p == NULL) { + key.dptr = 0; + key.dsize = 0; + return (key); + } + skip = strspn(p, tt->filesep); + if (skip > 1) { + if ((j = j - skip) <= 0) + j = 1; + } + s = p + skip; + } + } + if (p) { + sprintf(buf, "%s%.*s:", buf, (p - s), s); + } else { + if (buf[0] == 0) + strcpy(buf, s); + break; + } + } + + if (q = strrchr(buf, ':')) + *q = 0; + + key.dptr = buf; + key.dsize = strlen(buf); + + return (key); +} + +datum +filedbmkey_publickey(line) + char *line; +{ + static char buf[BUFSIZ+1]; + int i; + char *p; + datum key; + + + if (mechs) { + char netname[MAXNETNAMELEN], keys[8704]; + char keylen[256]; + char *pubkey, *privkey, *algtype; + int bitlen = 0; + + sscanf(line, "%s %s", netname, keys); + + pubkey = keys; + /* + * We don't use the private key, but we need to get to + * the algtype + */ + if (privkey = strchr(keys, ':')) { + *privkey++ = '\0'; + /* Get rid of extra :'s */ + if (*privkey == ':') + privkey++; + } + if (algtype = strchr(privkey, ':')) + *algtype++ = '\0'; + else + algtype = "0"; + + bitlen = (strlen(pubkey) / 2) * 8; + if (bitlen == 192) + strcpy(keylen, "DES"); + else + snprintf(keylen, 256, "%d", bitlen); + + snprintf(buf, BUFSIZ, "%s:%s:%s", netname, keylen, algtype); + } else { + for (i = tt->filekeycol; i >= 0; i--) { + p = strpbrk(line, tt->filesep); + if (i > 0) { + if (p == 0) { + key.dptr = 0; + key.dsize = 0; + return (key); + } + line = p + strspn(p, tt->filesep); + } + } + + if (p) + sprintf(buf, "%.*s", p-line, line); + else + strcpy(buf, line); + } + + key.dptr = buf; + key.dsize = strlen(buf); + + return (key); +} + +/* + * Note that most of the filedbmkey() code is duplicated in + * filedbmkey_netid() below. + */ +datum +filedbmkey(line) + char *line; +{ + static char buf[BUFSIZ+1]; + int i; + char *p; + datum key; + + for (i = tt->filekeycol; i >= 0; i--) { + p = strpbrk(line, tt->filesep); + if (i > 0) { + if (p == 0) { + key.dptr = 0; + key.dsize = 0; + return (key); + } + line = p + strspn(p, tt->filesep); + } + } + + if (p) + sprintf(buf, "%.*s", p-line, line); + else + strcpy(buf, line); + + key.dptr = buf; + key.dsize = strlen(buf); + + return (key); +} + +/* + * filedbmkey_netid() duplicates filedbmkey() above, but also + * removes trailing dots. + */ +datum +filedbmkey_netid(line) + char *line; +{ + static char buf[BUFSIZ+1]; + int i; + char *p; + datum key; + + for (i = tt->filekeycol; i >= 0; i--) { + p = strpbrk(line, tt->filesep); + if (i > 0) { + if (p == 0) { + key.dptr = 0; + key.dsize = 0; + return (key); + } + line = p + strspn(p, tt->filesep); + } + } + + if (p) + sprintf(buf, "%.*s", p-line, line); + else + strcpy(buf, line); + + key.dptr = buf; + key.dsize = strlen(buf); + + /* Strip any trailing dot from key */ + if (key.dsize > 0 && buf[key.dsize-1] == '.') { + buf[key.dsize-1] = '\0'; + key.dsize--; + } + + return (key); +} + +int +blankline(line) + char *line; +{ + char *p; + + for (p = line; *p; p++) + if (*p != ' ' && *p != '\t') + return (0); + return (1); +} + +/* + * Read a line into a line_buf starting at offset 'n'. We expand the + * line_buf when needed. We read until '\n' or EOF. If we don't read + * any characters before EOF we return 0. Otherwise, we return a pointer + * to the string read. + */ + +char * +fget_line_at(line, n, fp) + struct line_buf *line; + int n; + FILE *fp; +{ + int c; + + line->len = n; + while (1) { + c = fgetc(fp); + if (c == -1) + break; + if (line->len >= line->alloc) + line_buf_expand(line); + line->str[line->len++] = c; + if (c == '\n') + break; + } + + /* null terminate */ + if (line->len >= line->alloc) + line_buf_expand(line); + line->str[line->len++] = 0; + + /* if we read no characters, return NULL to indicate EOF */ + if (line->str[0] == '\0') + return (0); + + return (line->str); +} + + +int +filedbmline(line, etcf, lineno, loc) + struct line_buf *line; + FILE *etcf; + int *lineno; + struct file_loc *loc; +{ + int len = 0; + + loc->offset = ftell(etcf); + while (1) { + if (fget_line_at(line, len, etcf) == 0) + return (0); + + if (lineno) + (*lineno)++; + + len = strlen(line->str); + if (line->str[len-1] == '\n') { + line->str[len-1] = 0; + len -= 1; + } + + if (!blankline(line->str)) + break; + + len = 0; + loc->offset = ftell(etcf); + } + + loc->size = len; + return (1); +} + +int +filedbmline_comment(line, etcf, lineno, loc) + struct line_buf *line; + FILE *etcf; + int *lineno; + struct file_loc *loc; +{ + int len = 0; + + loc->offset = ftell(etcf); + while (1) { + if (fget_line_at(line, len, etcf) == 0) + return (0); + + if (lineno) + (*lineno)++; + + len = strlen(line->str); + if (len >= 2 && + line->str[0] != '#' && + line->str[len-2] == '\\' && line->str[len-1] == '\n') { + line->str[len-2] = 0; + len -= 2; + continue; /* append next line at end */ + } + + if (line->str[len-1] == '\n') { + line->str[len-1] = 0; + len -= 1; + } + + if (!blankline(line->str) && line->str[0] != '#') + break; + + len = 0; + loc->offset = ftell(etcf); + } + + loc->size = len; + return (1); +} + +int +filedbmline_plus(line, etcf, lineno, loc) + struct line_buf *line; + FILE *etcf; + int *lineno; + struct file_loc *loc; +{ + int len = 0; + + loc->offset = ftell(etcf); + while (1) { + if (fget_line_at(line, len, etcf) == 0) + return (0); + + if (lineno) + (*lineno)++; + + len = strlen(line->str); + if (line->str[len-1] == '\n') { + line->str[len-1] = 0; + len -= 1; + } + + if (!blankline(line->str) && + line->str[0] != '+' && line->str[0] != '-') + break; + + len = 0; + loc->offset = ftell(etcf); + } + + loc->size = len; + return (1); +} + + +char * +fetchdbm(key) + datum key; +{ + static char line[BUFSIZ+1]; + datum val; + + val = dbm_fetch(dbmf, key); + if (val.dptr) { + sprintf(line, "%.*s", val.dsize, val.dptr); + return (line); + } + return (0); +} + +char * +fetchdbm_addkey(key) + datum key; +{ + static char line[BUFSIZ+1]; + datum val; + + val = dbm_fetch(dbmf, key); + if (val.dptr) { + sprintf(line, "%.*s %.*s", + key.dsize, key.dptr, + val.dsize, val.dptr); + return (line); + } else if (key.dptr[key.dsize-1] == NULL) { + key.dsize -= 1; + val = dbm_fetch(dbmf, key); + if (val.dptr) { + sprintf(line, "%.*s %.*s", + key.dsize, key.dptr, + val.dsize, val.dptr); + return (line); + } + } + return (0); +} + +char * +fetchfile(key) + datum key; +{ + static struct line_buf line; + char *p; + datum content; + datum lkey; + struct file_loc loc; + + content = dbm_fetch(dbmf, key); + if (content.dptr == 0) + return (0); /* not in file */ + memcpy((char *)&loc, (char *)content.dptr, sizeof (loc)); + + if (fseek(etcf, loc.offset, SEEK_SET) == -1) { + fprintf(stderr, "error seeking on file\n"); + exit(1); + } + + if (line.alloc == 0) + line_buf_init(&line); + + if (tt->filedbmline(&line, etcf, 0, &loc)) + return (line.str); + return (0); +} +/* + * Convert a line into key and content. The key is included + * in the content. + */ +void +filetodbm(line, key, content) + char *line; + datum *key; + datum *content; +{ + int i; + char *p; + + content->dptr = line; + content->dsize = strlen(line); + + for (i = tt->filekeycol; i >= 0; i--) { + p = strpbrk(line, tt->filesep); + if (i > 0) { + if (p == 0) { + key->dptr = 0; + key->dsize = 0; + return; + } + line = p + strspn(p, tt->filesep); + } + } + + if (p) { + key->dptr = line; + key->dsize = p-line; + } else { + key->dptr = line; + key->dsize = strlen(line); + } +} + +/* + * Convert a line into key and content. Unlike filetodbm, the key + * is not included in the content. + */ +void +filetodbm_keyvalue(line, key, content) + char *line; + datum *key; + datum *content; +{ + int i; + char *p; + + for (i = tt->filekeycol; i >= 0; i--) { + p = strpbrk(line, tt->filesep); + if (i > 0) { + if (p == 0) { + key->dptr = 0; + key->dsize = 0; + return; + } + line = p + strspn(p, tt->filesep); + } + } + + if (p) { + key->dptr = line; + key->dsize = p-line; + line = p + strspn(p, tt->filesep); + content->dptr = line; + content->dsize = strlen(line); + } else { + key->dptr = line; + key->dsize = strlen(line); + content->dptr = ""; + content->dsize = 0; + } +} + +/* + * Convert a line into key and content. The key is included + * in the content. + */ +void +filetodbm_attr(char *line, datum *key, datum *content) +{ + int i, j, skip, numkeycol; + int *keycol; + char *p; + + content->dptr = line; + content->dsize = strlen(line); + + if (strcmp(tt->ttype, NSS_DBNAM_EXECATTR) == 0) { + numkeycol = EXECATTR_DB_NKEYCOL; + keycol = exec_attr_keycol; + } else if (strcmp(tt->ttype, NSS_DBNAM_USERATTR) == 0) { + numkeycol = USERATTR_DB_NKEYCOL; + keycol = user_attr_keycol; + } + + for (i = 0; i < numkeycol; i++) { + for (j = keycol[i]; j >= 0; j--) { + p = strpbrk(line, tt->filesep); + if (j > 0) { + if (p == NULL) { + key->dptr = 0; + key->dsize = 0; + return; + } + skip = strspn(p, tt->filesep); + if (skip > 1) { + if ((j = j - skip) <= 0) + j = 1; + } + line = p + skip; + } + } + } + + if (p) { + key->dptr = line; + key->dsize = p - line; + } else { + key->dptr = line; + key->dsize = strlen(line); + } +} + +void +printfkeystr_0(fstr, entry) + char *fstr; + nis_object *entry; +{ + char keystr[BUFSIZ]; + + sprintf(keystr, "%s", NKEYVAL(0)); + printf(fstr, keystr); +} + +void +printfkeystr_01(fstr, entry) + char *fstr; + nis_object *entry; +{ + char keystr[BUFSIZ]; + + sprintf(keystr, "%s %s", NKEYVAL(0), NKEYVAL(1)); + printf(fstr, keystr); +} + +void +printfkeystr_cname(fstr, entry) + char *fstr; + nis_object *entry; +{ + char keystr[BUFSIZ]; + + if (strcasecmp(NKEYVAL(0), NKEYVAL(1)) == 0) + sprintf(keystr, "%s", NKEYVAL(0)); + else + sprintf(keystr, "%s (%s)", NKEYVAL(0), NKEYVAL(1)); + + printf(fstr, keystr); +} + + +#define GENENT_OK 0 +#define GENENT_PARSEERR 1 +#define GENENT_CBERR 2 +#define GENENT_ERR 3 + + +/* + * /etc/hosts + * nis+ table: (hosts_tbl) cname, name, addr, comment + * + */ + +int +genent_hosts(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1]; + char *t; + nis_object eobj; + entry_col ecol[4]; + char *cname; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * comment (col 3) + */ + t = strchr(buf, '#'); + if (t) { + *t++ = 0; + ecol[3].ec_value.ec_value_val = t; + ecol[3].ec_value.ec_value_len = strlen(t)+1; + } else { + ecol[3].ec_value.ec_value_val = 0; + ecol[3].ec_value.ec_value_len = 0; + } + + /* + * addr(col 2) + */ + if ((t = strtok(buf, " \t")) == 0) { + strcpy(parse_err_msg, "no host"); + return (GENENT_PARSEERR); + } + ecol[2].ec_value.ec_value_val = t; + ecol[2].ec_value.ec_value_len = strlen(t)+1; + + /* + * cname (col 0) + */ + if ((t = strtok(NULL, " \t")) == 0) { + strcpy(parse_err_msg, "no cname"); + return (GENENT_PARSEERR); + } + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + cname = t; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 4; + + if (!cback) + cback = addentry; + + /* + * name (col 1) + */ + do { + /* + * don't clobber comment in canonical entry + */ + if (t != cname && strcasecmp(t, cname) == 0) + continue; + + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + + if ((*cback)(ta_name, &eobj, udata, 0)) + return (GENENT_CBERR); + + /* + * only put comment in canonical entry + */ + ecol[3].ec_value.ec_value_val = 0; + ecol[3].ec_value.ec_value_len = 0; + + } while (t = strtok(NULL, " \t")); + + return (GENENT_OK); +} + +/* + * List all of the entries that have the same cname as the passed + * entry. + */ +nis_result * +dump_match_cname(table, entry) + nis_name table; + nis_object *entry; +{ + nis_result *res = 0; + char *c0, *c1, *c2; + char srch[NIS_MAXNAMELEN]; + + if ((c0 = KEYVAL(0)) && *c0 && + (c1 = KEYVAL(1)) && (c2 = KEYVAL(2)) && + (strcasecmp(c0, c1) == 0)) { + sprintf(srch, "[cname=%s],%s", c0, table); + res = nis_list(srch, allres|master, 0, 0); + } + + return (res); +} + +/* + * List all of the entries that have the same cname and address as the passed + * entry. + */ +nis_result * +dump_match_hosts(table, entry) + nis_name table; + nis_object *entry; +{ + nis_result *res = 0; + char *c0, *c1, *c2; + char srch[NIS_MAXNAMELEN]; + + if ((c0 = KEYVAL(0)) && *c0 && + (c1 = KEYVAL(1)) && (c2 = KEYVAL(2)) && + (strcasecmp(c0, c1) == 0)) { + sprintf(srch, "[cname=%s,addr=%s],%s", c0, c2, table); + res = nis_list(srch, allres|master, 0, 0); + } + + return (res); +} + +void +dump_hosts(res) + nis_result *res; +{ + int i, j; + char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c0a, *c1a; + + for (i = 0; i < res->objects.objects_len; i++) { + if ((c0 = OBJ_COL_VAL(i, 0)) && *c0 && + (c1 = OBJ_COL_VAL(i, 1)) && (c2 = OBJ_COL_VAL(i, 2)) && + (strcasecmp(c0, c1) == 0)) { + strcpy(buf, c2); + strcat(buf, tabtocol(buf, 2)); + strcat(buf, c0); + for (j = 0; j < res->objects.objects_len; j++) + if (j != i && + (c0a = OBJ_COL_VAL(j, 0)) && *c0a && + (c1a = OBJ_COL_VAL(j, 1)) && + strcasecmp(c0, c0a) == 0) { + strcat(buf, " "); + strcat(buf, c1a); + *c0a = 0; + } + if ((c3 = OBJ_COL_VAL(i, 3)) && !blankline(c3)) { + strcat(buf, tabtocol(buf, 5)); + strcat(buf, "#"); + strcat(buf, c3); + } + *c0 = 0; + printf("%s\n", buf); + } + } +} + + +/* + * /etc/passwd + * /etc/shadow + * nis+ table: (passwd_tbl) name, passwd, uid, gid, gcos, home, shell, shadow + * + */ + +int +genent_passwd(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1]; + char *s, *t; + nis_object eobj; + entry_col ecol[8]; + char *name, pname[NIS_MAXNAMELEN]; + char aname[NIS_MAXNAMELEN]; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + t = buf; + + /* ignore empty entries */ + if (*t == '\0') + return (GENENT_OK); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * name (col 0) + */ + if ((s = strchr(t, ':')) == 0) { + strcpy(parse_err_msg, "no password"); + return (GENENT_PARSEERR); + } + *s++ = 0; + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + name = t; + t = s; + + /* + * passwd (col 1) + */ + if ((s = strchr(t, ':')) == 0) { + strcpy(parse_err_msg, "no uid"); + return (GENENT_PARSEERR); + } + *s++ = 0; +#ifndef FOURDOTX + if ((dbmf && ! etcf) || (flags & F_PASSWD)) { +#endif + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + ecol[1].ec_flags = EN_CRYPT|EN_MODIFIED; +#ifndef FOURDOTX + } +#endif + t = s; + + /* + * uid (col 2) + */ + if ((s = strchr(t, ':')) == 0 || s == t) { + strcpy(parse_err_msg, "no gid"); + return (GENENT_PARSEERR); + } + *s++ = 0; + ecol[2].ec_value.ec_value_val = t; + ecol[2].ec_value.ec_value_len = strlen(t)+1; + ecol[2].ec_flags = EN_MODIFIED; + t = s; + + /* + * gid (col 3) + */ + if ((s = strchr(t, ':')) == 0 || s == t) { + strcpy(parse_err_msg, "no gcos"); + return (GENENT_PARSEERR); + } + *s++ = 0; + ecol[3].ec_value.ec_value_val = t; + ecol[3].ec_value.ec_value_len = strlen(t)+1; + ecol[3].ec_flags = EN_MODIFIED; + t = s; + + /* + * gcos (col 4) + */ + if ((s = strchr(t, ':')) == 0) { + strcpy(parse_err_msg, "no home"); + return (GENENT_PARSEERR); + } + *s++ = 0; + ecol[4].ec_value.ec_value_val = t; + ecol[4].ec_value.ec_value_len = strlen(t)+1; + ecol[4].ec_flags = EN_MODIFIED; + t = s; + + /* + * home (col 5) + */ + if ((s = strchr(t, ':')) == 0) { + strcpy(parse_err_msg, "no shell"); + return (GENENT_PARSEERR); + } + *s++ = 0; + ecol[5].ec_value.ec_value_val = t; + ecol[5].ec_value.ec_value_len = strlen(t)+1; + ecol[5].ec_flags = EN_MODIFIED; + t = s; + + /* + * shell (col 6) + */ + ecol[6].ec_value.ec_value_val = t; + ecol[6].ec_value.ec_value_len = strlen(t)+1; + ecol[6].ec_flags = EN_MODIFIED; + + /* + * NIS+ principal name + */ + sprintf(pname, "%s.%s", name, nisdomain); + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_owner = pname; + /* + * If access mode did not come from -D option, + * then ignore it. + */ + if (nis_default_access_src != NIS_SRC_ARG) + eobj.zo_access = NIS_OWNER_MASK(NIS_READ_ACC); + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 8; + + if (!cback) + cback = addentry; + + /* specify entry by name alone (ignore uid) */ + sprintf(aname, "[name=%s],%s", name, ta_name); + + if ((*cback)(aname, &eobj, udata, 1)) + return (GENENT_CBERR); + + return (GENENT_OK); +} + +void +dump_passwd(res) + nis_result *res; +{ + int i; + char *c0, *c1, *c2, *c3, *c4, *c5, *c6; + + for (i = 0; i < res->objects.objects_len; i++) { + if (c0 = OBJ_COL_VAL(i, 0)) { + c1 = OBJ_COL_VAL(i, 1); + c1 = c1 ? c1 : null_string; + c2 = OBJ_COL_VAL(i, 2); + c2 = c2 ? c2 : null_string; + c3 = OBJ_COL_VAL(i, 3); + c3 = c3 ? c3 : null_string; + c4 = OBJ_COL_VAL(i, 4); + c4 = c4 ? c4 : null_string; + c5 = OBJ_COL_VAL(i, 5); + c5 = c5 ? c5 : null_string; + c6 = OBJ_COL_VAL(i, 6); + c6 = c6 ? c6 : null_string; + printf("%s:%s:%s:%s:%s:%s:%s\n", + c0, +#ifndef FOURDOTX + "x", +#else + c1, +#endif + c2, c3, c4, c5, c6); + } + } +} + +int +genent_shadow(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1]; + char *s, *t; + nis_object eobj; + entry_col ecol[8]; + char *name, mname[NIS_MAXNAMELEN], pname[NIS_MAXNAMELEN]; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + t = buf; + + /* ignore empty entries */ + if (*t == '\0') + return (GENENT_OK); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * name (col 0) + */ + if ((s = strchr(t, ':')) == 0) { + strcpy(parse_err_msg, "no password"); + return (GENENT_PARSEERR); + } + *s++ = 0; + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + name = t; + t = s; + + /* + * passwd (col 1) + */ + if ((s = strchr(t, ':')) == 0) { + strcpy(parse_err_msg, "no shadow"); + return (GENENT_PARSEERR); + } + *s++ = 0; + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + ecol[1].ec_flags = EN_CRYPT|EN_MODIFIED; + t = s; + + /* + * shadow (col 7) + */ + ecol[7].ec_value.ec_value_val = t; + ecol[7].ec_value.ec_value_len = strlen(t)+1; + ecol[7].ec_flags = EN_MODIFIED; + + /* + * NIS+ principal name + */ + sprintf(pname, "%s.%s", name, nisdomain); + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_owner = pname; + /* + * If access mode did not come from -D option, + * then ignore it. + */ + if (nis_default_access_src != NIS_SRC_ARG) + eobj.zo_access = NIS_OWNER_MASK(NIS_READ_ACC); + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 8; + + if (!cback) + cback = addentry; + + /* specify entry by name alone (ignore uid) */ + sprintf(mname, "[name=%s],%s", name, ta_name); + + if ((*cback)(mname, &eobj, udata, 1)) + return (GENENT_CBERR); + + return (GENENT_OK); +} + +void +dump_shadow(res) + nis_result *res; +{ + int i; + char *c0, *c1, *c7; + + for (i = 0; i < res->objects.objects_len; i++) { + if (c0 = OBJ_COL_VAL(i, 0)) { + c1 = OBJ_COL_VAL(i, 1); + c1 = c1 ? c1 : null_string; + if (c7 = OBJ_COL_VAL(i, 7)) + printf("%s:%s:%s\n", c0, c1, c7); + else + printf("%s:%s::::::\n", c0, c1); + } + } +} + + +/* + * /etc/ethers + * nis+ table: (ethers_tbl) addr, name, comment + * + */ + +int +genent_ethers(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1]; + char *t; + nis_object eobj; + entry_col ecol[3]; + char *name; + char *addr, aname[NIS_MAXNAMELEN]; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * comment (col 2) + */ + t = strchr(buf, '#'); + if (t) { + *t++ = 0; + ecol[2].ec_value.ec_value_val = t; + ecol[2].ec_value.ec_value_len = strlen(t)+1; + } else { + ecol[2].ec_value.ec_value_val = 0; + ecol[2].ec_value.ec_value_len = 0; + } + + /* + * addr(col 0) + */ + if ((t = strtok(buf, " \t")) == 0) { + strcpy(parse_err_msg, "no name"); + return (GENENT_PARSEERR); + } + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + addr = t; + + /* + * name(col 1) + */ + if ((t = strtok(NULL, " \t")) == 0) { + strcpy(parse_err_msg, "no white space allowed in name"); + return (GENENT_PARSEERR); + } + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + name = t; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 3; + + if (!cback) + cback = addentry; + + /* specify entry by addr alone (ignore name) */ + sprintf(aname, "[addr=%s],%s", addr, ta_name); + + if ((*cback)(aname, &eobj, udata, 0)) + return (GENENT_CBERR); + + return (GENENT_OK); +} + +void +dump_ethers(res) + nis_result *res; +{ + int i; + char buf[BUFSIZ+1], *c0, *c1, *c2; + + for (i = 0; i < res->objects.objects_len; i++) { + if ((c0 = OBJ_COL_VAL(i, 0)) && + (c1 = OBJ_COL_VAL(i, 1))) { + strcpy(buf, c0); + strcat(buf, tabtocol(buf, 2)); + strcat(buf, c1); + if ((c2 = OBJ_COL_VAL(i, 2)) && + !blankline(c2)) { + strcat(buf, tabtocol(buf, 5)); + strcat(buf, "#"); + strcat(buf, c2); + } + printf("%s\n", buf); + } + } +} + + +/* + * /etc/group + * nis+ table: (group_tbl) name, passwd, gid, members + * + */ + +int +genent_group(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BIGBUF+1]; + char *s, *t; + nis_object eobj; + entry_col ecol[4]; + char *name; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + t = buf; + + /* ignore empty entries */ + if (*t == '\0') + return (GENENT_OK); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * name (col 0) + */ + if ((s = strchr(t, ':')) == 0) { + strcpy(parse_err_msg, "no passwd"); + return (GENENT_PARSEERR); + } + *s++ = 0; + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + name = t; + t = s; + + /* + * passwd (col 1) + */ + if ((s = strchr(t, ':')) == 0) { + strcpy(parse_err_msg, "no gid"); + return (GENENT_PARSEERR); + } + *s++ = 0; + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + ecol[1].ec_flags = EN_CRYPT; + t = s; + + /* + * gid (col 2) + */ + if ((s = strchr(t, ':')) == 0 || s == t) { + strcpy(parse_err_msg, "no members"); + return (GENENT_PARSEERR); + } + *s++ = 0; + ecol[2].ec_value.ec_value_val = t; + ecol[2].ec_value.ec_value_len = strlen(t)+1; + t = s; + + /* + * members (col 3) + */ + ecol[3].ec_value.ec_value_val = t; + ecol[3].ec_value.ec_value_len = strlen(t)+1; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 4; + + if (!cback) + cback = addentry; + + if ((*cback)(ta_name, &eobj, udata, 0)) + return (GENENT_CBERR); + + return (GENENT_OK); +} + +void +dump_group(res) + nis_result *res; +{ + int i; + char *c0, *c1, *c2, *c3; + + for (i = 0; i < res->objects.objects_len; i++) { + if (c0 = OBJ_COL_VAL(i, 0)) { + c1 = OBJ_COL_VAL(i, 1); + c1 = c1 ? c1 : null_string; + c2 = OBJ_COL_VAL(i, 2); + c2 = c2 ? c2 : null_string; + c3 = OBJ_COL_VAL(i, 3); + c3 = c3 ? c3 : null_string; + printf("%s:%s:%s:%s\n", c0, c1, c2, c3); + } + } +} + + +/* + * /etc/netmasks + * nis+ table: (netmasks_tbl) number, mask, comment + * + */ + +int +genent_netmasks(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1]; + char *t; + nis_object eobj; + entry_col ecol[3]; + char *addr, aname[NIS_MAXNAMELEN]; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * comment (col 2) + */ + t = strchr(buf, '#'); + if (t) { + *t++ = 0; + ecol[2].ec_value.ec_value_val = t; + ecol[2].ec_value.ec_value_len = strlen(t)+1; + } else { + ecol[2].ec_value.ec_value_val = 0; + ecol[2].ec_value.ec_value_len = 0; + } + + /* + * addr(col 0) + */ + if ((t = strtok(buf, " \t")) == 0) { + strcpy(parse_err_msg, "no mask"); + return (GENENT_PARSEERR); + } + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + addr = t; + + /* + * mask (col 1) + */ + if ((t = strtok(NULL, " \t")) == 0) { + strcpy(parse_err_msg, "no mask"); + return (GENENT_PARSEERR); + } + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 3; + + if (!cback) + cback = addentry; + + /* specify entry by addr alone (ignore mask) */ + sprintf(aname, "[addr=%s],%s", addr, ta_name); + + if ((*cback)(aname, &eobj, udata, 0)) + return (GENENT_CBERR); + + return (GENENT_OK); +} + +void +dump_netmasks(res) + nis_result *res; +{ + int i; + char buf[BUFSIZ+1], *c0, *c1, *c2; + + for (i = 0; i < res->objects.objects_len; i++) { + if ((c0 = OBJ_COL_VAL(i, 0)) && (c1 = OBJ_COL_VAL(i, 1))) { + strcpy(buf, c0); + strcat(buf, tabtocol(buf, 2)); + strcat(buf, c1); + if ((c2 = OBJ_COL_VAL(i, 2)) && + !blankline(c2)) { + strcat(buf, tabtocol(buf, 5)); + strcat(buf, "#"); + strcat(buf, c2); + } + printf("%s\n", buf); + } + } +} + + +/* + * /etc/networks + * nis+ table: (networks_tbl) cname, name, addr, comment + * + */ + +int +genent_networks(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1]; + char *t; + nis_object eobj; + entry_col ecol[4]; + char *cname; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * comment (col 3) + */ + t = strchr(buf, '#'); + if (t) { + *t++ = 0; + ecol[3].ec_value.ec_value_val = t; + ecol[3].ec_value.ec_value_len = strlen(t)+1; + } else { + ecol[3].ec_value.ec_value_val = 0; + ecol[3].ec_value.ec_value_len = 0; + } + + /* + * cname(col 0) + */ + if ((t = strtok(buf, " \t")) == 0) { + strcpy(parse_err_msg, "no number"); + return (GENENT_PARSEERR); + } + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + cname = t; + + /* + * number (col 2) + */ + if ((t = strtok(NULL, " \t")) == 0) { + strcpy(parse_err_msg, "no number"); + return (GENENT_PARSEERR); + } + ecol[2].ec_value.ec_value_val = t; + ecol[2].ec_value.ec_value_len = strlen(t)+1; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 4; + + if (!cback) + cback = addentry; + + /* + * name (col 1) + */ + t = cname; + do { + /* + * don't clobber comment in canonical entry + */ + if (t != cname && strcasecmp(t, cname) == 0) + continue; + + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + + if ((*cback)(ta_name, &eobj, udata, 0)) + return (GENENT_CBERR); + + /* + * only put comment in canonical entry + */ + ecol[3].ec_value.ec_value_val = 0; + ecol[3].ec_value.ec_value_len = 0; + + } while (t = strtok(NULL, " \t")); + + return (GENENT_OK); +} + +void +dump_networks(res) + nis_result *res; +{ + int i, j, a; + char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c0a, *c1a; + + for (i = 0; i < res->objects.objects_len; i++) { + if ((c0 = OBJ_COL_VAL(i, 0)) && *c0 && + (c1 = OBJ_COL_VAL(i, 1)) && (c2 = OBJ_COL_VAL(i, 2)) && + (strcasecmp(c0, c1) == 0)) { + strcpy(buf, c0); + strcat(buf, tabtocol(buf, 2)); + strcat(buf, c2); + for (a = j = 0; j < res->objects.objects_len; j++) + if (j != i && + (c0a = OBJ_COL_VAL(j, 0)) && *c0a && + (c1a = OBJ_COL_VAL(j, 1)) && + strcasecmp(c0, c0a) == 0) { + if (a++ > 0) + strcat(buf, " "); + else + strcat(buf, tabtocol(buf, 4)); + strcat(buf, c1a); + *c0a = 0; + } + if ((c3 = OBJ_COL_VAL(i, 3)) && !blankline(c3)) { + strcat(buf, tabtocol(buf, 6)); + strcat(buf, "#"); + strcat(buf, c3); + } + *c0 = 0; + printf("%s\n", buf); + } + } +} + + +/* + * /etc/protocols + * nis+ table: (protocols_tbl) cname, name, number, comment + * + */ + +int +genent_protocols(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1]; + char *t; + nis_object eobj; + entry_col ecol[4]; + char *cname; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * comment (col 3) + */ + t = strchr(buf, '#'); + if (t) { + *t++ = 0; + ecol[3].ec_value.ec_value_val = t; + ecol[3].ec_value.ec_value_len = strlen(t)+1; + } else { + ecol[3].ec_value.ec_value_val = 0; + ecol[3].ec_value.ec_value_len = 0; + } + + /* + * cname(col 0) + */ + if ((t = strtok(buf, " \t")) == 0) { + strcpy(parse_err_msg, "no number"); + return (GENENT_PARSEERR); + } + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + cname = t; + + /* + * number (col 2) + */ + if ((t = strtok(NULL, " \t")) == 0) { + strcpy(parse_err_msg, "no number"); + return (GENENT_PARSEERR); + } + ecol[2].ec_value.ec_value_val = t; + ecol[2].ec_value.ec_value_len = strlen(t)+1; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 4; + + if (!cback) + cback = addentry; + + /* + * name (col 1) + */ + t = cname; + do { + /* + * don't clobber comment in canonical entry + */ + if (t != cname && strcasecmp(t, cname) == 0) + continue; + + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + + if ((*cback)(ta_name, &eobj, udata, 0)) + return (GENENT_CBERR); + + /* + * only put comment in canonical entry + */ + ecol[3].ec_value.ec_value_val = 0; + ecol[3].ec_value.ec_value_len = 0; + + } while (t = strtok(NULL, " \t")); + + return (GENENT_OK); +} + +void +dump_protocols(res) + nis_result *res; +{ + int i, j, a; + char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c0a, *c1a; + + for (i = 0; i < res->objects.objects_len; i++) { + if ((c0 = OBJ_COL_VAL(i, 0)) && *c0 && + (c1 = OBJ_COL_VAL(i, 1)) && (c2 = OBJ_COL_VAL(i, 2)) && + (strcasecmp(c0, c1) == 0)) { + strcpy(buf, c0); + strcat(buf, tabtocol(buf, 2)); + strcat(buf, c2); + for (a = j = 0; j < res->objects.objects_len; j++) + if (j != i && + (c0a = OBJ_COL_VAL(j, 0)) && *c0a && + (c1a = OBJ_COL_VAL(j, 1)) && + strcasecmp(c0, c0a) == 0) { + if (a++ > 0) + strcat(buf, " "); + else + strcat(buf, tabtocol(buf, 4)); + strcat(buf, c1a); + *c0a = 0; + } + if ((c3 = OBJ_COL_VAL(i, 3)) && !blankline(c3)) { + strcat(buf, tabtocol(buf, 6)); + strcat(buf, "#"); + strcat(buf, c3); + } + *c0 = 0; + printf("%s\n", buf); + } + } +} + + +/* + * /etc/rpc + * nis+ table: (rpc_tbl) cname, name, number, comment + * + */ + +int +genent_rpc(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1]; + char *t; + nis_object eobj; + entry_col ecol[4]; + char *cname; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * comment (col 3) + */ + t = strchr(buf, '#'); + if (t) { + *t++ = 0; + ecol[3].ec_value.ec_value_val = t; + ecol[3].ec_value.ec_value_len = strlen(t)+1; + } else { + ecol[3].ec_value.ec_value_val = 0; + ecol[3].ec_value.ec_value_len = 0; + } + + /* + * cname(col 0) + */ + if ((t = strtok(buf, " \t")) == 0) { + strcpy(parse_err_msg, "no number"); + return (GENENT_PARSEERR); + } + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + cname = t; + + /* + * number (col 2) + */ + if ((t = strtok(NULL, " \t")) == 0) { + strcpy(parse_err_msg, "no number"); + return (GENENT_PARSEERR); + } + ecol[2].ec_value.ec_value_val = t; + ecol[2].ec_value.ec_value_len = strlen(t)+1; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 4; + + if (!cback) + cback = addentry; + + /* + * name (col 1) + */ + t = cname; + do { + /* + * don't clobber comment in canonical entry + */ + if (t != cname && strcasecmp(t, cname) == 0) + continue; + + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + + if ((*cback)(ta_name, &eobj, udata, 0)) + return (GENENT_CBERR); + + /* + * only put comment in canonical entry + */ + ecol[3].ec_value.ec_value_val = 0; + ecol[3].ec_value.ec_value_len = 0; + + } while (t = strtok(NULL, " \t")); + + return (GENENT_OK); +} + +void +dump_rpc(res) + nis_result *res; +{ + int i, j, a; + char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c0a, *c1a; + + for (i = 0; i < res->objects.objects_len; i++) { + if ((c0 = OBJ_COL_VAL(i, 0)) && *c0 && + (c1 = OBJ_COL_VAL(i, 1)) && (c2 = OBJ_COL_VAL(i, 2)) && + (strcasecmp(c0, c1) == 0)) { + strcpy(buf, c0); + strcat(buf, tabtocol(buf, 2)); + strcat(buf, c2); + for (a = j = 0; j < res->objects.objects_len; j++) + if (j != i && + (c0a = OBJ_COL_VAL(j, 0)) && *c0a && + (c1a = OBJ_COL_VAL(j, 1)) && + strcasecmp(c0, c0a) == 0) { + if (a++ > 0) + strcat(buf, " "); + else + strcat(buf, tabtocol(buf, 4)); + strcat(buf, c1a); + *c0a = 0; + } + if ((c3 = OBJ_COL_VAL(i, 3)) && + !blankline(c3)) { + strcat(buf, tabtocol(buf, 6)); + strcat(buf, "#"); + strcat(buf, c3); + } + *c0 = 0; + printf("%s\n", buf); + } + } +} + + +/* + * /etc/services + * nis+ table: (services_tbl) cname, name, proto, port, comment + * + */ + +int +genent_services(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1]; + char *t, *p; + nis_object eobj; + entry_col ecol[5]; + char *cname; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * comment (col 4) + */ + t = strchr(buf, '#'); + if (t) { + *t++ = 0; + ecol[4].ec_value.ec_value_val = t; + ecol[4].ec_value.ec_value_len = strlen(t)+1; + } else { + ecol[4].ec_value.ec_value_val = 0; + ecol[4].ec_value.ec_value_len = 0; + } + + /* + * cname(col 0) + */ + if ((t = strtok(buf, " \t")) == 0) { + strcpy(parse_err_msg, "no port"); + return (GENENT_PARSEERR); + } + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + cname = t; + + /* + * port (col 3) + */ + if ((t = strtok(NULL, " \t")) == 0) { + strcpy(parse_err_msg, "no protocol"); + return (GENENT_PARSEERR); + } + if ((p = strchr(t, '/')) == 0) { + strcpy(parse_err_msg, "bad port/proto"); + return (GENENT_PARSEERR); + } + *(p++) = 0; + ecol[3].ec_value.ec_value_val = t; + ecol[3].ec_value.ec_value_len = strlen(t)+1; + + /* + * proto (col 2) + */ + ecol[2].ec_value.ec_value_val = p; + ecol[2].ec_value.ec_value_len = strlen(p)+1; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 5; + + if (!cback) + cback = addentry; + + /* + * name (col 1) + */ + t = cname; + do { + /* + * don't clobber comment in canonical entry + */ + if (t != cname && strcasecmp(t, cname) == 0) + continue; + + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + + if ((*cback)(ta_name, &eobj, udata, 0)) + return (GENENT_CBERR); + + /* + * only put comment in canonical entry + */ + ecol[4].ec_value.ec_value_val = 0; + ecol[4].ec_value.ec_value_len = 0; + + } while (t = strtok(NULL, " \t")); + + return (GENENT_OK); +} + +/* + * List all of the entries that match the cname, proto, and port + * of the passed entry. + */ +nis_result * +dump_match_services(table, entry) + nis_name table; + nis_object *entry; +{ + nis_result *res = 0; + char *c0, *c1, *c2, *c3; + char srch[NIS_MAXNAMELEN]; + + if ((c0 = KEYVAL(0)) && *c0 && + (c1 = KEYVAL(1)) && (c2 = KEYVAL(2)) && (c3 = KEYVAL(3)) && + (strcasecmp(c0, c1) == 0)) { + sprintf(srch, "[cname=%s,proto=%s,port=%s],%s", + c0, c2, c3, table); + res = nis_list(srch, allres|master, 0, 0); + } + + return (res); +} + +void +dump_services(res) + nis_result *res; +{ + int i, j, a; + char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c4, *c0a, *c1a, *c2a, *c3a; + + for (i = 0; i < res->objects.objects_len; i++) { + if ((c0 = OBJ_COL_VAL(i, 0)) && *c0 && + (c1 = OBJ_COL_VAL(i, 1)) && (c2 = OBJ_COL_VAL(i, 2)) && + (c3 = OBJ_COL_VAL(i, 3)) && (strcasecmp(c0, c1) == 0)) { + strcpy(buf, c0); + strcat(buf, tabtocol(buf, 2)); + strcat(buf, c3); + strcat(buf, "/"); + strcat(buf, c2); + for (a = j = 0; j < res->objects.objects_len; j++) + if (j != i && + (c0a = OBJ_COL_VAL(j, 0)) && *c0a && + (c1a = OBJ_COL_VAL(j, 1)) && + (c2a = OBJ_COL_VAL(j, 2)) && + (c3a = OBJ_COL_VAL(j, 3)) && + strcasecmp(c0, c0a) == 0 && + strcasecmp(c2, c2a) == 0 && + strcasecmp(c3, c3a) == 0) { + if (a++ > 0) + strcat(buf, " "); + else + strcat(buf, tabtocol(buf, 4)); + strcat(buf, c1a); + *c0a = 0; + } + if ((c4 = OBJ_COL_VAL(i, 4)) && !blankline(c4)) { + strcat(buf, tabtocol(buf, 6)); + strcat(buf, "#"); + strcat(buf, c4); + } + *c0 = 0; + printf("%s\n", buf); + } + } +} + +char * +dbmniskey_services(key) + datum key; +{ + static char buf[NIS_MAXNAMELEN]; + char *p; + + if ((p = strchr(key.dptr, '/')) == 0) { + sprintf(buf, "%s=%.*s", OBJ_COL_NAME(2), + key.dsize, key.dptr); + } else { + sprintf(buf, "%s=%.*s,%s=%.*s", OBJ_COL_NAME(2), + key.dsize-(p-key.dptr)-1, p+1, + OBJ_COL_NAME(3), p-key.dptr, key.dptr); + } + + return (buf); +} + +datum +nisdbmkey_services(entry) + nis_object *entry; +{ + static char buf[BUFSIZ+1]; + datum key; + + sprintf(buf, "%s/%s", NKEYVAL(3), NKEYVAL(2)); + key.dptr = buf; + key.dsize = strlen(buf); + + return (key); +} + +void +printfkeystr_services(fstr, entry) + char *fstr; + nis_object *entry; +{ + char keystr[BUFSIZ]; + + if (strcasecmp(NKEYVAL(0), NKEYVAL(1)) == 0) + sprintf(keystr, "%s %s/%s", NKEYVAL(0), NKEYVAL(3), + NKEYVAL(2)); + else + sprintf(keystr, "%s %s/%s (%s)", NKEYVAL(0), NKEYVAL(3), + NKEYVAL(2), NKEYVAL(1)); + + printf(fstr, keystr); +} + + +/* + * /etc/publickey + * /etc/netid + * nis+ table: (cred_tbl) cname, auth_type, auth_name, public_data, + * private_data + * + */ + +int +genent_publickey(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1], tmpbuf[BUFSIZ+1], cname[NIS_MAXNAMELEN]; + char *t, *p; + nis_object eobj; + entry_col ecol[5]; + int uid; + struct passwd *pwd; + char aname[NIS_MAXNAMELEN], auth_type[MECH_MAXATNAME+1]; + keylen_t keylen; + algtype_t algtype; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * auth_name (col 2) + */ + if ((t = strtok(buf, " \t")) == 0) { + strcpy(parse_err_msg, "no cname"); + return (GENENT_PARSEERR); + } + ecol[2].ec_value.ec_value_val = t; + ecol[2].ec_value.ec_value_len = strlen(t)+1; + + /* + * Special case: /etc/publickey usually has an entry + * for principal "nobody". We skip it because it does + * not apply to NIS+. + */ + if (strcmp(t, "nobody") == 0) + return (GENENT_OK); + + /* + * cname (col 0) + */ + if (strncmp(t, "unix.", 5)) { + strcpy(parse_err_msg, "bad cname"); + return (GENENT_PARSEERR); + } + strcpy(tmpbuf, &(t[5])); + if ((p = strchr(tmpbuf, '@')) == 0) { + strcpy(parse_err_msg, "bad cname"); + return (GENENT_PARSEERR); + } + *(p++) = 0; + if (isdigit(*tmpbuf)) { + nis_error st; + extern struct passwd *getpwuid_nisplus_master(); + + uid = atoi(tmpbuf); + /* + * don't generate entries for uids without passwd entries + * first try it in the regular way, and if that fails, then + * a lookup to the NIS+ Master server to be sure. + */ + if (((pwd = getpwuid(uid)) == 0) && + ((pwd = getpwuid_nisplus_master(nisdomain, + uid, &st)) == 0)) { + fprintf(stderr, + "can't map uid %d to username, skipping\n", + uid); + return (GENENT_OK); + } + strcpy(cname, pwd->pw_name); + } else + strcpy(cname, tmpbuf); + if (*p != '.') + strcat(cname, "."); + strcat(cname, p); + if (cname[strlen(cname)-1] != '.') + strcat(cname, "."); + /* + * XXX complain if domain in netname not same as nisdomain? + */ + ecol[0].ec_value.ec_value_val = cname; + ecol[0].ec_value.ec_value_len = strlen(cname)+1; + + /* + * public_data (col 3) + */ + if ((t = strtok(NULL, " \t")) == 0) { + strcpy(parse_err_msg, "no private_data"); + return (GENENT_PARSEERR); + } + if ((p = strchr(t, ':')) == 0) { + strcpy(parse_err_msg, "bad public_data"); + return (GENENT_PARSEERR); + } + *(p++) = 0; + ecol[3].ec_value.ec_value_val = t; + ecol[3].ec_value.ec_value_len = strlen(t)+1; + keylen = (strlen(t) / 2) * 8; + + /* + * private_data (col 4) and algtype extraction + */ + if (*p == ':') + p++; + t = p; + if (!(t = strchr(t, ':'))) { + if (!oldpubkeymode) + printf( + "WARNING: No algtype data found in publickey file, assuming 0\n"); + algtype = 0; + } else { + *t = '\0'; + t++; + algtype = atoi(t); + } + ecol[4].ec_value.ec_value_val = p; + ecol[4].ec_value.ec_value_len = strlen(p)+1; + ecol[4].ec_flags = EN_CRYPT; + + /* + * auth_type (col 1) + */ + if (!(__nis_keyalg2authtype(keylen, algtype, auth_type, + MECH_MAXATNAME))) + return (GENENT_PARSEERR); + + ecol[1].ec_value.ec_value_val = auth_type; + ecol[1].ec_value.ec_value_len = strlen(auth_type)+1; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_owner = cname; + eobj.zo_access = NIS_OWNER_MASK(NIS_READ_ACC) | + NIS_GROUP_MASK(NIS_ALL_ACC); + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 5; + + if (!cback) + cback = addentry; + + /* specify entry by cname/auth_type alone (ignore auth_name) */ + sprintf(aname, "[cname=%s,auth_type=%s],%s", cname, auth_type, + ta_name); + + if ((*cback)(aname, &eobj, udata, 0)) + return (GENENT_CBERR); + + return (GENENT_OK); +} + +void +dump_publickey(res) + nis_result *res; +{ + int i, mcount = 0, authlistcount = 0, filter = 0; + char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c4; + char **authlist = NULL; + + /* + * If RPCSEC_GSS is configured on this system, a list of + * auth_types needs to be generated to filter entries (since + * dumptable has ignored tt->clrsrch). Otherwise, the + * nis_list has prefiltered out the extra entries in + * dumptable. + */ + if (mechs) { + while (mechs[mcount]) { + char *authtype; + + authtype = (char *)malloc(MECH_MAXATNAME+1); + if (__nis_mechalias2authtype(mechs[mcount]->alias, + authtype, + MECH_MAXATNAME)) { + authlist = (char **)realloc(authlist, + sizeof (char *) * (authlistcount + 2)); + authlist[authlistcount] = authtype; + authlistcount++; + } else + free(authtype); + mcount++; + } + if (authlistcount) { + authlist[authlistcount] = NULL; + filter++; + } + } + + for (i = 0; i < res->objects.objects_len; i++) { + if ((c0 = OBJ_COL_VAL(i, 0)) && (c1 = OBJ_COL_VAL(i, 1)) && + (c2 = OBJ_COL_VAL(i, 2)) && (c3 = OBJ_COL_VAL(i, 3)) && + (c4 = OBJ_COL_VAL(i, 4))) { + /* + * If we have a filter list, then we walk + * through it until we find a matching + * auth_type. Otherwise we go on to the next + * result entry. + */ + if (filter) { + int gotit = 0; + + authlistcount = 0; + while (authlist[authlistcount]) { + if (strcmp(authlist[authlistcount], + c1) == 0) { + gotit++; + break; + } + authlistcount++; + } + if (!gotit) + continue; + } + + strcpy(buf, c2); + strcat(buf, tabtocol(buf, 2)); + strcat(buf, c3); + if (strchr(c3, ':') == 0) + strcat(buf, ":"); + strcat(buf, c4); + if (!oldpubkeymode) { + if (strcmp(c1, AUTH_DES_AUTH_TYPE) == 0) + strcat(buf, ":0"); + else { + char algbuf[MECH_MAXATNAME], *a; + + strcpy(algbuf, c1); + if (a = strchr(algbuf, '-')) { + *a = ':'; + strcat(buf, a); + } else + strcat(buf, ":0"); + } + } + printf("%s\n", buf); + } + } +} + +int +genent_netid(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1], tmpbuf[BUFSIZ+1], cname[NIS_MAXNAMELEN]; + char *t, *p; + nis_object eobj; + entry_col ecol[5]; + int uid; + struct passwd *pwd; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * cname (col 0) + */ + if ((t = strtok(buf, " \t")) == 0) { + strcpy(parse_err_msg, "no auth_name"); + return (GENENT_PARSEERR); + } + if (strncmp(t, "unix.", 5)) { + strcpy(parse_err_msg, "bad cname"); + return (GENENT_PARSEERR); + } + strcpy(tmpbuf, &(t[5])); + if ((t[5] == '@') || ((p = strchr(tmpbuf, '@')) == 0)) { + strcpy(parse_err_msg, "bad cname"); + return (GENENT_PARSEERR); + } + *(p++) = 0; + if (isdigit(*tmpbuf)) { + nis_error st; + extern struct passwd *getpwuid_nisplus_master(); + + uid = atoi(tmpbuf); + /* + * don't generate entries for uids without passwd entries + * first try it in the regular way, and if that fails, then + * a lookup to the NIS+ Master server to be sure. + */ + if (((pwd = getpwuid(uid)) == 0) && + ((pwd = getpwuid_nisplus_master(nisdomain, + uid, &st)) == 0)) { + fprintf(stderr, + "can't map uid %d to username, skipping\n", + uid); + return (GENENT_OK); + } + strcpy(cname, pwd->pw_name); + } else + uid = 0; + /* + * don't generate LOCAL entries for root + */ + if (uid == 0) + return (GENENT_OK); + if (*p != '.') + strcat(cname, "."); + strcat(cname, p); + if (cname[strlen(cname)-1] != '.') + strcat(cname, "."); + /* + * XXX complain if domain in netname not same as nisdomain? + */ + ecol[0].ec_value.ec_value_val = cname; + ecol[0].ec_value.ec_value_len = strlen(cname)+1; + + /* + * auth_name (col 2) + */ + if ((t = strtok(NULL, " \t")) == 0) { + strcpy(parse_err_msg, "no public_data"); + return (GENENT_PARSEERR); + } + if ((p = strchr(t, ':')) == 0) { + strcpy(parse_err_msg, "bad auth_name"); + return (GENENT_PARSEERR); + } + *(p++) = 0; + ecol[2].ec_value.ec_value_val = t; + ecol[2].ec_value.ec_value_len = strlen(t)+1; + + /* + * public_data (col 3) + */ + ecol[3].ec_value.ec_value_val = p; + ecol[3].ec_value.ec_value_len = strlen(p)+1; + + /* + * private_data (col 4) + */ + ecol[4].ec_value.ec_value_val = 0; + ecol[4].ec_value.ec_value_len = 0; + + /* + * auth_type (col 1) + */ + ecol[1].ec_value.ec_value_val = "LOCAL"; + ecol[1].ec_value.ec_value_len = 6; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 5; + + if (!cback) + cback = addentry; + + if ((*cback)(ta_name, &eobj, udata, 0)) + return (GENENT_CBERR); + + return (GENENT_OK); +} + +void +dump_netid(res) + nis_result *res; +{ + int i, j; + char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c4; + + for (i = 0; i < res->objects.objects_len; i++) { + if ((c0 = OBJ_COL_VAL(i, 0)) && (c1 = OBJ_COL_VAL(i, 1)) && + (c2 = OBJ_COL_VAL(i, 2)) && (c3 = OBJ_COL_VAL(i, 3))) { + nis_name objdom = nis_domain_of(c0); + size_t objdom_len = strlen(objdom); + + if (objdom_len > 0) { + if (objdom[objdom_len - 1] == '.') + objdom[objdom_len - 1] = '\0'; + } + + sprintf(buf, "unix.%s@%s", c2, objdom); + strcat(buf, tabtocol(buf, 2)); + strcat(buf, c2); + strcat(buf, ":"); + strcat(buf, c3); + printf("%s\n", buf); + } + } +} + +char * +dbmniskey_netid(key) + datum key; +{ + static char buf[NIS_MAXNAMELEN]; + int i; + + strcpy(buf, "auth_name=xxx"); + + if (key.dsize < 7 || strncmp(key.dptr, "unix.", 5)) + return (buf); + for (i = 5; i < key.dsize; i++) + if (key.dptr[i] == '@') + break; + if (i == 5 || i == key.dsize) + return (buf); + strncpy(&(buf[10]), &(key.dptr[5]), i-5); + buf[5+i] = 0; + + return (buf); +} + +datum +nisdbmkey_netid(entry) + nis_object *entry; +{ + static char buf[BUFSIZ+1]; + datum key; + + sprintf(buf, "unix.%s@%s", NKEYVAL(2), nis_domain_of(NKEYVAL(0))); + buf[strlen(buf)-1] = 0; + + key.dptr = buf; + key.dsize = strlen(buf); + + return (key); +} + + +/* + * /etc/aliases + * nis+ table: (mail_aliases) alias, expansion, comments, options + * + * The handling of dbm keys and values for aliases is a little funny + * because the length field in a datum includes the terminating + * null (e.g., "name" would have length 5). The other types of dbm + * files do not include the terminating null. + */ + +int +genent_aliases(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1]; + char *t; + nis_object eobj; + entry_col ecol[4]; + char *alias, aname[NIS_MAXNAMELEN], key[NIS_MAXNAMELEN]; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * alias(col 0) + */ + if ((t = strtok_quotes(buf, " \t")) == 0) { + strcpy(parse_err_msg, "no expansion"); + return (GENENT_PARSEERR); + } + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + alias = t; + + /* + * expansion (col 1) + */ + if ((t = strtok_quotes(NULL, "")) == 0) { + strcpy(parse_err_msg, "no expansion"); + return (GENENT_PARSEERR); + } + /* + * skip white space + */ + t += strspn(t, " \t"); + if (*t == 0) { + strcpy(parse_err_msg, "empty expansion"); + return (GENENT_PARSEERR); + } + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + + /* + * comments (col 2) + */ + ecol[2].ec_value.ec_value_val = 0; + ecol[2].ec_value.ec_value_len = 0; + + /* + * options (col 3) + */ + ecol[3].ec_value.ec_value_val = 0; + ecol[3].ec_value.ec_value_len = 0; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 4; + + if (!cback) + cback = addentry; + + (void) __nis_quote_key(alias, key, sizeof (key)); + + /* specify entry by alias alone (ignore expansion) */ + sprintf(aname, "[alias=%s],%s", key, ta_name); + + if ((*cback)(aname, &eobj, udata, 0)) + return (GENENT_CBERR); + + return (GENENT_OK); +} + +void +dump_aliases(res) + nis_result *res; +{ + int i; + char *c0, *c1, *c2; + + for (i = 0; i < res->objects.objects_len; i++) { + if ((c0 = OBJ_COL_VAL(i, 0)) && (c1 = OBJ_COL_VAL(i, 1))) { + if ((c2 = OBJ_COL_VAL(i, 2)) && !blankline(c2)) + printf("# %s\n", c2); + printf("%s: %s\n", c0, c1); + } + } +} + +datum +nisdbmkey_aliases(entry) + nis_object *entry; +{ + static char buf[BUFSIZ+1]; + datum key; + + strcpy(buf, NKEYVAL(tt->niskeycol)); + + key.dptr = buf; + key.dsize = strlen(buf)+1; + + return (key); +} + +datum +filedbmkey_aliases(line) + char *line; +{ + static char buf[BUFSIZ+1]; + int i; + char *p; + datum key; + + for (i = tt->filekeycol; i >= 0; i--) { + p = strpbrk_quotes(line, tt->filesep); + if (i > 0) { + if (p == 0) { + key.dptr = 0; + key.dsize = 0; + return (key); + } + line = p + strspn(p, tt->filesep); + } + } + + if (p) + sprintf(buf, "%.*s", p-line, line); + else + strcpy(buf, line); + + key.dptr = buf; + key.dsize = strlen(buf)+1; + + return (key); +} + +int +filedbmline_aliases(line, etcf, lineno, loc) + struct line_buf *line; + FILE *etcf; + int *lineno; + struct file_loc *loc; +{ + char *p; + int c; + int len = 0; + + loc->offset = ftell(etcf); + while (1) { + if (fget_line_at(line, len, etcf) == 0) + return (0); + + if (lineno) + (*lineno)++; + + len = strlen(line->str); + if (line->str[len-1] == '\n') { + line->str[len-1] = 0; + len -= 1; + } + + /* + * A continuation line starts with a space or a tab. + * Continuation lines count even for commented lines. + */ + if ((c = fgetc(etcf)) != EOF && + ungetc(c, etcf) != EOF && + (c == ' ' || c == '\t')) { + continue; + } + + if (!blankline(line->str) && line->str[0] != '#') + break; + + len = 0; + loc->offset = ftell(etcf); + } + + if (p = strchr_quotes(line->str, ':')) + *p = ' '; + + loc->size = len; + return (1); +} + +/* + * Convert an alias line into key and content. Just like filetodbm_keyvalue + * but the key and data includes the terminating null. + */ +void +filetodbm_aliases(line, key, content) + char *line; + datum *key; + datum *content; +{ + int i; + char *p; + + for (i = tt->filekeycol; i >= 0; i--) { + p = strpbrk_quotes(line, tt->filesep); + if (i > 0) { + if (p == 0) { + key->dptr = 0; + key->dsize = 0; + return; + } + line = p + strspn(p, tt->filesep); + } + } + + if (p) { + *p++ = '\0'; /* null-terminate key */ + key->dptr = line; + key->dsize = p-line; /* include terminating null */ + line = p + strspn(p, tt->filesep); + content->dptr = line; + content->dsize = strlen(line) + 1; + } else { + key->dptr = line; + key->dsize = strlen(line) + 1; + content->dptr = ""; + content->dsize = 0; + } +} + +/* + * key-value + * nis+ table: key, value + * + */ + +int +genent_keyvalue(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1]; + char *t; + nis_object eobj; + entry_col ecol[2]; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * key(col 0) + */ + if ((t = strtok(buf, " \t")) == 0) { + strcpy(parse_err_msg, "no value"); + return (GENENT_PARSEERR); + } + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + + /* + * value (col 1) + */ + if ((t = strtok(NULL, "")) == 0) + t = ""; + else + t += strspn(t, " \t"); /* skip white space */ + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 2; + + if (!cback) + cback = addentry; + + if ((*cback)(ta_name, &eobj, udata, 0)) + return (GENENT_CBERR); + + return (GENENT_OK); +} + +void +dump_keyvalue(res) + nis_result *res; +{ + int i, j; + char buf[BUFSIZ+1], *c0, *c1; + + for (i = 0; i < res->objects.objects_len; i++) { + if (c0 = OBJ_COL_VAL(i, 0)) { + strcpy(buf, c0); + if (c1 = OBJ_COL_VAL(i, 1)) { + strcat(buf, tabtocol(buf, 2)); + strcat(buf, c1); + } + printf("%s\n", buf); + } + } +} + +/* + * /etc/netgroup + * nis+ table: (netgroup_tbl) name, group, host, user, domain, comment + * + */ + +int +genent_netgroup(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BIGBUF+1]; /* netgroup entries tend to be big */ + char *t, *p; + nis_object eobj; + entry_col ecol[6]; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * comment (col 5) + */ + t = strchr(buf, '#'); + if (t) { + *t++ = 0; + ecol[5].ec_value.ec_value_val = t; + ecol[5].ec_value.ec_value_len = strlen(t)+1; + } else { + ecol[5].ec_value.ec_value_val = 0; + ecol[5].ec_value.ec_value_len = 0; + } + + /* + * name(col 0) + */ + if ((t = strtok(buf, " \t")) == 0) { + return (GENENT_OK); /* empty line */ + } + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + + /* + * membership list + */ + if ((p = strtok(NULL, "")) == 0) + p = ""; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 6; + + if (!cback) + cback = addentry; + + /* + * group (col 1) + * host (col 2) + * user (col 3) + * domain (col 4) + */ + while (*p) { + while (*p == ' ' || *p == '\t') + p++; + + if (*p == '\0') + break; + + if (*p == '(') { + /* + * group (col 1) + */ + ecol[1].ec_value.ec_value_val = 0; + ecol[1].ec_value.ec_value_len = 0; + + /* + * host (col 2) + */ + p++; + while (*p == ' ' || *p == '\t') + p++; + t = p; + while (*p != ' ' && *p != '\t' && *p != ',') + if (*p == '\0' || *p == ')') { + strcpy(parse_err_msg, "bad host"); + return (GENENT_PARSEERR); + } + else + p++; + *p++ = 0; + ecol[2].ec_value.ec_value_val = t; + ecol[2].ec_value.ec_value_len = strlen(t)+1; + + + /* + * user (col 3) + */ + while (*p == ' ' || *p == '\t') + p++; + t = p; + while (*p != ' ' && *p != '\t' && *p != ',') + if (*p == '\0' || *p == ')') { + strcpy(parse_err_msg, "bad user"); + return (GENENT_PARSEERR); + } + else + p++; + *p++ = 0; + ecol[3].ec_value.ec_value_val = t; + ecol[3].ec_value.ec_value_len = strlen(t)+1; + + /* + * domain (col 4) + */ + while (*p == ' ' || *p == '\t') + p++; + t = p; + while (*p != ' ' && *p != '\t' && *p != ')') + if (*p == '\0' || *p == ',') { + strcpy(parse_err_msg, "bad domain"); + return (GENENT_PARSEERR); + } + else + p++; + *p++ = 0; + ecol[4].ec_value.ec_value_val = t; + ecol[4].ec_value.ec_value_len = strlen(t)+1; + + } else { + /* + * host (col 2) + */ + ecol[2].ec_value.ec_value_val = 0; + ecol[2].ec_value.ec_value_len = 0; + + /* + * user (col 3) + */ + ecol[3].ec_value.ec_value_val = 0; + ecol[3].ec_value.ec_value_len = 0; + + /* + * domain (col 4) + */ + ecol[4].ec_value.ec_value_val = 0; + ecol[4].ec_value.ec_value_len = 0; + + /* + * group (col 1) + */ + t = p; + while (*p != ' ' && *p != '\t' && *p != '\0') + p++; + if (*p) + *p++ = 0; + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + } + + /* + * XXX should avoid adding duplicates of first entry in + * order to keep from clobbering comment + */ + + if ((*cback)(ta_name, &eobj, udata, 0)) + return (GENENT_CBERR); + + /* + * only put comment in first entry + */ + ecol[5].ec_value.ec_value_val = 0; + ecol[5].ec_value.ec_value_len = 0; + + }; + + return (GENENT_OK); +} + + +/* + * There is no notion of a canonical entry for netgroup, so + * we can't tell be looking at an entry if it has already + * been handled. So, we keep a table of all of the netgroups + * we have done so far. + */ + +#define HASH_SIZE 64 + +struct hashent { + struct hashent *next; + char *name; +}; + +struct hashent *netgroup_table[64]; + +int +hash(s) + char *s; +{ + int retval = 0; + + while (*s) + retval += *s++; + return (retval % HASH_SIZE); +} + +int +did_netgroup(s) + char *s; +{ + int n; + struct hashent *hp; + + n = hash(s); + for (hp = netgroup_table[n]; hp; hp = hp->next) { + if (strcmp(s, hp->name) == 0) { + return (1); + } + } + + return (0); +} + +void +mark_netgroup(s) + char *s; +{ + int n; + struct hashent *hp; + + hp = (struct hashent *)malloc(sizeof (*hp)); + if (hp == 0) { + fprintf(stderr, "mark_netgroup: out of memory\n"); + exit(1); + } + + hp->name = strdup(s); + if (hp->name == 0) { + fprintf(stderr, "mark_netgroup: out of memory\n"); + exit(1); + } + + n = hash(s); + hp->next = netgroup_table[n]; + netgroup_table[n] = hp; +} + +/* + * List all of the entries that match the name of the passed entry. + */ +nis_result * +dump_match_netgroup(table, entry) + nis_name table; + nis_object *entry; +{ + nis_result *res = 0; + char *c0, *c1, *c2, *c3, *c4; + char srch[NIS_MAXNAMELEN]; + + c0 = KEYVAL(0); + c1 = KEYVAL(1); + c2 = KEYVAL(2); + c3 = KEYVAL(3); + c4 = KEYVAL(4); + if (c0 && *c0 && (c1 || (c3 && c4)) && ! did_netgroup(c0)) { + sprintf(srch, "[name=%s],%s", c0, table); + res = nis_list(srch, allres|master, 0, 0); + mark_netgroup(c0); + } + + return (res); +} + +void +dump_netgroup(res) + nis_result *res; +{ + int i, j; + char *c0, *c1, *c2, *c3, *c4, *c5; + char *c0a, *c1a, *c2a, *c3a, *c4a; + static struct line_buf line; + + for (i = 0; i < res->objects.objects_len; i++) { + if ((c0 = OBJ_COL_VAL(i, 0)) && *c0 && + ((c1 = OBJ_COL_VAL(i, 1)) || + ((c2 = OBJ_COL_VAL_NO_NULL(i, 2)) && + (c3 = OBJ_COL_VAL(i, 3)) && (c4 = OBJ_COL_VAL(i, 4))))) { + print2buf_init(&line); + print2buf(&line, c0); + print2buf(&line, tabtocol(line.str, 2)); + if (c1) + print2buf(&line, c1); + else { + print2buf(&line, "("); + print2buf(&line, c2); + print2buf(&line, ","); + print2buf(&line, c3); + print2buf(&line, ","); + print2buf(&line, c4); + print2buf(&line, ")"); + } + c5 = OBJ_COL_VAL(i, 5); + for (j = i+1; j < res->objects.objects_len; j++) { + if ((c0a = OBJ_COL_VAL(j, 0)) && *c0a && + ((c1a = OBJ_COL_VAL(j, 1)) || + ((c2a = OBJ_COL_VAL_NO_NULL(j, 2)) && + (c3a = OBJ_COL_VAL(j, 3)) && + (c4a = OBJ_COL_VAL(j, 4)))) && + strcmp(c0, c0a) == 0) { + print2buf(&line, " "); + if (c1a) + print2buf(&line, c1a); + else { + print2buf(&line, "("); + print2buf(&line, c2a); + print2buf(&line, ","); + print2buf(&line, c3a); + print2buf(&line, ","); + print2buf(&line, c4a); + print2buf(&line, ")"); + } + if (!c5) + c5 = OBJ_COL_VAL(j, 5); + *c0a = 0; + } + } + if (c5 && !blankline(c5)) { + print2buf(&line, tabtocol(line.str, 6)); + print2buf(&line, "#"); + print2buf(&line, c5); + } + *c0 = 0; + printf("%s\n", line.str); + print2buf_destroy(&line); + } + } +} + +void +printfkeystr_netgroup(fstr, entry) + char *fstr; + nis_object *entry; +{ + char keystr[BUFSIZ]; + + if (NKEYVAL(1)) + sprintf(keystr, "%s %s", NKEYVAL(0), NKEYVAL(1)); + else + sprintf(keystr, "%s (%s,%s,%s)", NKEYVAL(0), NKEYVAL(2), + NKEYVAL(3), NKEYVAL(4)); + + printf(fstr, keystr); +} + +/* + * /etc/timezone + * nis+ table: (timezone_tbl) name, tzone, comment + * + */ + +int +genent_timezone(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char buf[BUFSIZ+1]; + char *t; + nis_object eobj; + entry_col ecol[3]; + char *cname; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * clear column data + */ + memset((char *)ecol, 0, sizeof (ecol)); + + /* + * comment (col 2) + */ + t = strchr(buf, '#'); + if (t) { + *t++ = 0; + ecol[2].ec_value.ec_value_val = t; + ecol[2].ec_value.ec_value_len = strlen(t)+1; + } else { + ecol[2].ec_value.ec_value_val = 0; + ecol[2].ec_value.ec_value_len = 0; + } + + /* + * tzone (col 1) + */ + if ((t = strtok(buf, " \t")) == 0) { + strcpy(parse_err_msg, "no name"); + return (GENENT_PARSEERR); + } + ecol[1].ec_value.ec_value_val = t; + ecol[1].ec_value.ec_value_len = strlen(t)+1; + + /* + * name(col 0) + */ + if ((t = strtok(NULL, " \t")) == 0) { + strcpy(parse_err_msg, "no name"); + return (GENENT_PARSEERR); + } + ecol[0].ec_value.ec_value_val = t; + ecol[0].ec_value.ec_value_len = strlen(t)+1; + + /* + * build entry + */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = 3; + + if (!cback) + cback = addentry; + + if ((*cback)(ta_name, &eobj, udata, 0)) + return (GENENT_CBERR); + + return (GENENT_OK); +} + +void +dump_timezone(res) + nis_result *res; +{ + int i, j; + char buf[BUFSIZ+1], *c0, *c1, *c2; + + for (i = 0; i < res->objects.objects_len; i++) { + if ((c0 = OBJ_COL_VAL(i, 0)) && (c1 = OBJ_COL_VAL(i, 1))) { + strcpy(buf, c1); + strcat(buf, tabtocol(buf, 2)); + strcat(buf, c0); + if ((c2 = OBJ_COL_VAL(i, 2)) && + !blankline(c2)) { + strcat(buf, tabtocol(buf, 6)); + strcat(buf, "#"); + strcat(buf, c2); + } + printf("%s\n", buf); + } + } +} + +/* + * genent_attr: + * Generic function for generating entries for all of the *_attr databases. + */ +int +genent_attr(line, cback, udata, database, ncol, cnames, nkeycol, keycol) + char *line; + int (*cback)(); + void *udata; + char *database; /* name of the database */ + int ncol; /* number of columns in the database */ + char **cnames; /* array of column names */ + int nkeycol; /* number of searchable columns that form the id */ + int keycol[]; /* array containing indices of searchable columns */ +{ + int i, j; + char buf[BUFSIZ+1]; + char aname[NIS_MAXNAMELEN]; + char *s; + char *sep = KV_TOKEN_DELIMIT; + char *lasts; + entry_col *ecol; + nis_object eobj; + + /* + * don't clobber our argument + */ + if (strlen(line) >= sizeof (buf)) { + strcpy(parse_err_msg, "line too long"); + return (GENENT_PARSEERR); + } + strcpy(buf, line); + + /* + * setup and clear column data + */ + if ((ecol = (entry_col *)malloc(ncol * sizeof (entry_col))) == NULL) + return (GENENT_ERR); + memset((char *)ecol, 0, ncol * sizeof (ecol)); + + /* Split up columns */ + i = 0; + while (i < ncol) { + s = _strtok_escape(i ? NULL : buf, sep, &lasts); + if (s == NULL) { + ecol[i].ec_value.ec_value_val = NULL; + ecol[i].ec_value.ec_value_len = 0; + } else { + ecol[i].ec_value.ec_value_val = s; + ecol[i].ec_value.ec_value_len = strlen(s)+1; + } + ecol[i].ec_flags = EN_MODIFIED; + i++; + } + + /* build entry */ + eobj = nis_default_obj; + eobj.zo_data.zo_type = ENTRY_OBJ; + eobj.EN_data.en_type = ta_type; + eobj.EN_data.en_cols.en_cols_val = ecol; + eobj.EN_data.en_cols.en_cols_len = ncol; + + if (!cback) + cback = addentry; + + /* specify entry by unique identifier */ + memset(aname, 0, NIS_MAXNAMELEN * sizeof (char)); + strcat(aname, "["); + for (i = 0; i < nkeycol; i++) { + j = keycol[i]; + if (i) + strcat(aname, ","); + strcat(aname, cnames[j]); + strcat(aname, "=\""); + strcat(aname, ecol[j].ec_value.ec_value_val); + strcat(aname, "\""); + } + strcat(aname, "],"); + strcat(aname, ta_name); + + if ((*cback)(aname, &eobj, udata, 0)) + return (GENENT_CBERR); + + return (GENENT_OK); +} + +void +dump_attr(res, ncol) + nis_result *res; + int ncol; +{ + int i, j; + char *c; + + for (i = 0; i < res->objects.objects_len; i++) { + if (c = OBJ_COL_VAL(i, 0)) { + printf("%s", c); + for (j = 1; j < ncol; j++) { + c = OBJ_COL_VAL(i, j); + printf(":%s", c ? c : null_string); + } + printf("\n"); + } + } +} + +int +genent_auth_attr(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + int keycol[AUTHATTR_DB_NKEYCOL] = {AUTHATTR_KEYCOL0}; + char *cnames[AUTHATTR_DB_NCOL] = { + AUTHATTR_COL0_KW, + AUTHATTR_COL1_KW, + AUTHATTR_COL2_KW, + AUTHATTR_COL3_KW, + AUTHATTR_COL4_KW, + AUTHATTR_COL5_KW + }; + + return (genent_attr(line, cback, udata, NSS_DBNAM_AUTHATTR, + AUTHATTR_DB_NCOL, cnames, AUTHATTR_DB_NKEYCOL, keycol)); +} + +void +dump_auth_attr(res) + nis_result *res; +{ + dump_attr(res, AUTHATTR_DB_NCOL); +} + +int +genent_exec_attr(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char *cnames[EXECATTR_DB_NCOL] = { + EXECATTR_COL0_KW, + EXECATTR_COL1_KW, + EXECATTR_COL2_KW, + EXECATTR_COL3_KW, + EXECATTR_COL4_KW, + EXECATTR_COL5_KW, + EXECATTR_COL6_KW + }; + + return (genent_attr(line, cback, udata, NSS_DBNAM_EXECATTR, + EXECATTR_DB_NCOL, cnames, EXECATTR_DB_NKEYCOL, exec_attr_keycol)); +} + +void +dump_exec_attr(res) + nis_result *res; +{ + dump_attr(res, EXECATTR_DB_NCOL); +} + +int +genent_prof_attr(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + int keycol[PROFATTR_DB_NKEYCOL] = {PROFATTR_KEYCOL0}; + char *cnames[PROFATTR_DB_NCOL] = { + PROFATTR_COL0_KW, + PROFATTR_COL1_KW, + PROFATTR_COL2_KW, + PROFATTR_COL3_KW, + PROFATTR_COL4_KW + }; + + return (genent_attr(line, cback, udata, NSS_DBNAM_PROFATTR, + PROFATTR_DB_NCOL, cnames, PROFATTR_DB_NKEYCOL, keycol)); +} + +void +dump_prof_attr(res) + nis_result *res; +{ + dump_attr(res, PROFATTR_DB_NCOL); +} + +int +genent_user_attr(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + char *cnames[USERATTR_DB_NCOL] = { + USERATTR_COL0_KW, + USERATTR_COL1_KW, + USERATTR_COL2_KW, + USERATTR_COL3_KW, + USERATTR_COL4_KW, + }; + + return (genent_attr(line, cback, udata, NSS_DBNAM_USERATTR, + USERATTR_DB_NCOL, cnames, USERATTR_DB_NKEYCOL, user_attr_keycol)); +} + +void +dump_user_attr(res) + nis_result *res; +{ + dump_attr(res, USERATTR_DB_NCOL); +} + +int +genent_audit_user(line, cback, udata) + char *line; + int (*cback)(); + void *udata; +{ + int keycol[AUDITUSER_DB_NKEYCOL] = {AUDITUSER_KEYCOL0}; + char *cnames[AUDITUSER_DB_NCOL] = { + AUDITUSER_COL0_KW, + AUDITUSER_COL1_KW, + AUDITUSER_COL2_KW + }; + + return (genent_attr(line, cback, udata, NSS_DBNAM_AUDITUSER, + AUDITUSER_DB_NCOL, cnames, AUDITUSER_DB_NKEYCOL, keycol)); +} + +void +dump_audit_user(res) + nis_result *res; +{ + dump_attr(res, AUDITUSER_DB_NCOL); +} + +struct ttypelist_t ttypelist[] = { + { "key-value", 0, 0, 0, + 0, 0, 0, " \t", + dbmniskey, nisdbmkey, filedbmkey, genent_keyvalue, + dump_keyvalue, 0, + filedbmline_comment, filetodbm_keyvalue, fetchdbm_addkey, + printfkeystr_0 }, + { "aliases", "mail.aliases", "mail_aliases.org_dir", "mail_aliases", + 0, 0, 0, " \t", + dbmniskey, nisdbmkey_aliases, filedbmkey_aliases, genent_aliases, + dump_aliases, 0, + filedbmline_aliases, filetodbm_aliases, fetchdbm_addkey, + printfkeystr_0 }, + { "bootparams", "bootparams", "bootparams.org_dir", "bootparams_tbl", + 0, 0, 0, " \t", + dbmniskey, nisdbmkey, filedbmkey, genent_keyvalue, + dump_keyvalue, 0, + filedbmline_comment, filetodbm_keyvalue, fetchdbm_addkey, + printfkeystr_0 }, + { "ethers", "ethers.byaddr", "ethers.org_dir", "ethers_tbl", + 0, 0, 0, " \t", + dbmniskey, nisdbmkey, filedbmkey, genent_ethers, + dump_ethers, 0, + filedbmline_comment, filetodbm, fetchdbm, + printfkeystr_0 }, + { "group", "group.byname", "group.org_dir", "group_tbl", + 0, 0, 0, ":", + dbmniskey, nisdbmkey, filedbmkey, genent_group, + dump_group, 0, + filedbmline_plus, filetodbm, fetchdbm, + printfkeystr_0 }, + { "hosts", "hosts.byaddr", "hosts.org_dir", "hosts_tbl", + 0, 2, 0, " \t", + dbmniskey, nisdbmkey, filedbmkey, genent_hosts, + dump_hosts, dump_match_hosts, + filedbmline_comment, filetodbm, fetchdbm, + printfkeystr_cname }, + { "ipnodes", "ipnodes.byaddr", "ipnodes.org_dir", "ipnodes_tbl", + 0, 2, 0, " \t", + dbmniskey, nisdbmkey, filedbmkey, genent_hosts, + dump_hosts, dump_match_hosts, + filedbmline_comment, filetodbm, fetchdbm, + printfkeystr_cname }, + { "netgroup", "netgroup", "netgroup.org_dir", "netgroup_tbl", + 0, 0, 0, " \t", + dbmniskey, nisdbmkey, filedbmkey, genent_netgroup, + dump_netgroup, dump_match_netgroup, + filedbmline_comment, filetodbm_keyvalue, fetchdbm_addkey, + printfkeystr_netgroup }, + { "netid", "netid.byname", "cred.org_dir", "cred_tbl", + "auth_type=LOCAL", 0, 0, " \t", + dbmniskey_netid, nisdbmkey_netid, filedbmkey_netid, genent_netid, + dump_netid, 0, + filedbmline_comment, filetodbm, fetchdbm_addkey, + printfkeystr_01 }, + { "netmasks", "netmasks.byaddr", "netmasks.org_dir", "netmasks_tbl", + 0, 0, 0, " \t", + dbmniskey, nisdbmkey, filedbmkey, genent_netmasks, + dump_netmasks, 0, + filedbmline_comment, filetodbm_keyvalue, fetchdbm_addkey, + printfkeystr_0 }, + { "networks", "networks.byname", "networks.org_dir", "networks_tbl", + 0, 0, 0, " \t", + dbmniskey, nisdbmkey, filedbmkey, genent_networks, + dump_networks, dump_match_cname, + filedbmline_comment, filetodbm, fetchdbm, + printfkeystr_cname }, + { "passwd", "passwd.byname", "passwd.org_dir", "passwd_tbl", + 0, 0, 0, ":", + dbmniskey, nisdbmkey, filedbmkey, genent_passwd, + dump_passwd, 0, + filedbmline_plus, filetodbm, fetchdbm, + printfkeystr_0 }, + { "protocols", "protocols.byname", "protocols.org_dir", "protocols_tbl", + 0, 0, 0, " \t", + dbmniskey, nisdbmkey, filedbmkey, genent_protocols, + dump_protocols, dump_match_cname, + filedbmline_comment, filetodbm, fetchdbm, + printfkeystr_cname }, + {"publickey", "publickey.byname", "cred.org_dir", "cred_tbl", + "auth_type=DES", 2, 0, " \t", + dbmniskey_publickey, nisdbmkey_publickey, filedbmkey_publickey, + genent_publickey, dump_publickey, 0, + filedbmline_comment, filetodbm_keyvalue, fetchdbm_addkey, + printfkeystr_01 }, + { "rpc", "rpc.bynumber", "rpc.org_dir", "rpc_tbl", + 0, 2, 1, " \t", + dbmniskey, nisdbmkey, filedbmkey, genent_rpc, + dump_rpc, dump_match_cname, + filedbmline_comment, filetodbm, fetchdbm, + printfkeystr_cname }, + { "services", "services.byname", "services.org_dir", "services_tbl", + 0, -1, 1, " \t", + dbmniskey_services, nisdbmkey_services, filedbmkey, genent_services, + dump_services, dump_match_services, + filedbmline_comment, filetodbm, fetchdbm, + printfkeystr_services }, +#ifndef FOURDOTX + { "shadow", 0, "passwd.org_dir", "passwd_tbl", + 0, 0, 0, ":", + dbmniskey, nisdbmkey, filedbmkey, genent_shadow, + dump_shadow, 0, + filedbmline_plus, filetodbm, fetchdbm, + printfkeystr_0 }, +#endif + { "timezone", "timezone.byname", "timezone.org_dir", "timezone_tbl", + 0, 0, 1, " \t", + dbmniskey, nisdbmkey, filedbmkey, genent_timezone, + dump_timezone, 0, + filedbmline_comment, filetodbm, fetchdbm, + printfkeystr_0 }, + { "auth_attr", "auth_attr.byname", "auth_attr.org_dir", "auth_attr_tbl", + 0, 0, 0, ":", + dbmniskey, nisdbmkey, filedbmkey, genent_auth_attr, + dump_auth_attr, 0, + filedbmline_comment, filetodbm, fetchdbm, + printfkeystr_0 }, + { "exec_attr", "exec_attr.byname", "exec_attr.org_dir", "exec_attr_tbl", + 0, 0, 0, ":", + dbmniskey_attr, nisdbmkey_attr, filedbmkey_attr, genent_exec_attr, + dump_exec_attr, 0, + filedbmline_comment, filetodbm_attr, fetchdbm, + printfkeystr_0 }, + { "prof_attr", "prof_attr.byname", "prof_attr.org_dir", "prof_attr_tbl", + 0, 0, 0, ":", + dbmniskey, nisdbmkey, filedbmkey, genent_prof_attr, + dump_prof_attr, 0, + filedbmline_comment, filetodbm, fetchdbm, + printfkeystr_0 }, + { "user_attr", "user_attr.byname", "user_attr.org_dir", "user_attr_tbl", + 0, 0, 0, ":", + dbmniskey_attr, nisdbmkey_attr, filedbmkey_attr, genent_user_attr, + dump_user_attr, 0, + filedbmline_comment, filetodbm_attr, fetchdbm, + printfkeystr_0 }, + { "audit_user", "audit_user.byname", "audit_user.org_dir", + "audit_user_tbl", + 0, 0, 0, ":", + dbmniskey, nisdbmkey, filedbmkey, genent_audit_user, + dump_audit_user, 0, + filedbmline_comment, filetodbm, fetchdbm, + printfkeystr_0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +/* + * This routine dumps a table entry. If the dump_match routine + * is defined, then we call it to get all of the entries related + * to this one. For example, in the hosts table, we want all + * of the entries for a given address. If the dump_match routine + * returns 0, then the entry will be handled in conjuction with + * another entry and should be ignored now. If the dump_match + * routine is not defined, then we make a nis_result out of + * the entry and then dump it. + */ +int +dumpcback(table, entry, udata) + nis_name table; + nis_object *entry; + void *udata; +{ + datum key, val; + char *line; + nis_result *res; + nis_result matchres; + + if (tt->dump_match) { + res = tt->dump_match(table, entry); + if (res) { + tt->dump(res); + nis_freeresult(res); + } + } else { + matchres.status = NIS_SUCCESS; + matchres.objects.objects_len = 1; + matchres.objects.objects_val = entry; + tt->dump(&matchres); + } + return (0); +} + + +/* + * In "quick" mode, we ask the server for all of the entries for + * the table and then call the dump routine to merge entries and + * print them. This only works for small tables or on machines + * with a LOT of swap space. In "normal" mode, we go through + * the entries one by one, calling the dumpcback routine for + * each one. The dumpcback routine will find all of the entries + * related to the current one, merge them, and then print the + * result. + */ +void +dumptable() +{ + int i, j; + char buf[BUFSIZ+1], srch[NIS_MAXNAMELEN], *c0, *c1, *c2, *c3, *c4; + nis_result *eres; + + if ((strcmp("publickey", tt->ttype) == 0) && mechs) + sprintf(srch, "%s", ta_name); + else { + if (tt->clrsrch) + sprintf(srch, "[%s],%s", tt->clrsrch, ta_name); + else + sprintf(srch, "%s", ta_name); + } + + if (flags & F_QUICK) { + eres = nis_list(srch, allres|master, 0, 0); + tt->dump(eres); + } else { + eres = nis_list(srch, allres|master, dumpcback, 0); + + if (eres->status != NIS_CBRESULTS && + eres->status != NIS_NOTFOUND) { + nis_perror(eres->status, "can't list table"); + exit(1); + } + nis_freeresult(eres); + } +} + + +int lineno = 0; + +int +putfile() +{ + struct line_buf line; + char *p; + struct file_loc loc; + + line_buf_init(&line); + while (tt->filedbmline(&line, etcf, &lineno, &loc)) { + + switch ((*(tt->genent))(line.str, 0, 0)) { + case GENENT_OK: + break; + case GENENT_PARSEERR: + fprintf(stderr, "parse error: %s (line %d)\n", + parse_err_msg, lineno); + exit_val = 2; + break; + default: + return (1); + } + } + + return (0); +} + + +int +isypsym(key) + datum *key; +{ + if (key->dptr && (key->dsize > 3) && !memcmp(key->dptr, "YP_", 3)) + return (1); + + return (0); +} + +int +putdbm() +{ + datum key, val; + char *line; + + for (key = dbm_firstkey(dbmf); key.dptr; key = dbm_nextkey(dbmf)) { + + if (isypsym(&key)) + continue; + + if (line = tt->fetchdbm(key)) { + switch ((*(tt->genent))(line, 0, 0)) { + case GENENT_OK: + break; + case GENENT_PARSEERR: + fprintf(stderr, "parse error: %s (key %.*s)\n", + parse_err_msg, key.dsize, key.dptr); + exit_val = 2; + break; + default: + return (1); + } + } else { + fprintf(stderr, "bad dbm file"); + exit(1); + } + } + + return (0); +} + +/* + * __nis_form_keys: puts "," as key component separator, as required by + * nis_list etc. + * E.g.: "name=File System Mangement,policy=tsol,id=/usr/sbin/mount" is the + * corrent search key for exec_attr. + * + */ +void +__nis_form_keys(char *keyname) +{ + char *p, *s; + + s = keyname; + while (s) { + p = strpbrk(s, tt->filesep); + if (p == NULL) + break; + *p = ','; + s = ++p; + } +} + +int +addfile(merge) + int merge; +{ + struct line_buf line; + char **p; + datum key; + datum content; + datum tmp; + char *niskey, keyname[NIS_MAXNAMELEN], indexname[NIS_MAXNAMELEN]; + nis_result *eres; + struct file_loc loc; + + line_buf_init(&line); + while (tt->filedbmline(&line, etcf, &lineno, &loc)) { + + key = tt->filedbmkey(line.str); + if (key.dptr == 0) { + fprintf(stderr, + "parse error: zero key length (line %d)\n", lineno); + continue; + } + + /* if merging, add key/file_offset to dbm file */ + if (merge) { + content.dsize = sizeof (loc); + content.dptr = (char *)&loc; + tmp = dbm_fetch(dbmf, key); + if (tmp.dptr == NULL) { + if (dbm_store(dbmf, key, content, 1) != 0) { + fprintf(stderr, + "problem storing %.*s %.*s\n", + key.dsize, key.dptr, + content.dsize, content.dptr); + dbm_close(dbmf); + return (1); + } + } + } + (void) __nis_quote_key(tt->dbmniskey(key), keyname, + sizeof (keyname)); + + /* + * Make sure "," is the separator for multi-component key. E.g., + * "name=File System Mangement,policy=tsol,id=/usr/sbin/mount" + * is the correct search key for exec_attr. + * + * __nis_quote_key puts quotes around ",". Is case of multi- + * component key, "," is a separator and should not be in + * quotes (requirement of nis_list etc. Hence __nis_form_keys + * is called after __nis_quote_key. + */ + (void) __nis_form_keys(keyname); + + sprintf(indexname, "[%s],%s", keyname, ta_name); + eres = nis_list(indexname, fpath|MASTER_ONLY, 0, 0); + switch (eres->status) { + case NIS_BADATTRIBUTE: + fprintf(stderr, "parse error: bad key (line %d)\n", + lineno); + break; + case NIS_SUCCESS: + switch ((*(tt->genent))(line.str, 0, eres)) { + case GENENT_OK: + break; + case GENENT_PARSEERR: + fprintf(stderr, "parse error: %s (line %d)\n", + parse_err_msg, lineno); + exit_val = 2; + break; + default: + nis_freeresult(eres); + return (1); + } + break; + case NIS_NOTFOUND: + switch ((*(tt->genent))(line.str, 0, 0)) { + case GENENT_OK: + break; + case GENENT_PARSEERR: + fprintf(stderr, "parse error: %s (line %d)\n", + parse_err_msg, lineno); + exit_val = 2; + break; + default: + nis_freeresult(eres); + return (1); + } + break; + default: + nis_perror(eres->status, "can't list table"); + exit(1); + } + nis_freeresult(eres); + } + + return (0); +} + + +int +adddbm() +{ + datum key, val; + char *line; + char *niskey, keyname[NIS_MAXNAMELEN], indexname[NIS_MAXNAMELEN]; + nis_result *eres; + + for (key = dbm_firstkey(dbmf); key.dptr; key = dbm_nextkey(dbmf)) { + + if (isypsym(&key)) + continue; + + if (line = tt->fetchdbm(key)) { + (void) __nis_quote_key(tt->dbmniskey(key), keyname, + sizeof (keyname)); + sprintf(indexname, "[%s],%s", keyname, + ta_name); + eres = nis_list(indexname, fpath|MASTER_ONLY, 0, 0); + switch (eres->status) { + case NIS_BADATTRIBUTE: + fprintf(stderr, "bad key (%.*s)\n", key.dsize, + key.dptr); + break; + case NIS_SUCCESS: + switch ((*(tt->genent))(line, 0, eres)) { + case GENENT_OK: + break; + case GENENT_PARSEERR: + fprintf(stderr, + "parse error: %s (key %.*s)\n", + parse_err_msg, key.dsize, key.dptr); + exit_val = 2; + break; + default: + nis_freeresult(eres); + return (1); + } + break; + case NIS_NOTFOUND: + switch ((*(tt->genent))(line, 0, 0)) { + case GENENT_OK: + break; + case GENENT_PARSEERR: + fprintf(stderr, + "parse error: %s (key %.*s)\n", + parse_err_msg, key.dsize, key.dptr); + exit_val = 2; + break; + default: + nis_freeresult(eres); + return (1); + } + break; + default: + nis_perror(eres->status, "can't list table"); + exit(1); + } + nis_freeresult(eres); + + } else { + fprintf(stderr, "bad dbm file"); + exit(1); + } + } + + return (0); +} + + +int +deletecback(table, entry, udata) + nis_name table; + nis_object *entry; + void *udata; +{ + datum key; + char *line; + nis_result matchres; + static nis_name last_table = NULL; + static nis_object *last_entry = NULL; + nis_name tmp_table = table; + nis_object *tmp_entry; + int ret; + + /* + * nis_list requires that the previous entry exists + * to return the next batch of entries. If we remove + * the last entry in a batch, we will not be able to + * get any more entries. + * + * To work around this, we will remember the last entry + * and only remove it after obtaining the next entry. + * deletecback must be called after nis_list completes + * to remove the last entry. + */ + + if (entry != NULL) { + key = tt->nisdbmkey(entry); + + if (line = ((char *(*)())udata)(key)) { + matchres.objects.objects_len = 1; + matchres.objects.objects_val = entry; + + switch ((*(tt->genent))(line, matchentry, &matchres)) { + case GENENT_CBERR: + return (0); + case GENENT_OK: + break; + case GENENT_PARSEERR: + fprintf(stderr, + "parse error (key %.*s)\n", + key.dsize, key.dptr); + exit_val = 2; + return (1); + default: + return (1); + } + } + + tmp_entry = nis_clone_object(entry, 0); + if (tmp_entry == NULL) { + fprintf(stderr, + "Could not clone %s\n", NKEYVAL(0)); + exit_val = 2; + return (0); + } + tmp_table = strdup(table); + if (tmp_table == NULL) { + fprintf(stderr, "deletecback: out of memory\n"); + exit_val = 2; + return (0); + } + } else { + tmp_entry = NULL; + } + + if (last_entry == NULL || last_table == NULL) { + last_table = tmp_table; + last_entry = tmp_entry; + return (0); + } + + ret = removeentry(last_table, last_entry); + + nis_destroy_object(last_entry); + free(last_table); + + last_table = tmp_table; + last_entry = tmp_entry; + + return (ret); +} + + +/* + * The deletecback routine is too slow for large files, so we create + * a dbm file and then use mergedbm instead. An exit routine will + * clean up the temporary files when we exit. + */ +int +mergefile() +{ + char line[BUFSIZ+1], *p; + char lname[NIS_MAXNAMELEN]; + datum key; + datum content; + datum tmp; + char *niskey, keyname[NIS_MAXNAMELEN]; + nis_result *eres; + char *dbmfile; + FILE *fp; + + /* create dbm file from text file */ + + dbmfile = tmpnam((char *)0); + + strcpy(tmpdirbuf, dbmfile); + strcat(tmpdirbuf, ".dir"); + strcpy(tmppagbuf, dbmfile); + strcat(tmppagbuf, ".pag"); + + fp = fopen(tmpdirbuf, "w"); + if (fp == 0) { + fprintf(stderr, "can't open %s\n", tmpdirbuf); + return (1); + } + fclose(fp); + created_dir = 1; + + fp = fopen(tmppagbuf, "w"); + if (fp == 0) { + fprintf(stderr, "can't open %s\n", tmppagbuf); + return (1); + } + fclose(fp); + created_pag = 1; + + if ((dbmf = dbm_open(dbmfile, O_RDWR, 0)) == 0) { + fprintf(stderr, "can't open dbmfile %s\n", dbmfile); + exit(1); + } + + if (addfile(1)) + return (1); + + if (mechs && (strcmp(tt->ttype, "publickey") == 0)) + sprintf(lname, "[],%s", ta_name); + else + sprintf(lname, "[%s],%s", + (tt->clrsrch)?tt->clrsrch:"", ta_name); + eres = nis_list(lname, MASTER_ONLY, deletecback, + (void*)(fetchfile)); + (void) deletecback(NULL, NULL, NULL); + if (eres->status != NIS_CBRESULTS && + eres->status != NIS_NOTFOUND) { + nis_perror(eres->status, "can't list table"); + exit(1); + } + + nis_freeresult(eres); + + /* dbm files will be removed in cleanup, called at exit */ + dbm_close(dbmf); + + return (0); +} + +int +mergedbm() +{ + nis_result *eres; + char lname[NIS_MAXNAMELEN]; + + if ((strcmp(tt->ttype, "publickey") == 0) && mechs) { + fprintf(stderr, +"Merging of extended Diffie-Hellman keys from NIS(YP) are not supported!\n"); + exit(1); + } + + if (adddbm()) + return (1); + + sprintf(lname, "[%s],%s", + (tt->clrsrch)?tt->clrsrch:"", ta_name); + eres = nis_list(lname, MASTER_ONLY, deletecback, (void*)(tt->fetchdbm)); + (void) deletecback(NULL, NULL, NULL); + if (eres->status != NIS_CBRESULTS && + eres->status != NIS_NOTFOUND) { + nis_perror(eres->status, "can't list table"); + exit(1); + } + + nis_freeresult(eres); + return (0); +} + +void +cleanup() +{ + if (created_dir) + unlink(tmpdirbuf); + if (created_pag) + unlink(tmppagbuf); +} + + +main(argc, argv) + int argc; + char *argv[]; +{ + char *defstr = 0, *ttype, *tname = 0, *ypdomain = 0, *ypmap = 0, + *etcfile = 0; + int c; + int op = OP_ADD; + nis_result *tres, *rres; + char rname[NIS_MAXNAMELEN]; + char dbmfile[MAXPATHLEN]; + + /* + * We register an exit routine to clean up any temporary + * files. + */ + atexit(cleanup); + + mechs = __nis_get_mechanisms(FALSE); + + while ((c = getopt(argc, argv, "D:PAMvapqrmdt:f:y:Y:o")) != -1) { + switch (c) { + case 'D': + defstr = optarg; + break; + case 'P': + fpath = FOLLOW_PATH; + break; + case 'A': + allres = ALL_RESULTS; + break; + case 'M': + master = MASTER_ONLY; + break; + case 'p': + flags |= F_PASSWD; + break; + case 'v': + flags |= F_VERBOSE; + break; + case 'a': + if (op) + usage(); + op = OP_ADD; + break; + case 'r': + if (op) + usage(); + op = OP_REPLACE; + break; + case 'm': + if (op) + usage(); + op = OP_MERGE; + break; + case 'd': + if (op) + usage(); + op = OP_DUMP; + break; + case 'q': + flags |= F_QUICK; + break; + case 't': + tname = optarg; + break; + case 'f': + if (ypmap || ypdomain) + usage(); + etcfile = optarg; + break; + case 'y': + if (etcfile) + usage(); + ypdomain = optarg; + break; + case 'Y': + if (etcfile) + usage(); + ypmap = optarg; + break; + case 'o': + oldpubkeymode++; + break; + default: + usage(); + } + } + + if (ypmap && !ypdomain) + usage(); + + if ((op == OP_MERGE) && !ypdomain && !etcfile) + usage(); + + if (argc - optind < 1) + usage(); + + ttype = argv[optind++]; + + for (tt = ttypelist; tt->ttype; tt++) + if (strcmp(tt->ttype, ttype) == 0) + break; + if (tt->ttype == 0) { + fprintf(stderr, "type %s not supported; supported types are:\n", + ttype); + for (tt = ttypelist; tt->ttype; tt++) + fprintf(stderr, "\t%s\n", tt->ttype); + exit(1); + } + + if (ypdomain && !ypmap && !(tt->ypmap)) { + fprintf(stderr, "yp map not known; specify with -Y\n"); + exit(1); + } + + if (!tname && !(tt->nistbl)) { + fprintf(stderr, "nis table not known; specify with -t\n"); + exit(1); + } + + if (argc - optind > 0) { + nisdomain = argv[optind++]; + if (nisdomain[strlen(nisdomain)-1] != '.') { + fprintf(stderr, + "nisdomain must be fully qualified.\n"); + exit(1); + } + } else + nisdomain = nis_local_directory(); + + if (argc - optind > 0) + usage(); + + if (!tname) + tname = tt->nistbl; + + if (tname[strlen(tname)-1] != '.') + sprintf(ta_name, "%s.%s", tname, nisdomain); + else + strcpy(ta_name, tname); + + tres = nis_lookup(ta_name, MASTER_ONLY|FOLLOW_LINKS); + if (tres->status != NIS_SUCCESS) { + nis_perror(tres->status, ta_name); + exit(1); + } + ta_obj = tres->objects.objects_val; + + if (ta_obj->zo_data.zo_type != NIS_TABLE_OBJ) { + fprintf(stderr, "%s is not a table!\n", ta_name); + exit(1); + } + + /* + * Check type of table + */ + if (tt->ta_type) { + if (strcmp(ta_obj->zo_data.objdata_u.ta_data.ta_type, + tt->ta_type)) { + fprintf(stderr, "%s is not of type %s!\n", + ta_name, tt->ta_type); + exit(1); + } + } else { + /* + * key-value requires two column table + */ + if (ta_obj->zo_data.objdata_u.ta_data.ta_cols.ta_cols_len + != 2) { + fprintf(stderr, "%s is not a two column table!\n", + ta_name); + exit(1); + } + } + strcpy(ta_type, ta_obj->zo_data.objdata_u.ta_data.ta_type); + + if (op == OP_DUMP) { + master = MASTER_ONLY; + dumptable(); + exit(exit_val); + } + + if (!nis_defaults_init(defstr)) + exit(1); + + if (nis_default_obj.zo_ttl > ta_obj->zo_ttl) + nis_default_obj.zo_ttl = ta_obj->zo_ttl; + + if (ypdomain) { + sprintf(dbmfile, "/var/yp/%s/%s", ypdomain, + (ypmap)?ypmap:tt->ypmap); + if ((dbmf = dbm_open(dbmfile, O_RDONLY, 0)) == 0) { + fprintf(stderr, "can't open dbmfile %s\n", dbmfile); + exit(1); + } + } else if (etcfile) { + if ((etcf = fopen(etcfile, "r")) == 0) { + fprintf(stderr, "can't open file %s\n", etcfile); + exit(1); + } + } else { + etcfile = "stdin"; + etcf = stdin; + } + + if (flags & F_VERBOSE) + switch (op) { + case OP_MERGE: + printf("merging %s into table %s\n", + (ypdomain)?dbmfile:etcfile, ta_name); + break; + case OP_REPLACE: + case OP_ADD: + printf("adding %s to table %s\n", + (ypdomain)?dbmfile:etcfile, ta_name); + break; + } + + switch (op) { + case OP_REPLACE: + /* + * Handle special case of publickey and RPCSEC_GSS + * being configured on this system. + */ + if ((strcmp(tt->ttype, "publickey") == 0) && mechs) { + int mcount = 0; + int verbose = flags & F_VERBOSE; + + while (mechs[mcount]) { + char authtype[MECH_MAXATNAME+1]; + + if (__nis_mechalias2authtype( + mechs[mcount]->alias, + authtype, + MECH_MAXATNAME)) { + if (verbose) + printf( + "clearing table (auth_type=%s) %s\n", + authtype, ta_name); + + sprintf(rname, "[auth_type=%s],%s", + authtype, ta_name); + rres = nis_remove_entry(rname, 0, + REM_MULTIPLE); + switch (rres->status) { + case NIS_NOTFOUND: + case NIS_SUCCESS: + break; + default: + nis_perror(rres->status, + "can't clear table"); + exit(1); + } + nis_freeresult(rres); + } + mcount++; + } + } else { + if (flags & F_VERBOSE) { + if (tt->clrsrch) + printf("clearing table (%s) %s\n", + tt->clrsrch, ta_name); + else + printf("clearing table %s\n", ta_name); + } + + sprintf(rname, "[%s],%s", + (tt->clrsrch)?tt->clrsrch:"", ta_name); + rres = nis_remove_entry(rname, 0, REM_MULTIPLE); + switch (rres->status) { + case NIS_NOTFOUND: + case NIS_SUCCESS: + break; + default: + nis_perror(rres->status, "can't clear table"); + exit(1); + } + nis_freeresult(rres); + } + + if (ypdomain) + putdbm(); + else + putfile(); + break; + + case OP_ADD: + if (ypdomain) + adddbm(); + else + addfile(0); + break; + + case OP_MERGE: + if (ypdomain) + mergedbm(); + else + mergefile(); + break; + } + + if (flags & F_VERBOSE) { + printf("%d entries added/updated\n", nent_add); + if (op == OP_MERGE) + printf("%d entries removed\n", nent_del); + } + + exit(exit_val); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisauthconf.c b/usr/src/cmd/rpcsvc/nis/utils/nisauthconf.c new file mode 100644 index 0000000000..08f759eba0 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisauthconf.c @@ -0,0 +1,223 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisauthconf.c + * + * Configure NIS+ to use RPCSEC_GSS + * + * Copyright (c) 1997,1998 Sun Microsystems, Inc. + * All Rights Reserved. + * + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <libgen.h> +#include <rpcsvc/nis_dhext.h> + +static struct mech_t { + char *mechname; + char *keylen; + char *algtype; + char *alias; + char *additional; +} mechs[] = { + "diffie_hellman_1024_0", "1024", "0", "dh1024-0", "default integrity", + "diffie_hellman_640_0", "640", "0", "dh640-0", "default integrity", + "-", "-", "-", "des", "# AUTH_DES", + NULL, NULL, NULL, NULL, NULL +}; + +static void +preamble(FILE *f) +{ + (void) fprintf(f, "# DO NOT EDIT FILE, AUTOMATICALLY GENERATED\n"); + (void) fprintf(f, "# \n"); + (void) fprintf(f, + "# The format of this file may change or it may be removed\n"); + (void) fprintf(f, "# in future versions of Solaris.\n# \n"); +} + +static void +printconf() +{ + int i = 0; + mechanism_t **mechlist; + + if (mechlist = __nis_get_mechanisms(FALSE)) { + while (mechlist[i]) { + (void) printf("%s", mechlist[i]->alias); + + if (mechlist[++i]) + (void) printf(", "); + else + (void) printf("\n"); + } + } else + (void) printf("des\n"); + exit(0); +} + + +#define DESMECH1 "-" +#define DESMECH2 "des" +#define DESMECH3 "192" +#define DESMECH4 0 +#define HEADING1 "GSS Mechanism Name" +#define HEADING2 "Alias" +#define HEADING3 "Bit Size" +#define HEADING4 "Algorithm Type" +#define HEADFMT "%s%*c%s%*c%s%*c%s\n" +#define LINEFMT "%s%*c%s%*c%s%*c%d\n" +#define GENSPC(a, b) b, (a - strlen(b)) + 2, ' ' + +static void +listmechs() +{ + int sc[3], i; + char tmpstr[1024]; + mechanism_t **mechlist; + + sc[0] = strlen(HEADING1); + sc[1] = strlen(HEADING2); + sc[2] = strlen(HEADING3); + + if ((mechlist = __nis_get_mechanisms(FALSE)) == NULL) { + mechlist = (mechanism_t **) malloc(sizeof (mechanism_t *) * 2); + + mechlist[0] = (mechanism_t *) malloc(sizeof (mechanism_t)); + mechlist[1] = NULL; + + mechlist[0]->mechname = NULL; + mechlist[0]->alias = "des"; + mechlist[0]->keylen = 192; + mechlist[0]->algtype = 0; + } + + for (i = 0; mechlist[i]; i++) { + int m1 = 0, m2 = 0; + + if (mechlist[i]->mechname) m1 = strlen(mechlist[i]->mechname); + if (mechlist[i]->alias) m2 = strlen(mechlist[i]->alias); + + sc[0] = sc[0] > m1 ? sc[0] : m1; + sc[1] = sc[1] > m2 ? sc[1] : m2; + (void) snprintf(tmpstr, 1024, "%d", mechlist[i]->keylen); + sc[2] = sc[2] > strlen(tmpstr) ? sc[2] : strlen(tmpstr); + } + + (void) printf(HEADFMT, GENSPC(sc[0], HEADING1), + GENSPC(sc[1], HEADING2), + GENSPC(sc[2], HEADING3), HEADING4); + for (i = 0; mechlist[i]; i++) { + (void) snprintf(tmpstr, 1024, "%d", mechlist[i]->keylen); + (void) printf(LINEFMT, GENSPC(sc[0], + mechlist[i]->mechname ? + mechlist[i]->mechname : "-"), + GENSPC(sc[1], mechlist[i]->alias ? + mechlist[i]->alias : "-"), + GENSPC(sc[2], tmpstr), + mechlist[i]->algtype); + } + exit(0); +} + + +static void +usage(char *cmd) +{ + (void) fprintf(stderr, "usage:\n\t%s [-v] [mechanism, ...]\n", cmd); + exit(1); +} + + +void +main(int argc, char **argv) +{ + int c, i, dolistmechs = 0; + char *cmd = basename(argv[0]); + FILE *f; + + while ((c = getopt(argc, argv, "v")) != -1) { + switch (c) { + case 'v': + dolistmechs++; + break; + default: + usage(cmd); + } + } + + if (dolistmechs) + listmechs(); + + if (argc < 2) + printconf(); + + if (argc == 2 && (strcmp(argv[1], NIS_SEC_CF_DES_ALIAS) == 0)) { + (void) unlink(NIS_SEC_CF_PATHNAME); + exit(0); + } + + if (!(f = fopen(NIS_SEC_CF_PATHNAME, "w"))) { + (void) fprintf(stderr, + "Could not open %s for writing.\n", + NIS_SEC_CF_PATHNAME); + exit(1); + } + + preamble(f); + + for (i = 1; i < argc; i++) { + int j = 0; + int gotit = 0; + + while (mechs[j].alias) { + if (!(strcmp(argv[i], mechs[j].alias))) { + gotit++; + + (void) fprintf(f, "mech\t%s\t%s\t%s\t%s\t%s\n", + mechs[j].mechname, + mechs[j].keylen, + mechs[j].algtype, + mechs[j].alias, + mechs[j].additional); + (void) fflush(f); + break; + } + j++; + } + + if (!gotit) { + (void) fprintf(stderr, + "%s: Mechanism, %s, not found!\n", cmd, + argv[i]); + (void) fflush(f); + (void) fclose(f); + exit(1); + } + } + (void) fclose(f); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisbackup.cc b/usr/src/cmd/rpcsvc/nis/utils/nisbackup.cc new file mode 100644 index 0000000000..3fa1527179 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisbackup.cc @@ -0,0 +1,1159 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisbackup.cc + * + * Copyright (c) 1988-1997 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisbackup.cc + * + * A utility for performing database backups. + */ + +#include <stdio.h> +#include <syslog.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <signal.h> +#include <unistd.h> +#include <string.h> +#include <wait.h> +#include <stdlib.h> +#include <ctype.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <sys/stat.h> +#include <dirent.h> +#include <errno.h> +#include <rpcsvc/nis_db.h> +#include <rpcsvc/nis_tags.h> +#include "nis_bkrst.h" +#include "../rpc.nisd/nis_proc.h" +#include "../rpc.nisd/log.h" + +/* + * Global state variables. + */ +static bool_t verbiage = FALSE; +static bool_t root_dir = FALSE; +static bool_t file_cleanup = FALSE; +static bool_t backup_preserved = FALSE; +static bool_t server_ro = FALSE; +static int flags = 0; +static char *nis_local_dir = __nis_rpc_domain(); +static char *backupdir; +static directory_obj slist; + +static nis_error set_ro(void); +static void set_rw(void); +static void abort_backup(char *); +static void abort_handler(int); + +/* + * Needs to be cleaned up in nislog as well as here. nis_log_common.o + * is shared by rpc.nisd, nislog and nisbackup/nisrestore. map_log() + * calls abort_transaction, which is defined in nis_svc_log.c. We can't + * link with nis_log_svc, so we'll stub it here. It's not used, since + * we are not modifying the transaction log. + */ +int +abort_transaction(int xid) +{ + xid = 0; + return (0); +} + +static void +init_signals() +{ + sigset(SIGHUP, abort_handler); + sigset(SIGINT, abort_handler); + sigset(SIGTERM, abort_handler); +} + +static void +abort_handler(int sig) +{ + sig = 0; + abort_backup(backupdir); + exit(1); +} + +void +usage() +{ + fprintf(stderr, "usage: nisbackup [ -v ] backup-dir directory...\n"); + fprintf(stderr, " nisbackup [ -v ] -a backup-dir\n"); + exit(1); +} + +struct file_set { + char **fs; + u_int fssize; + u_int fslen; +}; + +void +fspush(char *name, struct file_set *fset) +{ + if (fset == NULL) { + syslog(LOG_ERR, "fspush: Internal error"); + exit(1); + } + if (fset->fs == 0) + if ((fset->fs = (char **) calloc(fset->fssize, + sizeof (char *))) == NULL) { + syslog(LOG_ERR, "Unable to calloc memory: %m"); + exit(1); + } + if (fset->fslen == fset->fssize) { + fset->fssize += 512; + if ((fset->fs = (char **) realloc(fset->fs, + fset->fssize * sizeof (char *))) == NULL) { + syslog(LOG_ERR, "Unable to calloc memory: %m"); + exit(1); + } + } + fset->fs[fset->fslen++] = name; +} + +/* + * This routine sets the rpc.nisd into a read-only state, so that the + * nisbackup can take a snapshot of the database. We set a global (server_ro) + * to indicate if we're sucessful. set_rw() is called on a kill signal (^C) + * and we don't want to reset the server to read-write if we're being aborted + * due to the existance of another nisbackup in progress! + */ +static nis_error +set_ro(void) +{ + nis_tag *res = NULL; + nis_error error = NIS_SUCCESS; + nis_tag tagctl; + + tagctl.tag_val = ""; + tagctl.tag_type = TAG_READONLY; + error = nis_servstate(slist.do_servers.do_servers_val, + &tagctl, 1, &res); + if (res != NULL) { + if (strlen(res->tag_val) != 0) { + error = NIS_TRYAGAIN; + } else { + if (error == NIS_SUCCESS) + server_ro = TRUE; + } + nis_freetags(res, 1); + } + return (error); +} + +static void +set_rw(void) +{ + nis_tag *res = NULL; + nis_error error; + nis_tag tagctl; + + /* + * If we never set the server to read-only, we don't want to try + * to set it to read-write. This could cause another instance of + * nisbackup to fail. + */ + if (server_ro == FALSE) + return; + tagctl.tag_val = ""; + tagctl.tag_type = TAG_READWRITE; + error = nis_servstate(slist.do_servers.do_servers_val, + &tagctl, 1, &res); + if (error != NIS_SUCCESS) { + syslog(LOG_ERR, + "Unable to reset rpc.nisd to read/write, kill and restart! %s\n", + nis_sperrno(error)); + } + if (res != NULL) + nis_freetags(res, 1); +} + +#define PRESERVE 0 +#define RESTORE 1 + +int +_mv_dir(char * name, int arg) +{ + struct stat s; + pid_t child; + siginfo_t si; + char src[BUFSIZ], dst[BUFSIZ]; + + if (arg == PRESERVE) { + strcpy(src, name); + sprintf(dst, "%stmp", name); + } else { + sprintf(src, "%stmp", name); + strcpy(dst, name); + } + if (stat(src, &s) == -1) { + syslog(LOG_ERR, + "Directory %s does not exist: %m", src); + return (1); + } + + switch (child = vfork()) { + case -1: /* error */ + syslog(LOG_ERR, "Cannot move directory %s", name); + return (1); + case 0: /* child */ + if (execl("/bin/mv", "mv", src, dst, NULL) < 0) + _exit(1); + _exit(0); /* Since this is the child proc, we'll exit */ + default: /* parent, we'll wait for the cp to complete */ + if (waitid(P_PID, child, &si, WEXITED) == -1) { + syslog(LOG_ERR, "Cannot move directory %s", + src); + return (1); + } else { + if (si.si_status != 0) { + syslog(LOG_ERR, + "Can't move directory: error = %d", + si.si_status); + return (1); + } + } + } + return (0); +} + +int +_rm_dir(char * name) +{ + struct stat s; + pid_t child; + siginfo_t si; + + if (stat(name, &s) == -1) { + syslog(LOG_ERR, + "nisbackup: directory %s does not exist: %m", + name); + return (1); + } + switch (child = vfork()) { + case -1: /* error */ + syslog(LOG_ERR, "Cannot remove files in %s", name); + return (1); + case 0: /* child */ + if (execl("/bin/rm", "rm", + "-r", + name, + NULL) < 0) + _exit(1); + _exit(0); /* Since this is the child proc, we'll exit */ + default: /* parent, we'll wait for the cp to complete */ + if (waitid(P_PID, child, &si, WEXITED) == -1) { + syslog(LOG_ERR, "Cannot remove files in %s", name); + return (1); + } else { + if (si.si_status != 0) { + syslog(LOG_ERR, + "Can't remove files: error = %d", + si.si_status); + return (1); + } + } + } + return (0); +} + +/* + * bool_t is_master_of() + * + * Boolean function to check if local host is the master for the object + * passed in nis_obj. If the passed in object is not fully qualified, then + * nis_getnames() is called to resolve the object's full name. The parameter, + * full_name, is used to return the fully qualified name (ignored if set to + * NULL). The serving list is returned in srv_list. + */ + +bool_t +is_master_of(char *nis_obj, char *full_name, directory_obj *srv_list) +{ + + char myname[NIS_MAXNAMELEN]; + nis_name *namelist; + nis_error err; + int i; + + /* + * We will use __nis_CacheBind instead of nis_lookup() so that we + * can take advantage of the NIS_SHARED_DIRCACHE. + */ + if (nis_obj[strlen(nis_obj) - 1] != '.') { /* Partially qualified */ + namelist = nis_getnames(nis_obj); + for (i = 0; namelist[i]; i++) { +#ifdef DEBUG +printf("name: %s\n", namelist[i]); +#endif + err = __nis_CacheBind(namelist[i], srv_list); + if (err == NIS_SUCCESS) { + if (full_name) + sprintf(full_name, "%s", namelist[i]); + break; + } + } + } else { /* Fully qualified */ + err = __nis_CacheBind(nis_obj, srv_list); + if (err != NIS_SUCCESS) { + if (verbiage) + syslog(LOG_INFO, + "Unable to lookup %s: %s", nis_obj, + nis_sperrno(err)); + return (0); + } + if (full_name) + sprintf(full_name, "%s", nis_obj); + } + + if (err != NIS_SUCCESS) { + if (verbiage) + syslog(LOG_INFO, + "Unable to lookup %s: %s", nis_obj, nis_sperrno(err)); + return (0); + } + /* + * If we can get a directory object, we check the master server name + * against the local host's name. This is the final result. + */ + if (err == NIS_SUCCESS) { + sprintf(myname, "%s", nis_local_host()); + if (strcasecmp(myname, + srv_list->do_servers.do_servers_val[0].name) + == 0) { + return (1); + } + } + return (0); +} + +/* + * This is a link list of all the directories backed up on this server. + */ +struct nis_dir_list { + char *name; + struct file_set obj_fs; + struct nis_dir_list *next; +}; +static struct nis_dir_list *dirlisthead = NULL; +enum CACHE_OP {CACHE_INIT, CACHE_ADD, CACHE_SAVE, CACHE_FIND}; +/* + * dirobj_list() maintains list of directory objects that are backed up + * with the -a(ll) option of the nisbackup utility. + * + * The fclose() calls are checked for error returns, _except_ in the case + * where an error has already occured, and we're returning FALSE. + */ +struct nis_dir_list * +dirobj_list(enum CACHE_OP op, char *argp) +{ + char filename[BUFSIZ], tmpname[BUFSIZ]; + char buf[BUFSIZ]; + FILE *fr, *fw; + char *name, *end; + int ss; + struct nis_dir_list *tmp = NULL; + struct nis_dir_list *dlp = NULL; + directory_obj sl; + struct stat st; + + switch (op) { + case CACHE_INIT: + /* Initialize cache with the serving list, for -a(ll) */ + strcpy(filename, nis_data("serving_list")); + fr = fopen(filename, "r"); + if (fr == NULL) { + ss = stat(filename, &st); + if (ss == -1 && errno == ENOENT) { + syslog(LOG_ERR, "Error opening %s: %m", + filename); + return (NULL); + } + } + while (fgets(buf, BUFSIZ, fr)) { + name = buf; + while (isspace(*name)) + name++; + end = name; + while (!isspace(*end)) + end++; + *end = NULL; + tmp = (struct nis_dir_list *) + XMALLOC(sizeof (struct nis_dir_list)); + if (tmp == NULL) { + fclose(fr); + syslog(LOG_ERR, + "Internal error, no memory: %m"); + exit(1); + } + /* + * If we're the master, and it hasn't already been added... + */ + if (is_master_of(name, tmpname, &sl)) { + for (dlp = dirlisthead; dlp; dlp = dlp->next) + if (strcasecmp(dlp->name, + (char *)tmpname) == 0) { + XFREE(tmp); + tmp = NULL; + break; + } + if (tmp == NULL) + continue; + if ((tmp->name = strdup(tmpname)) == NULL) { + XFREE(tmp); + fclose(fr); + syslog(LOG_ERR, + "Internal error, no memory: %m"); + exit(1); + } + tmp->obj_fs.fssize = BUFSIZ; + tmp->obj_fs.fslen = 0; + tmp->obj_fs.fs = NULL; + tmp->next = dirlisthead; + dirlisthead = tmp; + } else { +#ifdef DEBUG +fprintf(stderr, "dirobj_list: not master for %s\n", name); +#endif + XFREE(tmp); + } + } + if (fclose(fr) == EOF) { + syslog(LOG_ERR, "Error closing %s", filename); + exit(1); + } + return (dirlisthead); + + case CACHE_ADD: + if (argp == NULL) + return (NULL); + /* Check whether directory already added */ + for (tmp = dirlisthead; tmp; tmp = tmp->next) + if (strcasecmp(tmp->name, (char *)argp) == 0) + return (tmp); + /* Add it to the cache */ + tmp = (struct nis_dir_list *) + XMALLOC(sizeof (struct nis_dir_list)); + if (tmp == NULL) { + syslog(LOG_ERR, "Could not allocate memory: %m"); + return (NULL); + } + if ((tmp->name = strdup((char *)argp)) == NULL) { + syslog(LOG_ERR, "Could not allocate memory: %m"); + XFREE(tmp); + return (NULL); + } + tmp->obj_fs.fssize = BUFSIZ; + tmp->obj_fs.fslen = 0; + tmp->obj_fs.fs = NULL; + tmp->next = dirlisthead; + dirlisthead = tmp; +#ifdef DEBUG + fprintf(stderr, "objdir_list: added : %s\n", tmp->name); +#endif + return (tmp); + + case CACHE_SAVE: + /* + * Save back'd up object(s) to a file. If argp set, then we're + * doing an individual object to the file: + * /backupdir/nis+object/backup_list. If argp is NULL, we're + * doing the whole cached list in /backupdir/backup_list. + */ + if (argp == NULL) + return (NULL); + fw = fopen(argp, "w"); + if (fw == NULL) { + ss = stat(argp, &st); + if (ss == -1 && errno == ENOENT) { + fw = fopen(argp, "w+"); + } + if (fw == NULL) { + syslog(LOG_ERR, + "Could not open file %s for updating: %m", + argp); + return (NULL); + } + } + for (tmp = dirlisthead; tmp; tmp = tmp->next) + fprintf(fw, "%s\n", (char *)tmp->name); + if (fclose(fw) == EOF) + return (NULL); + return (dirlisthead); + + case CACHE_FIND: /* Search for object in cache */ + if (argp == NULL) + return (NULL); + for (tmp = dirlisthead; tmp; tmp = tmp->next) + if (strcasecmp(tmp->name, (char *)argp) == 0) + return (tmp); + return (NULL); + + default: + return (NULL); + } +} + +void +abort_backup(char * backupdir) +{ + char buf[NIS_MAXNAMELEN]; + struct stat s; + struct nis_dir_list *tmp = NULL; + + if (!backup_preserved) { + /* + * If we didn't save an existing backup, then we didn't create anything + * either, so there's nothing to cleanup. + */ + return; + } + if (verbiage) + syslog(LOG_WARNING, + "Aborting NIS+ directory backup. Cleaning up files."); + /* + * Check if backup/data directory exists. If yes, delete it. + */ + for (tmp = dirlisthead; tmp; tmp = tmp->next) { + if (file_cleanup) { + sprintf(buf, "%s/%s", backupdir, tmp->name); + if (stat(buf, &s) != -1) { + if (_rm_dir(buf)) { + syslog(LOG_ERR, + "Unable to remove directory %s", buf); + } + } else { + if (errno != ENOENT) { + syslog(LOG_ERR, + "Unable to stat file system directory %s: %m", + buf); + } + } + } + if (backup_preserved) { + if (_mv_dir(buf, RESTORE)) { + syslog(LOG_ERR, + "Unable to restore old backup %s.tmp", + buf); + } + } + } +} + +void +_cleanup(char * backupdir) +{ + char buf[NIS_MAXNAMELEN]; + struct stat s; + struct nis_dir_list *tmp = NULL; + + /* + * Check if preserved backup directories exists. If yes, delete them. + */ + for (tmp = dirlisthead; tmp; tmp = tmp->next) { + sprintf(buf, "%s/%stmp", backupdir, tmp->name); + if (stat(buf, &s) != -1) { + if (_rm_dir(buf)) { + syslog(LOG_ERR, + "Unable to remove old backup %s", buf); + } + } + } +} + +bool_t +copyfile(char *srcfile, char *destfile) +{ + int infd, outfd; + char buffer[4096]; + ssize_t n; + + if ((infd = open(srcfile, O_RDONLY)) == -1) + return (FALSE); + if ((outfd = open(destfile, O_WRONLY|O_CREAT, 0664)) == -1) { + close(infd); + return (FALSE); + } + while (n = read(infd, buffer, sizeof (buffer))) { + if (n == -1) { + close(infd); + close(outfd); + return (FALSE); + } + if (write(outfd, buffer, n) != n) { + close(infd); + close(outfd); + return (FALSE); + } + } + if (close(infd)) + return (FALSE); + if (close(outfd)) + return (FALSE); + return (TRUE); +} + + +bool_t +backup(char * backup_path, struct nis_dir_list *dlp) +{ + char logfile[NIS_MAXNAMELEN]; + char srcfile[NIS_MAXNAMELEN], destfile[NIS_MAXNAMELEN]; + struct stat s; + char *fs_p; + char **fs; + u_int fslen; + int i; + + if (verbiage) + syslog(LOG_INFO, "Copying files to backup partition: %s", + backup_path); + fs = dlp->obj_fs.fs; + fslen = dlp->obj_fs.fslen; + for (i = 0; i < fslen; ++i) { + sprintf(logfile, "%s.log", fs[i]); + fs_p = (fs[i]) + strlen(nis_data(NULL)); + sprintf(srcfile, "%s", fs[i]); + sprintf(destfile, "%s%s", backup_path, fs_p); + if ((stat(logfile, &s) == -1) && errno == ENOENT) { + /* + * Simple copy of file. + */ + if (copyfile(srcfile, destfile) == FALSE) + return (FALSE); + } else { + /* + * This loads the log file (if there is one) and copies all data + * to backup file. This esentially checkpoints the data file. + */ + if (db_copy_file(srcfile, destfile) == 0) + return (FALSE); + } + } + /* + * Finally, some special case files that need to be included, but that + * aren't part of the data dictionary. + */ + if ((root_dir) && (strcmp(dlp->name, nis_local_dir) == 0)) { + sprintf(srcfile, "%s", nis_data(ROOT_OBJ)); + sprintf(destfile, "%s/%s", backup_path, ROOT_OBJ); + return (copyfile(srcfile, destfile)); + } + return (TRUE); +} + + +/* + * bool_t is_this_a_dir() + * + * Boolean function to check if the object passed in is a directory object, + * as opposed to a table object. This routine uses nis_lookup(), in nis_db.c, + * to get a nis_object structure, which identifies the object type. + */ + +bool_t +is_this_a_dir(nis_name name) +{ + nis_result *res; + char fullname[NIS_MAXNAMELEN]; + + sprintf(fullname, "%s.%s", name, nis_local_dir); +#ifdef DEBUG + fprintf(stderr, "is_this_a_dir: looking up %s\n", fullname); +#endif + if ((res = nis_lookup(fullname, MASTER_ONLY+USE_DGRAM)) == NULL) + return (FALSE); + if (res->status != NIS_SUCCESS) { +#ifdef DEBUG + syslog(LOG_INFO, "Unable to lookup: %s: %s", fullname, + nis_sperrno(res->status)); +#endif + return (FALSE); + } + if (res->status == NIS_SUCCESS) { +#ifdef DEBUG + fprintf(stderr, "is_this_a_dir: %s found, type:%d\n", fullname, + res->objects.objects_val); +#endif + if (__type_of(res->objects.objects_val) == NIS_DIRECTORY_OBJ) + return (TRUE); + else + return (FALSE); + } + return (FALSE); +} + +void +sanity_checks(char *backupdir) +{ + struct stat s; + char buf[NIS_MAXNAMELEN]; + struct stat st_t; + nis_error error = NIS_SUCCESS; + struct nis_dir_list *tmp = NULL; + + if ((backupdir) && (*backupdir != '/')) { + syslog(LOG_ERR, "%s is not a absolute path name.", backupdir); + exit(1); + } + if (stat(backupdir, &s) == -1) { + if (errno == ENOENT) { + syslog(LOG_ERR, + "Directory %s does not exist, please create.", + backupdir); + exit(1); + } else { + syslog(LOG_ERR, + "An error occured while accessing %s: %m", backupdir); + exit(1); + } + } + + /* + * Are we backing up a root-domain ? Note, either the root object + * has been specified, or the -a(ll) option. If so, check for the + * existence of a root object. + */ + + sprintf(buf, "%s", nis_data(ROOT_OBJ)); + if ((dirobj_list(CACHE_FIND, nis_local_dir)) || + (flags & DOMAIN_ALL)) { + if (stat(buf, &st_t) == -1) { + if (errno == ENOENT) + root_dir = FALSE; + } + else + root_dir = TRUE; + } + + /* + * We can initialize the object cache with the serving list if we + * are backing up -a(ll). If the directory object(s) to backup is + * specified (not -a(ll) option), add that in to initialize the cache. + */ + if (flags & DOMAIN_ALL) { + if (dirobj_list(CACHE_INIT, NULL) == NULL) { + syslog(LOG_ERR, + "Could not initialize, is this a master server?"); + exit(1); + } + /* + * The cache is initialized, now we need initialize the server + * list. + */ + if (is_master_of(dirlisthead->name, NULL, &slist) == FALSE) { + syslog(LOG_ERR, "Not master for %s", dirlisthead->name); + exit(1); + } + } +#ifdef DEBUG + for (tmp = dirlisthead; tmp; tmp = tmp->next) + fprintf(stderr, "%s\n", tmp->name); +#endif + + /* + * Backup dirs setup, initialize signal handlers, which will cleanup + * if nisbackup is interupted from this point. + */ + init_signals(); + + /* + * Make sure we reset the read-write flag in the server upon exit. + * set_rw() checks server_ro == TRUE, to make sure we actually set + * it to read-only! + */ + atexit(set_rw); + + /* + * Set server state to read-only. This must be done to take a backup. + */ + int slp_time = 2; + int retries = 0; + while (((error = set_ro()) != NIS_SUCCESS) && + (retries++ < MAXRETRIES)) { + if (verbiage) { + syslog(LOG_INFO, + "Unable to set server to read-only: %s", + nis_sperrno(error)); + syslog(LOG_INFO, "Retrying in %d seconds", slp_time); + } + sleep(slp_time); + slp_time = slp_time * 2; + } + if (error != NIS_SUCCESS) { + syslog(LOG_INFO, + "Unable to set server to read-only mode, exiting."); + abort_backup(backupdir); + exit(1); + } + + /* + * Check if backup directory(ies) exists. If not, create. If they + * do exist (old backup), save them off, until new backup created. + * Upon successful creation of a new backup, remove the saved copy. + */ + for (tmp = dirlisthead; tmp; tmp = tmp->next) { + sprintf(buf, "%s/%s", backupdir, tmp->name); + if (stat(buf, &s) == -1) { + if (errno == ENOENT) { + if (mkdir(buf, 0777)) { + syslog(LOG_ERR, + "Unable to create directory %s: %m", + buf); + exit(1); + } + } else { + syslog(LOG_ERR, + "Unable to stat file system directory %s: %m", + buf); + exit(1); + } + } else { + /* + * Move old backup to backup-dir.tmp. Once backup + * successful, backup-dir.tmp will be removed. In + * case of failure, we will not lose old backup. + */ + if (_mv_dir(buf, PRESERVE)) { + syslog(LOG_ERR, + "Unable to save old backup %s", buf); + exit(1); + } + backup_preserved = TRUE; + if (mkdir(buf, 0777)) { + syslog(LOG_ERR, + "Unable to create directory %s: %m", + buf); + exit(1); + } + file_cleanup = TRUE; + } + + /* + * Check for a "data" directory under each backup-dir. + */ + sprintf(buf, "%s/%s", buf, NIS_DIR); + if (stat(buf, &s) == -1) { + if (errno == ENOENT) { + if (mkdir(buf, 0777)) { + syslog(LOG_ERR, + "Unable to create directory %s: %m", + buf); + abort_backup(backupdir); + exit(1); + } + } else { + syslog(LOG_ERR, + "Unable to stat directory %s: %m", buf); + abort_backup(backupdir); + exit(1); + } + } + } +} + +void +determine_file_set() +{ + char localdir[NIS_MAXNAMELEN]; + char full_name[NIS_MAXNAMELEN]; + char buf[NIS_MAXNAMELEN]; + char *domainof; + DIR *dirptr; + struct dirent *dent; + struct nis_dir_list *tmp = NULL; + + + sprintf(localdir, "%s", nis_data(NULL)); + if ((dirptr = opendir(localdir)) == NULL) { + syslog(LOG_ERR, "Unable to open %s: %m", localdir); + exit(1); + } + +/* This assumes that the dictionary has been initialized */ + + while ((dent = readdir(dirptr)) != NULL) { + if (strcmp(dent->d_name, ".") == 0) + continue; /* Ignore */ + if (strcmp(dent->d_name, "..") == 0) + continue; /* Ignore */ + if (strstr(dent->d_name, ".log")) + continue; /* Ignore log files */ + if (strcmp(dent->d_name, ROOT_OBJ) == 0) + continue; /* Ignore root.object, we'll get it later */ + + sprintf(buf, "%s/%s", nis_data(NULL), dent->d_name); + sprintf(full_name, "%s.%s", dent->d_name, nis_local_dir); +#ifdef DEBUG + printf("searching for: %s\n", buf); + printf("full name: %s\n", full_name); +#endif +/* + * Look up the file in the data dictionary. It could be a stray ufs + * file, that has nothing to do with NIS+. + */ + if (db_in_dict_file(buf)) { /* In dictionary? */ +/* + * Check for the directory objects that are being backed up. Put them into + * their own fileset, instead of the directory object that they belong + * to. This insures that each object backup is autonomous, and can be restored + * separately. Is it a DIRECTORY? Is it in list of objects to backup? fspush it! + */ + if (is_this_a_dir(dent->d_name)) { + if ((tmp = dirobj_list(CACHE_FIND, full_name)) + == NULL) + continue; + } else { +/* + * Determine the which directory object this object belongs to. + */ + domainof = nis_domain_of(full_name); + if ((tmp = dirobj_list(CACHE_FIND, + domainof)) == NULL) + continue; + } + fspush(strdup(buf), &(tmp->obj_fs)); + continue; + } /* In data dictionary */ + } /* while (readdir) */ + closedir(dirptr); +} + + +bool_t +create_perdir_translog(char *newlog, struct nis_dir_list *dlp) +{ + directory_obj sl; + char fullname[NIS_MAXNAMELEN]; + char *str_p = fullname; + uint32_t u_time; + uint32_t u_nl; + FILE *fw; + int ss; + struct stat st; + int i; + + fw = fopen(newlog, "w"); + if (fw == NULL) { + ss = stat(newlog, &st); + if (ss == -1 && errno == ENOENT) + if ((fw = fopen(newlog, "w+")) == NULL) { + syslog(LOG_ERR, + "Cannot open file %s for updating: %m", newlog); + return (FALSE); + } + } +/* + * Write a backup revision number to the this file, so that nisrestore(1M) + * will know if it can understand this backup. + */ + u_nl = htonl(BACKUPREV); + if (!fwrite(&u_nl, sizeof (u_nl), 1, fw)) { + syslog(LOG_ERR, "Cannot write rev # to file %s: %d", + newlog, ferror(fw)); + return (FALSE); + } +/* + * Find the last time stamp for the directory(ies) being modified. + */ + if (is_master_of(dlp->name, str_p, &sl) == FALSE) { + syslog(LOG_ERR, "Sorry, %s is not the master for %s", + nis_local_host(), dlp->name); + return (FALSE); + } +/* + * Since, we are running as root, the nis_local_principal() should be OK. + */ + for (i = 0; i < MAXRETRIES; i++) + if ((u_time = nis_cptime(sl.do_servers.do_servers_val, + fullname)) != 0) + break; + if (u_time == 0) { + syslog(LOG_ERR, "Unable to get last update time for %s", + fullname); + return (FALSE); + } + u_nl = htonl(u_time); + if (!fwrite(&u_nl, sizeof (u_nl), 1, fw)) { + syslog(LOG_ERR, "Cannot write update to file %s: error %d", + newlog, ferror(fw)); + return (FALSE); + } +#ifdef DEBUG + fprintf(stderr, "create_perdir_translog: last update: %d\n", u_time); +#endif + if (fclose(fw) == EOF) { + syslog(LOG_ERR, "Error closing file %s", newlog); + return (FALSE); + } + return (TRUE); +} + +bool_t +merge_backup_list() +{ + char buf[BUFSIZ]; + char filename[BUFSIZ]; + FILE *fr; + char *name, *end; + int ss; + struct stat st; + + sprintf(filename, "%s/%s", backupdir, BACKUPLIST); + ss = stat(filename, &st); + if (ss == -1 && errno == ENOENT) { + /* + * If it doesn't exist, CACHE_SAVE will create it. + */ + return ((dirobj_list(CACHE_SAVE, filename) == dirlisthead)); + } else { + fr = fopen(filename, "r"); + if (fr == NULL) + return (FALSE); + while (fgets(buf, BUFSIZ, fr)) { + name = buf; + while (isspace(*name)) + name++; + end = name; + while (!isspace(*end)) + end++; + *end = NULL; + if (dirobj_list(CACHE_ADD, name) == NULL) { + fclose(fr); + return (FALSE); + } + } + fclose(fr); + return ((dirobj_list(CACHE_SAVE, filename) == dirlisthead)); + } +} + +int +main(int argc, char *argv[]) +{ + char nisdomain[NIS_MAXNAMELEN]; + char buf[NIS_MAXNAMELEN]; + int c; + bool_t dbstat; + char *tmp; + struct nis_dir_list *dl = NULL; + + openlog("nisbackup", LOG_PID+LOG_NOWAIT, LOG_USER); + if (geteuid() != 0) { + syslog(LOG_ERR, "You must be root to run nisbackup."); + exit(1); + } + if (argc < 3) + usage(); + + while ((c = getopt(argc, argv, "av")) != -1) { + switch (c) { + case 'a' : + flags |= DOMAIN_ALL; + break; + case 'v' : + verbiage = TRUE; + break; + default: + usage(); + } + } + if (flags & DOMAIN_ALL) { + if (argc - optind != 1) + usage(); + backupdir = argv[optind++]; + } else { + if (argc - optind < 2) + usage(); + backupdir = argv[optind++]; + /* + * Yes, I really mean "=", not "==". tmp is assigned in while. + */ + while (tmp = argv[optind++]) { + memset(nisdomain, 0, sizeof (nisdomain)); + if (is_master_of(tmp, nisdomain, &slist)) { + if (dirobj_list(CACHE_ADD, nisdomain) + == NULL) { + syslog(LOG_ERR, + "Internal error, unable to add to cache"); + exit(1); + } + } else { + syslog(LOG_ERR, "Not master for %s", tmp); + exit(1); + } + } + } + sanity_checks(backupdir); + sprintf(buf, "%s.dict", nis_data(NULL)); + dbstat = db_initialize(buf); + if (dbstat == 0) { + abort_backup(backupdir); + syslog(LOG_ERR, "Unable to initialize %s", buf); + exit(1); + } + determine_file_set(); +/* + * Loop through dirobj_list, creating the backup_path for each + * object in the cache, then db_extrace_dict_entries, backup, + * create_perdir_translog and write backup_list for each entry in the cache. + */ + for (dl = dirlisthead; dl; dl = dl->next) { + sprintf(buf, "%s/%s/%s.dict", backupdir, dl->name, NIS_DIR); + if (!db_extract_dict_entries(buf, dl->obj_fs.fs, + dl->obj_fs.fslen)) { + abort_backup(backupdir); + syslog(LOG_ERR, "Unable to create %s.", buf); + exit(1); + } + sprintf(buf, "%s/%s/%s", backupdir, dl->name, NIS_DIR); + if (!backup(buf, dl)) { + abort_backup(backupdir); + syslog(LOG_ERR, "Unable to backup files to %s.", buf); + exit(1); + } + sprintf(buf, "%s/%s/%s", backupdir, dl->name, LASTUPDATE); + if (!create_perdir_translog(buf, dl)) { + abort_backup(backupdir); + syslog(LOG_ERR, "Unable to create %s.", buf); + exit(1); + } + } + if (!merge_backup_list()) { + abort_backup(backupdir); + syslog(LOG_ERR, "Unable to create %s.", BACKUPLIST); + exit(1); + } + _cleanup(backupdir); + exit(0); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/niscat.c b/usr/src/cmd/rpcsvc/nis/utils/niscat.c new file mode 100644 index 0000000000..ca46db0d77 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/niscat.c @@ -0,0 +1,284 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * niscat.c + * + * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * niscat.c + * + * nis+ table cat utility + */ + +#include <stdio.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <strings.h> +#include <stdlib.h> + +extern int optind; +extern char *optarg; + + +#define BINARY_STR "*BINARY*" + + +struct pl_data { + unsigned flags; + char ta_sep; +}; + +#define PL_BINARY 1 + +int +print_line(tab, ent, udata) + char *tab; + nis_object *ent; + void *udata; +{ + register entry_col *ec = ent->EN_data.en_cols.en_cols_val; + register int ncol = ent->EN_data.en_cols.en_cols_len; + register struct pl_data *d = (struct pl_data *)udata; + register int i; + int len; + + for (i = 0; i < ncol; i++) { + if (i > 0) + printf("%c", d->ta_sep); + len = ec[i].ec_value.ec_value_len; + if (len != 0) { + if (ec[i].ec_flags & EN_BINARY) { + if (d->flags & PL_BINARY) { + fwrite(ec[i].ec_value.ec_value_val, + 1, len, stdout); + } else { + printf(BINARY_STR); + } + } else { + printf("%s", ec[i].ec_value.ec_value_val); + } + } + } + printf("\n"); + + return (0); +} + +int +print_object(tab, ent, udata) + char *tab; + nis_object *ent; + void *udata; +{ + nis_print_object(ent); + return (0); +} + + +#define F_HEADER 1 +#define F_OBJECT 2 + +void +usage() +{ + fprintf(stderr, "usage: niscat [-LAMhv] [-s sep] tablename ...\n"); + fprintf(stderr, " niscat [-LPAM] -o name ...\n"); + exit(1); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + unsigned flags = 0; + char *name; + nis_result *tres, *eres; + char tname[NIS_MAXNAMELEN]; + struct pl_data pld; + int ncol, i; + ulong_t flinks = 0, fpath = 0, allres = 0, master = 0; + int error = 0; + int bad_name; + ulong_t list_flags; + nis_object *obj; + + /* + * By default, don't print binary data. + */ + pld.flags = 0; + + pld.ta_sep = '\0'; + while ((c = getopt(argc, argv, "LPAMohvs:")) != -1) { + switch (c) { + case 'L' : + flinks = FOLLOW_LINKS; + break; + case 'P': + fpath = FOLLOW_PATH; + break; + case 'A': + allres = ALL_RESULTS; + break; + case 'M': + master = MASTER_ONLY; + break; + case 'h': + flags |= F_HEADER; + break; + case 'v': + pld.flags |= PL_BINARY; + break; + case 'o': + flags |= F_OBJECT; + break; + case 's': + if (strlen(optarg) != 1) { + fprintf(stderr, + "separator must be a single character\n"); + exit(1); + } + pld.ta_sep = *optarg; + break; + default: + usage(); + } + } + + if (optind == argc) /* no table name */ + usage(); + + while (optind < argc) { + name = argv[optind++]; + + if (flags & F_OBJECT) { + if (*name == '[') { + list_flags = fpath|allres|master| + FOLLOW_LINKS|EXPAND_NAME; + tres = nis_list(name, list_flags, + print_object, 0); + if ((tres->status != NIS_CBRESULTS) && + (tres->status != NIS_NOTFOUND)) { + nis_perror(tres->status, name); + error = 1; + goto loop; + } + } else { + list_flags = flinks|master|EXPAND_NAME; + tres = nis_lookup(name, list_flags); + if (tres->status != NIS_SUCCESS) { + nis_perror(tres->status, name); + error = 1; + goto loop; + } + nis_print_object(tres->objects.objects_val); + } + goto loop; + } + + /* + * Get the table object using expand name magic. + */ + tres = nis_lookup(name, master|FOLLOW_LINKS|EXPAND_NAME); + if (tres->status != NIS_SUCCESS) { + nis_perror(tres->status, name); + error = 1; + goto loop; + } + + /* + * Construct the name for the table that we found. + */ + bad_name = (snprintf(tname, sizeof (tname), "%s.", + tres->objects.objects_val[0].zo_name) + >= sizeof (tname)); + if (!bad_name && + *(tres->objects.objects_val[0].zo_domain) != '.') + bad_name = (strlcat(tname, + tres->objects.objects_val[0].zo_domain, + sizeof (tname)) >= sizeof (tname)); + + if (bad_name) { + nis_perror(NIS_BADNAME, "can't list table"); + error = 1; + goto loop; + } + + /* + * Make sure it's a table object. + */ + if (tres->objects.objects_val[0].zo_data.zo_type != + NIS_TABLE_OBJ) { + fprintf(stderr, "%s is not a table!\n", tname); + error = 1; + goto loop; + } + + /* + * Use the table's separator character when printing entries. + * Unless one was specified w/ -s + */ + if (pld.ta_sep == '\0') { + pld.ta_sep + = tres->objects.objects_val[0].TA_data.ta_sep; + } + + /* + * Print column names if F_HEADER is set. + */ + if (flags & F_HEADER) { + obj = &tres->objects.objects_val[0]; + ncol = obj->TA_data.ta_cols.ta_cols_len; + c = pld.ta_sep; + printf("# "); + for (i = 0; i < ncol; i++) { + if (i > 0) + printf("%c", c); + printf("%s", + obj->TA_data.ta_cols.ta_cols_val[i].tc_name); + } + printf("\n"); + } + + /* + * Cat the table using a callback function. + */ + list_flags = allres|master; + eres = nis_list(tname, list_flags, print_line, (void *)&(pld)); + if ((eres->status != NIS_CBRESULTS) && + (eres->status != NIS_NOTFOUND)) { + nis_perror(eres->status, "can't list table"); + error = 1; + } + nis_freeresult(eres); + + loop: + nis_freeresult(tres); + } + + exit(error); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nischmod.c b/usr/src/cmd/rpcsvc/nis/utils/nischmod.c new file mode 100644 index 0000000000..823d9e1d77 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nischmod.c @@ -0,0 +1,339 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nischmod.c + * + * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nischmod.c + * + * nis+ object chmod/chown/chgrp/chttl utility + */ + +#include <stdio.h> +#include <string.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <stdlib.h> + +extern int optind; +extern char *optarg; + +extern bool_t nis_verifycred(); + +#define CMD_CHMOD 0 +#define CMD_CHOWN 1 +#define CMD_CHGRP 2 +#define CMD_CHTTL 3 + +int cmdnum; +char *cmdname; + +void +usage() +{ + char *str; + + switch (cmdnum) { + case CMD_CHMOD: + str = "mode"; + break; + case CMD_CHOWN: + str = "owner"; + break; + case CMD_CHGRP: + str = "group"; + break; + case CMD_CHTTL: + str = "ttl"; + break; + } + + fprintf(stderr, "usage: %s [-LPAf] %s name ...\n", + cmdname, str); + exit(1); +} + + +void +change_object(obj, udata) + nis_object *obj; + void *udata; +{ + switch (cmdnum) { + case CMD_CHMOD: + parse_rights(&(obj->zo_access), (char *)udata); + break; + case CMD_CHOWN: + obj->zo_owner = (nis_name)udata; + break; + case CMD_CHGRP: + obj->zo_group = (nis_name)udata; + break; + case CMD_CHTTL: + obj->zo_ttl = *(ulong_t *)udata; + if (obj->zo_data.zo_type == NIS_DIRECTORY_OBJ) + obj->DI_data.do_ttl = *(ulong_t *)udata; + break; + } +} + + +static char force = 0; +static int error = 0; + +static int +change_entry(name, ent, udata) + nis_name name; + nis_object *ent; + void *udata; +{ + nis_object newobj; + nis_result *res; + + newobj = *ent; + change_object(&newobj, udata); + res = nis_modify_entry(name, &newobj, MOD_SAMEOBJ); + if (res->status != NIS_SUCCESS) { + if (!force) { + nis_perror(res->status, "can't modify entry"); + error = 1; + } + } + nis_freeresult(res); + return (0); +} + + +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + char **plist, **p; + ulong_t flinks = 0, fpath = 0, allres = 0; + char *name; + void *udata; + uint_t x; + int bad_name; + nis_result *ores, *mres; + char oname[NIS_MAXNAMELEN]; + nis_object newobj; + + if (cmdname = strrchr(argv[0], '/')) + cmdname++; + else + cmdname = argv[0]; + + if (strcmp(cmdname, "nischmod") == 0) + cmdnum = CMD_CHMOD; + else if (strcmp(cmdname, "nischown") == 0) + cmdnum = CMD_CHOWN; + else if (strcmp(cmdname, "nischgrp") == 0) + cmdnum = CMD_CHGRP; + else if (strcmp(cmdname, "nischttl") == 0) + cmdnum = CMD_CHTTL; + else { + fprintf(stderr, "%s: bad command.\n", cmdname); + exit(1); + } + + while ((c = getopt(argc, argv, "LPAf")) != -1) { + switch (c) { + case 'L': + flinks = FOLLOW_LINKS; + break; + case 'P': + fpath = FOLLOW_PATH; + break; + case 'A': + allres = ALL_RESULTS; + break; + case 'f': + force = 1; + break; + default: + usage(); + } + } + + if (argc - optind < 2) + usage(); + + switch (cmdnum) { + case CMD_CHMOD: + /* + * attempt to parse the access rights first + */ + udata = argv[optind++]; + if (!parse_rights(&x, udata)) { + fprintf(stderr, "Can't parse access rights \"%s\"\n", + argv[optind-1]); + usage(); + } + break; + case CMD_CHOWN: + /* + * get the principal name using psuedo expand name + * magic + */ + udata = argv[optind++]; + if ((plist = nis_getnames(udata)) == 0) { + /* Could be either no mem or more likely bad name */ + nis_perror(NIS_BADNAME, udata); + exit(1); + } + for (p = plist; *p; p++) { + ulong_t flags; + /* + * First try to verify through regular means and only + * if that fails, then try the Master server. + */ + flags = (ulong_t)(USE_DGRAM|FOLLOW_LINKS|FOLLOW_PATH); + if (nis_verifycred(*p, flags) || + nis_verifycred(*p, (ulong_t)(flags | MASTER_ONLY))) + break; + } + if (*p == 0) { + if (!force) { + fprintf(stderr, + "%s: principal not found\n", + udata); + exit(1); + } else if ( + ((char *)udata)[strlen((char *)udata)-1] != '.') + exit(0); + } else + udata = *p; + break; + case CMD_CHGRP: + /* + * get the group name using psuedo expand name magic + */ + udata = argv[optind++]; + if ((plist = nis_getnames(udata)) == 0) { + /* Could be either no mem or more likely bad name */ + nis_perror(NIS_BADNAME, udata); + exit(1); + } + for (p = plist; *p; p++) + if (nis_verifygroup(*p) == NIS_SUCCESS) + break; + if (*p == 0) { + if (!force) { + fprintf(stderr, + "%s: group not found\n", + udata); + exit(1); + } else if ( + ((char *)udata)[strlen((char *)udata)-1] != '.') + exit(0); + } else + udata = *p; + break; + case CMD_CHTTL: + if (!parse_time(&x, argv[optind++])) { + fprintf(stderr, "can't parse time \"%s\"\n", + argv[optind-1]); + usage(); + } + udata = &x; + break; + } + + while (argc > optind) { + name = argv[optind++]; + + /* + * Get the object using expand name magic. + */ + if (*name == '[') { + ores = nis_list(name, + fpath|allres|FOLLOW_LINKS|MASTER_ONLY|EXPAND_NAME, + change_entry, udata); + if (ores->status != NIS_CBRESULTS && + ores->status != NIS_NOTFOUND) { + if (!force) { + nis_perror(ores->status, name); + error = 1; + } + } + } else { + ores = nis_lookup(name, flinks|MASTER_ONLY|EXPAND_NAME); + if (ores->status != NIS_SUCCESS) { + if (!force) { + nis_perror(ores->status, name); + error = 1; + } + goto loop; + } + + /* + * Construct the name for the object that we found. + */ + bad_name = (snprintf(oname, sizeof (oname), "%s.", + ores->objects.objects_val[0].zo_name) + >= sizeof (oname)); + if (!bad_name && + *(ores->objects.objects_val[0].zo_domain) != '.') + bad_name = (strlcat(oname, + ores->objects.objects_val[0].zo_domain, + sizeof (oname)) >= sizeof (oname)); + + if (bad_name) { + if (!force) { + fprintf(stderr, "%s: ", oname); + nis_perror(NIS_BADNAME, + "can't modify object"); + error = 1; + } + goto loop; + } + + /* + * Modify the object. + */ + newobj = ores->objects.objects_val[0]; + change_object(&newobj, udata); + mres = nis_modify(oname, &newobj); + if (mres->status != NIS_SUCCESS) { + if (!force) { + fprintf(stderr, "%s: ", oname); + nis_perror(mres->status, + "can't modify object"); + error = 1; + } + } + nis_freeresult(mres); + } + + loop: + nis_freeresult(ores); + } + + exit(error); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisdefaults.c b/usr/src/cmd/rpcsvc/nis/utils/nisdefaults.c new file mode 100644 index 0000000000..bc90747d1b --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisdefaults.c @@ -0,0 +1,203 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisdefaults.c + * + * Copyright (c) 1988-1992 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisdefaults.c + * + * A simple utility to tell you what the defaults are that will be + * plugged into object creation. + * + * Copyright (c) 1992 Sun Microsystems Inc. + * All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <rpcsvc/nis.h> + +extern int key_secretkey_is_set_g(); + +usage(cmd) + char *cmd; +{ + fprintf(stderr, "usage: %s [-pdhgrtsav]\n", cmd); + fprintf(stderr, " p = default principal name.\n"); + fprintf(stderr, " d = default domain name.\n"); + fprintf(stderr, " h = default host name.\n"); + fprintf(stderr, " g = default group name.\n"); + fprintf(stderr, " r = default access rights.\n"); + fprintf(stderr, " t = default time to live.\n"); + fprintf(stderr, " s = default search path.\n"); + fprintf(stderr, " a = all of the above (default).\n"); + fprintf(stderr, " v = verbose.\n"); + + exit(1); +} + +extern int optind; +extern char *optarg; + +extern nis_object nis_default_obj; + +main(argc, argv) + int argc; + char *argv[]; +{ + nis_name *result; + int c; + u_long secs, days, hrs, mins; + int i, verbose = 1, + pa = 1, /* print everything */ + ph = 0, /* print host */ + pd = 0, /* print domain */ + pp = 0, /* print principal */ + pr = 0, /* print rights */ + pt = 0, /* print ttl */ + pg = 0, /* print group */ + ps = 0; /* print search path */ + + while ((c = getopt(argc, argv, "hpartgdsv")) != -1) { + switch (c) { + case 'v' : + verbose = 50; + break; + case 'h' : + pa = 0; + ph++; + verbose--; + break; + case 'p' : + pa = 0; + verbose--; + pp++; + break; + case 'r' : + pa = 0; + verbose--; + pr++; + break; + case 't' : + pa = 0; + verbose--; + pt++; + break; + case 'g' : + pa = 0; + verbose--; + pg++; + break; + case 'd' : + pa = 0; + verbose--; + pd++; + break; + case 's' : + pa = 0; + verbose--; + ps++; + break; + case 'a' : + pa++; + verbose--; + break; + case '?' : + usage(argv[0]); + break; + } + } + + if (verbose < 0) + verbose = 0; + nis_defaults_init(NULL); + if (pa || pp) { + if (verbose) + printf("Principal Name : "); + printf("%s", nis_default_obj.zo_owner); + if (verbose && ! key_secretkey_is_set_g(0, 0)) + printf(" (not authenticated)"); + printf("\n"); + } + if (pa || pd) { + if (verbose) + printf("Domain Name : "); + printf("%s\n", nis_local_directory()); + } + if (pa || ph) { + if (verbose) + printf("Host Name : "); + printf("%s\n", nis_local_host()); + } + if (pa || pg) { + if (verbose) + printf("Group Name : "); + if (strlen(nis_default_obj.zo_group) != 0) + printf("%s\n", nis_default_obj.zo_group); + else + printf("%s\n", nis_local_group()); + } + if (pa || pr) { + if (verbose) + printf("Access Rights : "); + nis_print_rights(nis_default_obj.zo_access); + printf("\n"); + } + if (pa || pt) { + if (verbose) + printf("Time to live : "); + secs = nis_default_obj.zo_ttl; + days = secs / 86400; + hrs = (secs - (days * 86400)) / 3600; + mins = (secs - (days * 86400) - (hrs * 3600)) / 60; + secs = secs % 60; + if (verbose) { + if (days) + printf("%dD, ", days); + printf("%02d:%02d:%02d\n", hrs, mins, secs); + } else + printf("%d\n", nis_default_obj.zo_ttl); + } + if (pa || ps) { + result = nis_getnames("foo"); + if (verbose) + printf("Search Path : "); + if (result && result[0]) { + printf("%s\n", nis_domain_of(result[0])); + i = 1; + while (result[i]) { + if (verbose) + printf(" "); + printf("%s\n", + nis_domain_of(result[i++])); + } + } else + printf("**NONE**\n"); + } + exit(0); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/niserror.c b/usr/src/cmd/rpcsvc/nis/utils/niserror.c new file mode 100644 index 0000000000..3eb1089ff4 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/niserror.c @@ -0,0 +1,63 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * niserror.c + * + * Copyright (c) 1988-1992 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * niserror.c + * + * This module prints the error message associated with an NIS+ + * error code. + */ + +#include <stdio.h> +#include <ctype.h> +#include <rpcsvc/nis.h> + +usage() +{ + fprintf(stderr, "usage: niserror error-num\n"); + exit(1); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + nis_error err; + + if (argc != 2) + usage(); + + if (! isdigit(*argv[1])) + usage(); + + err = (nis_error) atoi(argv[1]); + printf("%s\n", nis_sperrno(err)); + exit(0); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisgrep.c b/usr/src/cmd/rpcsvc/nis/utils/nisgrep.c new file mode 100644 index 0000000000..b9d1dd0e41 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisgrep.c @@ -0,0 +1,376 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisgrep.c + * + * Copyright (c) 1988-1992 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisgrep.c + * + * nis+ table grep utility + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <regex.h> + +extern int optind; +extern char *optarg; + +extern char *nisname_index(); + + +#define BINARY_STR "*BINARY*" +#define TABLE_COLS(tres) tres->objects.objects_val[0].TA_data.ta_cols + +#define EXIT_MATCH 0 +#define EXIT_NOMATCH 1 +#define EXIT_ERROR 2 + +struct pl_data { + unsigned flags; + char ta_sep; + u_long nmatch; + regex_t *re_dfa; + int *dfa_set; /* dfa_set[i] set if re_dfa[i] is valid */ +}; + +#define PL_BINARY 1 +#define PL_COUNT 2 +#define PL_OBJECT 4 +#define PL_NOCASE 8 + +static +char * +strlower(s) + char *s; +{ + int i; + int len; + char *p; + + len = strlen(s) + 1; + p = malloc(len); + if (p == NULL) { + fprintf(stderr, "No memory!\n"); + exit(EXIT_ERROR); + } + /* this loop includes the terminating null */ + for (i = 0; i < len; i++) { + if (isupper(s[i])) + p[i] = tolower(s[i]); + else + p[i] = s[i]; + } + + return (p); +} + + +int +print_line(tab, ent, udata) + char *tab; + nis_object *ent; + void *udata; +{ + int len; + char *val; + int res; + register entry_col *ec = ent->EN_data.en_cols.en_cols_val; + register int ncol = ent->EN_data.en_cols.en_cols_len; + register struct pl_data *d = (struct pl_data *)udata; + register int i; + + /* + * check for matches with all patterns + */ + for (i = 0; i < ncol; i++) + if (d->dfa_set[i]) { + val = ec[i].ec_value.ec_value_val; + len = ec[i].ec_value.ec_value_len; + if (len == 0) + return (0); + if (val[len-1] != '\0') + return (0); + + if (d->flags & PL_NOCASE) { + val = strlower(val); + res = regexec(&d->re_dfa[i], val, 0, 0, 0); + free((void *)val); + } else { + res = regexec(&d->re_dfa[i], val, 0, 0, 0); + } + switch (res) { + case REG_ENOSYS: + return (-1); + case REG_NOMATCH: + return (0); + } + } + + d->nmatch++; + if (d->flags & PL_COUNT) + return (0); + + if (d->flags & PL_OBJECT) { + nis_print_object(ent); + return (0); + } + + for (i = 0; i < ncol; i++) { + if (i > 0) + printf("%c", d->ta_sep); + if (ec[i].ec_value.ec_value_len) { + if ((ec[i].ec_flags & EN_BINARY) && + !(d->flags & PL_BINARY)) + printf(BINARY_STR); + else + printf("%s", ec[i].ec_value.ec_value_val); + } + } + printf("\n"); + + return (0); +} + + +#define F_HEADER 1 + +void +usage() +{ + fprintf(stderr, + "usage: nisgrep [-AMchivo] [-s sep] keypat tablename\n"); + fprintf(stderr, + " nisgrep [-AMchivo] [-s sep] colname=keypat ... tablename\n"); + exit(EXIT_ERROR); +} + +re_error(pattern, code, expr) + char *pattern; + int code; + regex_t *expr; +{ + char buf[80]; + + buf[0] = 0; + regerror(code, expr, buf, sizeof (buf)); + fprintf(stderr, + "can't compile regular expression \"%s\": %s\n", + pattern, buf); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + int st; + u_long allres = 0, master = 0; + unsigned flags = 0; + char *p; + int npat, ncol, i, j; + char **patstr; + char *name; + nis_result *tres, *eres; + char tname[NIS_MAXNAMELEN]; + struct pl_data pld; + int re_flags = REG_EXTENDED|REG_NOSUB; + + /* + * By default, don't print binary data to ttys. + */ + pld.flags = (isatty(1))?0:PL_BINARY; + + pld.ta_sep = '\0'; + while ((c = getopt(argc, argv, "AMchivos:")) != -1) { + switch (c) { + case 'A': + allres = ALL_RESULTS; + break; + case 'M': + master = MASTER_ONLY; + break; + case 'c': + pld.flags |= PL_COUNT; + break; + case 'i': + pld.flags |= PL_NOCASE; + break; + case 'h': + flags |= F_HEADER; + break; + case 'v' : + pld.flags &= ~PL_BINARY; + break; + case 'o' : + pld.flags |= PL_OBJECT; + break; + case 's': + if (strlen(optarg) != 1) { + fprintf(stderr, + "separator must be a single character\n"); + exit(1); + } + pld.ta_sep = *optarg; + break; + default: + usage(); + } + } + + if ((npat = argc - optind - 1) < 1) + usage(); + if ((patstr = (char **)malloc(npat * sizeof (char *))) == 0) { + fprintf(stderr, "No memory!\n"); + exit(EXIT_ERROR); + } + for (i = 0; i < npat; i++) { + if (pld.flags & PL_NOCASE) + patstr[i] = strlower(argv[optind++]); + else + patstr[i] = argv[optind++]; + } + name = argv[optind++]; + + /* + * Get the table object using expand name magic. + */ + tres = nis_lookup(name, master|FOLLOW_LINKS|EXPAND_NAME); + if (tres->status != NIS_SUCCESS) { + nis_perror(tres->status, name); + exit(EXIT_ERROR); + } + + /* + * Construct the name for the table that we found. + */ + sprintf(tname, "%s.", tres->objects.objects_val[0].zo_name); + if (*(tres->objects.objects_val[0].zo_domain) != '.') + strcat(tname, tres->objects.objects_val[0].zo_domain); + + /* + * Make sure it's a table object. + */ + if (tres->objects.objects_val[0].zo_data.zo_type != NIS_TABLE_OBJ) { + fprintf(stderr, "%s is not a table!\n", tname); + exit(EXIT_ERROR); + } + + /* + * Compile the regular expressions. + */ + ncol = TABLE_COLS(tres).ta_cols_len; + + if ((pld.re_dfa = (regex_t *)malloc(ncol * sizeof (regex_t))) == 0) { + fprintf(stderr, "No memory!\n"); + exit(EXIT_ERROR); + } + memset(pld.re_dfa, 0, ncol * sizeof (regex_t)); + + if ((pld.dfa_set = (int *)malloc(ncol * sizeof (int))) == 0) { + fprintf(stderr, "No memory!\n"); + exit(EXIT_ERROR); + } + memset(pld.dfa_set, 0, ncol * sizeof (int)); + + /* XXX pat could contain '=' */ + if ((npat == 1) && (nisname_index(patstr[0], '=') == 0)) { + if ((st = regcomp(&pld.re_dfa[0], patstr[0], re_flags)) != 0) { + re_error(patstr[0], st, &pld.re_dfa[0]); + exit(EXIT_ERROR); + } + pld.dfa_set[0] = 1; + } else { + for (i = 0; i < npat; i++) { + if ((p = nisname_index(patstr[i], '=')) == 0) + usage(); + *(p++) = 0; + for (j = 0; j < ncol; j++) + if (TABLE_COLS(tres).ta_cols_val[j].tc_name && + (strcmp( + TABLE_COLS(tres).ta_cols_val[j].tc_name, + patstr[i]) == 0)) + break; + if (j == ncol) { + fprintf(stderr, "column not found: %s\n", + patstr[i]); + exit(EXIT_ERROR); + } + if ((st = regcomp(&pld.re_dfa[j], p, re_flags)) != 0) { + re_error(p, st, &pld.re_dfa[j]); + exit(EXIT_ERROR); + } + pld.dfa_set[j] = 1; + } + } + + /* + * Use the table's separator character when printing entries. + * Unless one was specified w/ -s + */ + if (pld.ta_sep == '\0') { + pld.ta_sep = tres->objects.objects_val[0].TA_data.ta_sep; + } + + /* + * Print column names + */ + if ((flags & F_HEADER) && !(pld.flags & (PL_COUNT|PL_OBJECT))) { + ncol = TABLE_COLS(tres).ta_cols_len; + c = pld.ta_sep; + printf("# "); + for (i = 0; i < ncol; i++) { + if (i > 0) + printf("%c", c); + printf("%s", + TABLE_COLS(tres).ta_cols_val[i].tc_name); + } + printf("\n"); + } + + /* + * Cat matching entries from the table using a callback function. + */ + pld.nmatch = 0; + eres = nis_list(tname, allres|master, print_line, (void *)&(pld)); + if (eres->status != NIS_CBRESULTS && + eres->status != NIS_NOTFOUND) { + nis_perror(eres->status, "can't list table"); + exit(EXIT_ERROR); + } + if (pld.flags & PL_COUNT) + printf("%d\n", pld.nmatch); + + if (pld.nmatch) + exit(EXIT_MATCH); + else + exit(EXIT_NOMATCH); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisgrpadm.c b/usr/src/cmd/rpcsvc/nis/utils/nisgrpadm.c new file mode 100644 index 0000000000..f64bfe3b95 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisgrpadm.c @@ -0,0 +1,442 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisgrpadm.c + * + * Copyright (c) 1988-1992 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisgrpadm.c + * + * This program allows the system administrator to create and administer + * groups within the NIS+ namespace. + * + */ + +#include <stdio.h> +#include <string.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> + +extern int optind, opterr; +extern char *optarg; +extern nis_object nis_default_obj; + +extern nis_name __nis_map_group_r(); +extern bool_t nis_verifycred(); + +enum op_types {NONE, DELETE, CREATE, ADD, REMOVE, LIST, TEST}; + +static u_long master = 0; + +bool_t +nis_verifydomain(n) + nis_name n; +{ + nis_result *res; + int err; + char dname[NIS_MAXNAMELEN]; + + sprintf(dname, "cred.org_dir.%s", n); + res = nis_lookup(dname, master); + err = (res->status == NIS_SUCCESS); + nis_freeresult(res); + return (err); +} + + + +/* + * temp_nis_verifygroup(group) + * + * Verify the existence of the named group. This is a duplicate of + * nis_verifygroup API call. + * This duplication was necessitated by the fact that nis_verifygroup does not + * accept a 'flags' argument to force it to go to the master server. + * Refer to bugid 1092089 and rfe 1102245. + * + */ +nis_error +temp_nis_verifygroup(group) + nis_name group; /* NIS group name */ +{ + nis_name grpname; + nis_result *res; + nis_error result; + char namebuf[NIS_MAXNAMELEN]; + + grpname = __nis_map_group_r(group, namebuf, sizeof (namebuf)); + res = nis_lookup(grpname, master | FOLLOW_LINKS); + if ((res->status == NIS_SUCCESS) || (res->status == NIS_S_SUCCESS)) { + if (__type_of(res->objects.objects_val) == NIS_GROUP_OBJ) + result = NIS_SUCCESS; + else + result = NIS_BADOBJECT; + } else + result = res->status; + nis_freeresult(res); + return (result); +} + + + + +bool_t +verify_principal(name) + nis_name name; +{ + u_long flags; + + if (name[0] == '-') + name++; + if (name[0] == '@') + return (temp_nis_verifygroup(&name[1]) == NIS_SUCCESS); + if (name[0] == '*') { + if (name[1] == '.') + return (nis_verifydomain(&name[2])); + else + return (FALSE); + } + /* + * Only if a random NIS+ server does not know about this, do we + * force a connection to the Master server to avoid overloading it. + */ + flags = (u_long)(USE_DGRAM | FOLLOW_LINKS | FOLLOW_PATH); + return (nis_verifycred(name, flags) ? TRUE : + nis_verifycred(name, (u_long)(flags | MASTER_ONLY))); +} + +void +usage() +{ + fprintf(stderr, + "usage: nisgrpadm -a | -r | -t [-s] group princpal ...\n"); + fprintf(stderr, + " nisgrpadm -d | -l [-M] [-s] group\n"); + fprintf(stderr, + " nisgrpadm -c [ -D defaults] [-M] [-s] group\n"); + exit(1); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + char *defstr = 0; + enum op_types op = NONE; + int silent = 0; + int c; + nis_error s, pstatus = NIS_SUCCESS, ostatus = NIS_SUCCESS; + char *grpname = NULL, *princp = NULL; + nis_name *grplist, *plist = 0, *p; + char buf[NIS_MAXNAMELEN], *grponame; + nis_result *gres; + int grpnm, found, i; + nis_name *grpml; + + while ((c = getopt(argc, argv, "artcdlsMD:")) != -1) { + switch (c) { + case 'D': + defstr = optarg; + break; + case 'M': + master = MASTER_ONLY; + break; + case 'd': + if (op != NONE) { + fprintf(stderr, + "c, d, a, r, t, and l options are mutually exclusive.\n"); + usage(); + } + op = DELETE; + break; + case 'c': + if (op != NONE) { + fprintf(stderr, + "c, d, a, r, t, and l options are mutually exclusive.\n"); + usage(); + } + op = CREATE; + break; + case 'a': + if (op != NONE) { + fprintf(stderr, + "c, d, a, r, t, and l options are mutually exclusive.\n"); + usage(); + } + op = ADD; + break; + case 'r': + if (op != NONE) { + fprintf(stderr, + "c, d, a, r, t, and l options are mutually exclusive.\n"); + usage(); + } + op = REMOVE; + break; + case 't': + if (op != NONE) { + fprintf(stderr, + "c, d, a, r, t, and l options are mutually exclusive.\n"); + usage(); + } + op = TEST; + break; + case 'l': + if (op != NONE) { + fprintf(stderr, + "c, d, a, r, t, and l options are mutually exclusive.\n"); + usage(); + } + op = LIST; + break; + case 's': + silent = 1; + break; + default: + usage(); + } + } + + if (op == NONE) + usage(); + + if (optind == argc) { + fprintf(stderr, "Missing group name.\n"); + usage(); + } + grpname = argv[optind++]; + + if (!nis_defaults_init(defstr)) + exit(1); + if ((op == CREATE) || (op == DELETE)) { + if (grpname[strlen(grpname)-1] != '.') { + fprintf(stderr, + "Group name must be fully qualified.\n"); + exit(NIS_BADNAME); + } + } else { + /* + * Get the group name using psuedo expand name magic + */ + if ((grplist = nis_getnames(grpname)) == 0) { + nis_perror(NIS_NOMEMORY, "nisgrpadm"); + exit(NIS_NOMEMORY); + } + for (p = grplist; *p; p++) + if (temp_nis_verifygroup(*p) == NIS_SUCCESS) + break; + if (*p == 0) { + if (!silent) + fprintf(stderr, + "Group \"%s\" not found.\n", + grpname); + exit(NIS_NOTFOUND); + } + grpname = *p; + } + + switch (op) { + case DELETE: + s = nis_destroygroup(grpname); + if (!silent) { + if (s == NIS_SUCCESS) + printf("Group \"%s\" destroyed.\n", + grpname); + else { + fprintf(stderr, + "Unable to destroy group \"%s\": %s\n", + grpname, + nis_sperrno(s)); + } + } + exit(s); + + case CREATE: + s = __nis_creategroup_obj(grpname, 0, &nis_default_obj); + if (!silent) { + if (s == NIS_SUCCESS) + printf("Group \"%s\" created.\n", + grpname); + else { + fprintf(stderr, + "Unable to create group \"%s\": %s\n", + grpname, + nis_sperrno(s)); + } + } + exit(s); + + case LIST: + if (!silent) + nis_print_group_entry(grpname); + exit(NIS_SUCCESS); + } + + if (optind == argc) { + fprintf(stderr, "Missing principal name(s).\n"); + usage(); + } + + /* + * Get the group object so we can do prefix matching on partial + * principal names. temp_nis_verifygroup() has already read and + * verified the group (too bad we have to look it up again) so + * this shouldn't fail. + */ + if (op == REMOVE) { + grponame = __nis_map_group_r(grpname, buf, sizeof (buf)); + gres = nis_lookup(grponame, master | FOLLOW_LINKS); + if (gres->status != NIS_SUCCESS) { + if (!silent) + nis_perror(gres->status, + "Can't read group object"); + exit(gres->status); + } + grpnm = NIS_RES_OBJECT(gres)->GR_data.gr_members.gr_members_len; + grpml = NIS_RES_OBJECT(gres)->GR_data.gr_members.gr_members_val; + } + + while (optind < argc) { + + princp = argv[optind++]; + + if (op == REMOVE) { + /* + * If the principal name isn't fully qualified, + * look for a unique prefix match in the group + * membership list. + */ + if (princp[strlen(princp)-1] != '.') { + for (found = -1, i = 0; i < grpnm; i++) { + if (strncasecmp(princp, + grpml[i], + strlen(princp)) == 0) { + if (found >= 0 && !silent) { + fprintf(stderr, + "Principal \"%s\" not unique, please use full name.\n", + princp); + found = -2; + break; + } else + found = i; + } + } + if (found < 0) { + pstatus = NIS_NOTFOUND; + if (found == -1 && !silent) + fprintf(stderr, + "Principal \"%s\" not found in group.\n", + princp); + continue; + } + princp = grpml[found]; + } + } else { + /* + * Get the principal name using psuedo expand name + * magic + */ + if (plist) + nis_freenames(plist); + if ((plist = nis_getnames(princp)) == 0) { + nis_perror(NIS_NOMEMORY, "nisgrpadm"); + exit(NIS_NOMEMORY); + } + for (p = plist; *p; p++) + if (verify_principal(*p)) + break; + if (*p == 0) { + pstatus = NIS_NOTFOUND; + if (!silent) + fprintf(stderr, + "Principal \"%s\" not found.\n", + princp); + continue; + } + princp = *p; + } + + switch (op) { + case ADD: + s = nis_addmember(princp, grpname); + if (ostatus == NIS_SUCCESS) + ostatus = s; + if (!silent) { + if (s == NIS_SUCCESS) + printf( + "Added \"%s\" to group \"%s\".\n", + princp, grpname); + else { + fprintf(stderr, + "Unable to add \"%s\" to group \"%s\": %s\n", + princp, grpname, + nis_sperrno(s)); + } + } + break; + + case REMOVE: + s = nis_removemember(princp, grpname); + if (ostatus == NIS_SUCCESS) + ostatus = s; + if (!silent) { + if (s == NIS_SUCCESS) + printf( + "Removed \"%s\" from group \"%s\".\n", + princp, grpname); + else { + fprintf(stderr, + "Unable to remove \"%s\" from group \"%s\": %s\n", + princp, grpname, + nis_sperrno(s)); + } + } + break; + + case TEST: + s = nis_ismember(princp, grpname); + if (!s && ostatus == NIS_SUCCESS) + ostatus = NIS_NOTFOUND; + if (!silent) { + if (s) + printf( + "\"%s\" is a member of group \"%s\".\n", + princp, grpname); + else + printf( + "\"%s\" is not a member of group \"%s\".\n", + princp, grpname); + } + break; + } + } + + if (ostatus == NIS_SUCCESS) + exit(pstatus); + else + exit(ostatus); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisln.c b/usr/src/cmd/rpcsvc/nis/utils/nisln.c new file mode 100644 index 0000000000..718a91af70 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisln.c @@ -0,0 +1,181 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisln.c + * + * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisln.c + * + * nis+ link utility + */ + +#include <stdio.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <stdlib.h> +#include <strings.h> + +extern nis_object nis_default_obj; + +extern char *nisname_index(); +extern int nisname_split(char *, char *, char *, int); + + +static void +usage() +{ + fprintf(stderr, "usage: nisln [-D defaults] [-L] name linkname\n"); + exit(1); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + char *defstr = 0; + ulong_t flinks = 0; + char *name, *lname; + nis_result *res, *ares; + nis_object *obj, lobj; + char oname[NIS_MAXNAMELEN], fbase[NIS_MAXNAMELEN]; + char srch[NIS_MAXNAMELEN], base[NIS_MAXNAMELEN]; + nis_error s; + ib_request ibr; + int bad_name; + int i, j; + + while ((c = getopt(argc, argv, "D:L")) != -1) { + switch (c) { + case 'D': + defstr = optarg; + break; + case 'L': + flinks = FOLLOW_LINKS; + break; + default: + usage(); + } + } + + if (argc - optind != 2) + usage(); + + name = argv[optind]; + lname = argv[optind+1]; + + /* + * Get the object to link to. + */ + if (nisname_split(name, base, srch, sizeof (base)) || base[0] == 0) { + nis_perror(NIS_BADNAME, name); + exit(1); + } + res = nis_lookup(base, flinks|MASTER_ONLY|EXPAND_NAME); + if (res->status != NIS_SUCCESS) { + nis_perror(res->status, base); + exit(1); + } + obj = res->objects.objects_val; + + bad_name = (snprintf(fbase, sizeof (fbase), "%s.", obj->zo_name) + >= sizeof (fbase)); + if (!bad_name && *(obj->zo_domain) != '.') + bad_name = (strlcat(fbase, obj->zo_domain, sizeof (fbase)) >= + sizeof (fbase)); + + if (bad_name) { + nis_perror(NIS_BADNAME, fbase); + exit(1); + } + + if (obj->zo_data.zo_type == NIS_DIRECTORY_OBJ) { + fprintf(stderr, "\"%s\" is a directory!\n", fbase); + exit(1); + } + + if (srch[0]) { + if (obj->zo_data.zo_type != NIS_TABLE_OBJ) { + fprintf(stderr, "\"%s\" is not a table!\n", fbase); + exit(1); + } + + bad_name = (snprintf(oname, sizeof (oname), "%s,%s", + srch, fbase) >= sizeof (oname)); + + if (bad_name) { + nis_perror(NIS_BADNAME, oname); + exit(1); + } + + s = nis_get_request(oname, 0, 0, &ibr); + if (s != NIS_SUCCESS) { + nis_perror(s, oname); + exit(1); + } + + for (i = 0; i < ibr.ibr_srch.ibr_srch_len; i++) { + for (j = 0; j < obj->TA_data.ta_cols.ta_cols_len; j++) + if + (strcmp(ibr.ibr_srch.ibr_srch_val[i].zattr_ndx, + obj->TA_data.ta_cols.ta_cols_val[j].tc_name) == 0) + break; + if (j == obj->TA_data.ta_cols.ta_cols_len) { + nis_perror(NIS_BADATTRIBUTE, name); + exit(1); + } + } + } else { + memset(&ibr, 0, sizeof (ibr)); + ibr.ibr_name = fbase; + } + + if (!nis_defaults_init(defstr)) + exit(1); + + /* + * Construct link object. + */ + lobj = nis_default_obj; + lobj.zo_data.zo_type = NIS_LINK_OBJ; + if (srch[0]) + lobj.LI_data.li_rtype = NIS_ENTRY_OBJ; + else + lobj.LI_data.li_rtype = obj->zo_data.zo_type; + lobj.LI_data.li_attrs.li_attrs_len = ibr.ibr_srch.ibr_srch_len; + lobj.LI_data.li_attrs.li_attrs_val = ibr.ibr_srch.ibr_srch_val; + lobj.LI_data.li_name = ibr.ibr_name; + + ares = nis_add(lname, &lobj); + if (ares->status != NIS_SUCCESS) { + nis_perror(ares->status, "can't add link"); + exit(1); + } + + exit(0); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisls.c b/usr/src/cmd/rpcsvc/nis/utils/nisls.c new file mode 100644 index 0000000000..870a840478 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisls.c @@ -0,0 +1,269 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisls.c + * + * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisls.c + * + * Simple "ls" utility for nis directories. + */ + +#include <stdio.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <stdlib.h> +#include <strings.h> + +extern nis_result * __nis_list_localcb(nis_name, uint_t, + int (*)(nis_name, nis_object *, void *), void *); + + +static char **nl = 0; +static int nlsize = 1024, nllen = 0; + +static void +nlpush(name) + char *name; +{ + if (nl == 0) + nl = (char **)malloc(nlsize * sizeof (char *)); + if (nllen == nlsize) { + nlsize += 1024; + nl = (char **)realloc(nl, nlsize * sizeof (char *)); + } + nl[nllen++] = name; +} + +static char * +nlpop() +{ + if (nllen == 0) + return (0); + return (nl[--nllen]); +} + + +#define F_LONG 1 +#define F_RECURSE 2 +#define F_MTIME 4 +#define F_GROUP 8 +#define F_DIR 16 + +static int flags = 0; + +static int +ls_obj(tab, ent, udata) + char *tab; + nis_object *ent; + void *udata; +{ + zotypes et; + char fname[NIS_MAXNAMELEN]; + char *tms; + + if (udata) + et = *(long *)(udata); + else + et = ntohl(*(long *)(ent-> + EN_data.en_cols.en_cols_val[0].ec_value.ec_value_val)); + + (void) snprintf(fname, sizeof (fname), "%s.", ent->zo_name); + if (*(ent->zo_domain) != '.') + (void) strlcat(fname, ent->zo_domain, sizeof (fname)); + + if ((flags & F_RECURSE) && (et == NIS_DIRECTORY_OBJ) && + !(flags & F_DIR)) + nlpush(strdup(fname)); + + if (flags & F_LONG) { + switch (et) { + case NIS_BOGUS_OBJ: + printf("* "); + break; + case NIS_DIRECTORY_OBJ: + printf("D "); + break; + case NIS_GROUP_OBJ: + printf("G "); + break; + case NIS_TABLE_OBJ: + printf("T "); + break; + case NIS_ENTRY_OBJ: + printf("E "); + break; + case NIS_LINK_OBJ: + printf("L "); + break; + case NIS_PRIVATE_OBJ: + printf("P "); + break; + default: + printf("%d ", et); + break; + } + nis_print_rights(ent->zo_access); + if (flags & F_GROUP) + printf(" %s ", ent->zo_group); + else + printf(" %s ", ent->zo_owner); + if (flags & F_MTIME) + tms = ctime((time_t *)&(ent->zo_oid.mtime)); + else + tms = ctime((time_t *)&(ent->zo_oid.ctime)); + *(tms+strlen(tms)-1) = '\0'; + printf("%s ", tms); + } + + printf("%s\n", (tab)?ent->zo_name:fname); + + return (0); +} + + +static void +usage() +{ + printf("usage: nisls [-LMlmgdR] [name ...]\n"); + exit(1); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + ulong_t flinks = 0, master = 0; + nis_name *result; + char *name; + nis_result *res, *lres; + char fname[NIS_MAXNAMELEN]; + int error = 0; + + while ((c = getopt(argc, argv, "LMlmgdR")) != -1) + switch (c) { + case 'L': + flinks = FOLLOW_LINKS; + break; + case 'M': + master = MASTER_ONLY; + break; + case 'l': + flags |= F_LONG; + break; + case 'm': + flags |= F_MTIME; + break; + case 'g': + flags |= F_GROUP; + break; + case 'd': + flags |= F_DIR; + break; + case 'R': + flags |= F_RECURSE; + break; + default: + usage(); + } + + /* + * push all of the names to ls onto a stack (in reverse order) + */ + if (argc - optind == 0) { + result = nis_getnames("foo"); + nlpush(nis_domain_of(*result)); + } else while (argc - optind > 0) + nlpush(argv[--argc]); + + /* + * keep popping names off the stack and ls'ing them. if we're + * recursive, then subdirectories will get pushed on in ls_obj. + */ + while (name = nlpop()) { + res = nis_lookup(name, flinks|master|EXPAND_NAME); + /* + * Hack to get around non replicated root object bug. + */ + if (res->status == NIS_NOT_ME) { + printf("%s:\n", name); + + lres = __nis_list_localcb(name, EXPAND_NAME|master, + ls_obj, 0); + if ((lres->status != NIS_CBRESULTS) && + (lres->status != NIS_NOTFOUND)) { + nis_perror(lres->status, + "can't list directory"); + error = 1; + } + nis_freeresult(lres); + + if (nllen) + printf("\n"); + goto loop; + + } else if (res->status != NIS_SUCCESS) { + nis_perror(res->status, name); + error = 1; + goto loop; + } + + if ((res->objects.objects_val[0].zo_data.zo_type != + NIS_DIRECTORY_OBJ) || (flags & F_DIR)) { + ls_obj(0, res->objects.objects_val, + &(res->objects.objects_val[0].zo_data.zo_type)); + goto loop; + } + + (void) snprintf(fname, sizeof (fname), "%s.", + res->objects.objects_val[0].zo_name); + if (*(res->objects.objects_val[0].zo_domain) != '.') + (void) strlcat(fname, + res->objects.objects_val[0].zo_domain, + sizeof (fname)); + + printf("%s:\n", fname); + + lres = __nis_list_localcb(fname, master, ls_obj, 0); + if ((lres->status != NIS_CBRESULTS) && + (lres->status != NIS_NOTFOUND)) { + nis_perror(lres->status, "can't list directory"); + error = 1; + } + nis_freeresult(lres); + + if (nllen) + printf("\n"); + + loop: + nis_freeresult(res); + } + + exit(error); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nismatch.c b/usr/src/cmd/rpcsvc/nis/utils/nismatch.c new file mode 100644 index 0000000000..56a0327184 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nismatch.c @@ -0,0 +1,334 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nismatch.c + * + * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nismatch.c + * + * nis+ table match utility + */ + +#include <stdio.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <strings.h> +#include <stdlib.h> + +extern char *nisname_index(); +extern nis_result * __nis_list_localcb(nis_name, uint_t, + int (*)(nis_name, nis_object *, void *), void *); +extern int nisname_split(char *, char *, char *, int); + + +#define BINARY_STR "*BINARY*" + + +struct pl_data { + unsigned flags; + char ta_sep; + ulong_t nmatch; +}; + +#define PL_BINARY 1 +#define PL_COUNT 2 +#define PL_OBJECT 4 + +int +print_line(tab, ent, udata) + char *tab; + nis_object *ent; + void *udata; +{ + register entry_col *ec = ent->EN_data.en_cols.en_cols_val; + register int ncol = ent->EN_data.en_cols.en_cols_len; + register struct pl_data *d = (struct pl_data *)udata; + register int i; + int len; + + d->nmatch++; + if (d->flags & PL_COUNT) + return (0); + + if (d->flags & PL_OBJECT) { + nis_print_object(ent); + return (0); + } + + for (i = 0; i < ncol; i++) { + if (i > 0) + printf("%c", d->ta_sep); + len = ec[i].ec_value.ec_value_len; + if (len != 0) { + if (ec[i].ec_flags & EN_BINARY) { + if (d->flags & PL_BINARY) { + fwrite(ec[i].ec_value.ec_value_val, + 1, len, stdout); + } else { + printf(BINARY_STR); + } + } else { + printf("%s", ec[i].ec_value.ec_value_val); + } + } + } + printf("\n"); + + return (0); +} + + +#define EXIT_MATCH 0 +#define EXIT_NOMATCH 1 +#define EXIT_ERROR 2 + +#define F_HEADER 1 + +void +usage() +{ + fprintf(stderr, + "usage: nismatch [-PAMchvo] [-s sep] key tablename\n"); + fprintf(stderr, + " nismatch [-PAMchvo] [-s sep] colname=key ... tablename\n"); + fprintf(stderr, + " nismatch [-PAMchvo] [-s sep] indexedname\n"); + exit(EXIT_ERROR); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + ulong_t fpath = 0, allres = 0, master = 0; + unsigned flags = 0; + char *p, *name; + nis_result *tres, *eres; + nis_object *tobj; + char index[NIS_MAXNAMELEN], + spred[NIS_MAXNAMELEN], + tname[NIS_MAXNAMELEN], + sname[NIS_MAXNAMELEN]; + struct pl_data pld; + int ncol, i; + table_col *tcol; + + /* + * By default, don't print binary data to ttys. + */ + pld.flags = (isatty(1))?0:PL_BINARY; + + pld.ta_sep = '\0'; + while ((c = getopt(argc, argv, "PAMchvos:")) != -1) { + switch (c) { + case 'P': + fpath = FOLLOW_PATH; + break; + case 'A': + allres = ALL_RESULTS; + break; + case 'M': + master = MASTER_ONLY; + break; + case 'c': + pld.flags |= PL_COUNT; + break; + case 'h': + flags |= F_HEADER; + break; + case 'v' : + pld.flags |= PL_BINARY; + break; + case 'o' : + pld.flags |= PL_OBJECT; + break; + case 's': + if (strlen(optarg) != 1) { + fprintf(stderr, + "separator must be a single character\n"); + exit(1); + } + pld.ta_sep = *optarg; + break; + default: + usage(); + } + } + + if (argc - optind < 1) + usage(); + + /* + * build up the search criteria in sname, if a token is + * just "hanging" it is assumed to be a value for the + * first column. In that case keep it in spred until we + * can find the column name from the lookup. + */ + sname[0] = 0; + spred[0] = 0; + while (optind < (argc - 1)) { + if (sname[0] == 0) + strcat(sname, "["); + p = argv[optind++]; + if (strchr(p, '=')) { + if ((strlcat(sname, p, sizeof (sname)) >= + sizeof (sname)) || + (strlcat(sname, ",", sizeof (sname)) >= + sizeof (sname))) { + (void) fprintf(stderr, + "search pattern too big\n"); + exit(EXIT_ERROR); + } + } else { + /* if no "=" sign assume it it implicit */ + if (spred[0]) { + fprintf(stderr, + "only one implicit column name allowed.\n"); + usage(argv[0]); + } + if (strlcat(spred, p, sizeof (spred)) >= + sizeof (spred)) { + nis_perror(NIS_BADNAME, p); + exit(EXIT_ERROR); + } + } + } + + /* This should be the last argument in the list */ + name = argv[optind]; + + /* Split the name into the indexed part and the table part */ + if (nisname_split(name, tname, index, sizeof (tname))) { + nis_perror(NIS_BADNAME, name); + exit(EXIT_ERROR); + } + if (index[0] && sname[0]) { + fprintf(stderr, + "use either an indexed name or the col=key form.\n"); + usage(); + } + + /* + * Get the table object using expand name magic. + */ + tres = nis_lookup(tname, master|FOLLOW_LINKS|EXPAND_NAME); + if (tres->status != NIS_SUCCESS) { + nis_perror(tres->status, tname); + exit(EXIT_ERROR); + } + + /* + * Make sure it's a table object. + */ + tobj = tres->objects.objects_val; + (void) snprintf(tname, sizeof (tname), "%s.%s", tobj->zo_name, + tobj->zo_domain); + if (__type_of(tobj) != NIS_TABLE_OBJ) { + fprintf(stderr, "\"%s\" is not a table!\n", tname); + exit(EXIT_ERROR); + } + ncol = tobj->TA_data.ta_cols.ta_cols_len; + tcol = tobj->TA_data.ta_cols.ta_cols_val; + + /* + * Finish the construction of the search criteria if necessary. + * At this point we've got three candidates for the search + * criteria : + * index - contains indexed name portion on table + * spred - contains "hanging" value, implicitly for the + * first column. + * sname - name=value pairs that were specified on the + * command line. + * tname - the name of our table. + * NB: If sname || spred have something in them then index is available + * otherwise we use what is in index. + */ + if (spred[0]) { + for (i = 0; i < ncol; i++) + if (tcol[i].tc_flags & TA_SEARCHABLE) + break; + if (i == ncol) { + fprintf(stderr, "%s: No searchable columns!\n", tname); + exit(1); + } + (void) snprintf(index, sizeof (index), "%s=\"%s\"]", + tcol[i].tc_name, spred); + (void) strlcat(sname, index, sizeof (sname)); + (void) strlcpy(index, sname, sizeof (index)); + } else if (sname[0]) { + sname[strlen(sname) - 1] = 0; + (void) snprintf(index, sizeof (index), "%s]", sname); + } + /* + * Construct the search criteria for the table that we found. + */ + (void) strlcat(index, ",", sizeof (index)); + (void) strlcat(index, tname, sizeof (index)); + + /* + * Use the table's separator character when printing entries. + * Unless one was specified w/ -s + */ + if (pld.ta_sep == '\0') { + pld.ta_sep = tobj->TA_data.ta_sep; + } + + /* + * Print column names + */ + if ((flags & F_HEADER) && !(pld.flags & (PL_COUNT|PL_OBJECT))) { + c = pld.ta_sep; + printf("# "); + for (i = 0; i < ncol; i++) { + if (i > 0) + printf("%c", c); + printf("%s", tcol[i].tc_name); + } + printf("\n"); + } + + /* + * Cat matching entries from the table using a callback function. + */ + pld.nmatch = 0; + eres = __nis_list_localcb(index, fpath|allres|master, + print_line, (void *)&(pld)); + if ((eres->status != NIS_CBRESULTS) && + (eres->status != NIS_NOTFOUND)) { + nis_perror(eres->status, "can't list table"); + exit(EXIT_ERROR); + } + if (pld.flags & PL_COUNT) + printf("%d\n", pld.nmatch); + + if (pld.nmatch) + exit(EXIT_MATCH); + else + exit(EXIT_NOMATCH); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nismkdir.c b/usr/src/cmd/rpcsvc/nis/utils/nismkdir.c new file mode 100644 index 0000000000..f7dcb5e8b7 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nismkdir.c @@ -0,0 +1,508 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1988-1995,2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nismkdir.c + * + * NIS+ directory and server create utility + * + */ + +#include <stdio.h> +#include <string.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <netdb.h> +#include <netdir.h> +#include <netconfig.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <rpc/key_prot.h> +#include <rpcsvc/nis_dhext.h> + +#define MAX_REPLICA 64 +char *defstr = 0; +int errcode = 0; + +extern int optind; +extern char *optarg; + +extern nis_object nis_default_obj; + +void +make_directory(char *name) +{ + nis_result *res, *ares; + nis_object *obj; + char *p, lname[NIS_MAXNAMELEN], *dname; + nis_error s; + + /* + * Break name into leaf and domain components. + */ + if ((p = nis_leaf_of(name)) == 0) { + nis_perror(NIS_BADNAME, name); + exit(1); + } + strcpy(lname, p); + dname = nis_domain_of(name); + + /* + * Get the parent directory object. + */ + res = nis_lookup(dname, MASTER_ONLY); + if (res->status != NIS_SUCCESS) { + nis_perror(res->status, dname); + exit(1); + } + + if (!nis_defaults_init(defstr)) + exit(1); + + /* + * Turn the parent directory object into the + * sub-directory object. If we cared about memory + * leaks, we would save pointers to the fields that + * are being overwritten, and restore them and free + * the parent object when we are done. + */ + obj = &(NIS_RES_OBJECT(res)[0]); + if (obj->zo_data.zo_type != NIS_DIRECTORY_OBJ) { + fprintf(stderr, "\"%s\": not a directory\n", dname); + exit(1); + } + obj->zo_owner = nis_default_obj.zo_owner; + obj->zo_group = nis_default_obj.zo_group; + obj->zo_access = nis_default_obj.zo_access; + obj->zo_ttl = nis_default_obj.zo_ttl; + obj->DI_data.do_name = name; + + /* + * Make the directory and add it to the namespace. + */ + ares = nis_add(name, obj); + if (ares->status != NIS_SUCCESS) { + nis_perror(ares->status, "can't add directory"); + exit(1); + } else { + s = nis_mkdir(name, + &(obj->DI_data.do_servers.do_servers_val[0])); + if (s != NIS_SUCCESS) { + (void) nis_remove(name, 0); + nis_perror(s, "can't make directory"); + exit(1); + } + } + nis_freeresult(ares); + /* + * do not free res because it contains pointers to structures + * it did not allocate. + */ +} + + +static int +verify_server(nis_name host, /* new server hostname */ + nis_name dir_to_serve, /* directory to serve */ + int to_be_master) /* will it be a master? */ +{ + + nis_error status; /* status of nis_stats() */ + nis_name dom_of_host; /* domain name of server host */ + nis_object *obj; /* generic NIS+ object variable */ + nis_result *hres, *sres; /* status of nis_lookup() */ + nis_server *servers; /* servers for host's domain */ + nis_tag tags, *tagres; /* NIS+ tags and tags result */ + + /* Try to lookup directory to serve... */ + sres = nis_lookup(dir_to_serve, 0); + + /* If it doesn't exist, we can't have a (replica) server for it! */ + if ((sres->status != NIS_SUCCESS) && !to_be_master) { + fprintf(stderr, "\"%s\": %s\n", + dir_to_serve, nis_sperrno(sres->status)); + return (0); + } + + /* It exists, but is it a directory object? */ + if (sres->status == NIS_SUCCESS) { + obj = sres->objects.objects_val; + if (__type_of(obj) != NIS_DIRECTORY_OBJ) { + fprintf(stderr, "\"%s\": not a directory.\n", + dir_to_serve); + return (0); + } + } + + nis_freeresult(sres); + + /* Try to lookup domain of host who wants to be a server... */ + dom_of_host = strdup(nis_domain_of(host)); + hres = nis_lookup(dom_of_host, 0); + + /* Can't get host domain, servers busy or domain name bogus... */ + if (hres->status != NIS_SUCCESS) { + fprintf(stderr, "\"%s\": %s\n", dom_of_host, + nis_sperrno(hres->status)); + return (0); + } + + /* It exists, but is it a directory object? */ + obj = hres->objects.objects_val; + if (__type_of(obj) != NIS_DIRECTORY_OBJ) { + fprintf(stderr, "\"%s\": not a directory.\n", dom_of_host); + return (0); + } + + servers = obj->DI_data.do_servers.do_servers_val; + + switch ((int)(nis_dir_cmp(dir_to_serve, dom_of_host))) { + + case SAME_NAME: + + tags.tag_type = TAG_ROOTSERVER; + tags.tag_val = "root server"; + + if (!servers[0].name) { + fprintf(stderr, "nismkdir: invalid directory object\n"); + return (0); + } + status = nis_stats(&servers[0], &tags, 1, &tagres); + if (status != NIS_SUCCESS) { + fprintf(stderr, + "nismkdir: can't contact root server for \"%s\"" + ": %s\n", servers[0].name, dom_of_host, + nis_sperrno(status)); + return (0); + } + nis_freeresult(hres); + + /* If the server-to-be is in the root domain, it can serve. */ + if (strcmp(tagres[0].tag_val, "ON") == 0) { + return (1); + } else { + fprintf(stderr, + "nismkdir: host '%s' must be in a domain above '%s'\n", + host, dir_to_serve); + return (0); + } + case LOWER_NAME: + /* directory is below domain of host in question... okay */ + return (1); + + case HIGHER_NAME: + case NOT_SEQUENTIAL: + fprintf(stderr, + "nismkdir: host '%s' must be in a domain above '%s'\n", + host, dir_to_serve); + return (0); + + case BAD_NAME: + default: + fprintf(stderr, + "nismkdir: cannot parse either or both of '%s' and '%s'\n", + dir_to_serve, dom_of_host); + return (0); + } +} + + +void +make_directory_master(char *name, char *host) +{ + nis_result *res, *ares, *mres; + nis_object *obj, dobj; + nis_error s; + nis_server *serv, sserv, *newserv; + int nserv, i; + + /* + * Does the directory already exist? + */ + res = nis_lookup(name, MASTER_ONLY); + if (res->status == NIS_SUCCESS) { + obj = &(NIS_RES_OBJECT(res)[0]); + if (obj->zo_data.zo_type != NIS_DIRECTORY_OBJ) { + fprintf(stderr, "\"%s\": not a directory\n", name); + exit(1); + } + nserv = obj->DI_data.do_servers.do_servers_len; + + newserv = __nis_host2nis_server_g(host, TRUE, TRUE, &errcode); + if (newserv == NULL) { + nis_perror(errcode, host); + exit(1); + } + + if (nis_dir_cmp(obj->DI_data.do_servers.do_servers_val[0].name, + newserv->name) == SAME_NAME) { + fprintf(stderr, + "\"%s\" is already master for \"%s\"!\n", + host, name); + exit(1); + } + + /* + * Verify server able to serve intended domain. + */ + if (!verify_server(newserv->name, name, 1)) + exit(1); + + /* + * Add master to list of servers and demote current + * master to the role of a slave. + */ + for (i = 1; i < nserv; i++) + if (nis_dir_cmp( + obj->DI_data.do_servers.do_servers_val[i].name, + newserv->name) == SAME_NAME) + break; + if (i < nserv) { + sserv = + obj->DI_data.do_servers.do_servers_val[i]; + obj->DI_data.do_servers.do_servers_val[i] = + obj->DI_data.do_servers.do_servers_val[0]; + obj->DI_data.do_servers.do_servers_val[0] = + sserv; + } else { + if ((serv = (nis_server*)malloc( + (nserv + 1)*sizeof (nis_server))) == + 0) { + nis_perror(NIS_NOMEMORY, + "can't add master"); + exit(1); + } + + for (i = 0; i < nserv; i++) + serv[i+1] = + obj->DI_data.do_servers.do_servers_val[i]; + serv[0] = *newserv; + obj->DI_data.do_servers.do_servers_len++; + obj->DI_data.do_servers.do_servers_val = serv; + } + + mres = nis_modify(name, obj); + if (mres->status != NIS_SUCCESS) { + nis_perror(mres->status, "can't add master"); + exit(1); + } + nis_freeresult(mres); + /* + * do not free res because it contains pointers to + * structures it did not allocate. + */ + } else { + if (!nis_defaults_init(defstr)) + exit(1); + + /* + * Construct the directory object. + */ + dobj = nis_default_obj; + dobj.zo_data.zo_type = NIS_DIRECTORY_OBJ; + dobj.DI_data.do_name = name; + dobj.DI_data.do_type = NIS; + dobj.DI_data.do_ttl = nis_default_obj.zo_ttl; + dobj.DI_data.do_servers.do_servers_len = 1; + dobj.DI_data.do_servers.do_servers_val = + __nis_host2nis_server_g(host, TRUE, TRUE, &errcode); + if (dobj.DI_data.do_servers.do_servers_val == NULL) { + nis_perror(errcode, host); + exit(1); + } + dobj.DI_data.do_armask.do_armask_len = 0; + dobj.DI_data.do_armask.do_armask_val = 0; + + /* + * Make the directory and add it to the namespace. + */ + ares = nis_add(name, &dobj); + if (ares->status != NIS_SUCCESS) { + nis_perror(ares->status, "can't add directory"); + exit(1); + } else { + s = nis_mkdir(name, + &(dobj.DI_data.do_servers.do_servers_val[0])); + if (s != NIS_SUCCESS) { + (void) nis_remove(name, 0); + nis_perror(s, "can't make directory"); + exit(1); + } + } + nis_freeresult(ares); + } +} + + +void +make_directory_replica(char *name, char *host) +{ + nis_result *res, *mres; + nis_object *obj; + nis_server *serv, *newserv; + int nserv, i; + + /* + * Get the directory object. + */ + res = nis_lookup(name, MASTER_ONLY); + if (res->status != NIS_SUCCESS) { + nis_perror(res->status, name); + exit(1); + } + obj = &(NIS_RES_OBJECT(res)[0]); + if (obj->zo_data.zo_type != NIS_DIRECTORY_OBJ) { + fprintf(stderr, "\"%s\": not a directory\n", name); + exit(1); + } + nserv = obj->DI_data.do_servers.do_servers_len; + + newserv = __nis_host2nis_server_g(host, TRUE, TRUE, &errcode); + if (newserv == NULL) { + nis_perror(errcode, host); + exit(1); + } + + for (i = 0; i < nserv; i++) + if (nis_dir_cmp( + obj->DI_data.do_servers.do_servers_val[i].name, + newserv->name) == SAME_NAME) + break; + if (i < nserv) { + fprintf(stderr, "\"%s\" already serves \"%s\"!\n", + host, name); + exit(1); + } + + /* + * Verify server able to serve intended domain + */ + if (!verify_server(newserv->name, name, 0)) + exit(1); + + /* + * Add slave to the list of servers. + */ + if ((serv = (nis_server*)malloc( + (nserv + 1)*sizeof (nis_server))) == 0) { + nis_perror(NIS_NOMEMORY, "can't add replica"); + exit(1); + } + for (i = 0; i < nserv; i++) + serv[i] = obj->DI_data.do_servers.do_servers_val[i]; + serv[i] = *newserv; + obj->DI_data.do_servers.do_servers_len++; + obj->DI_data.do_servers.do_servers_val = serv; + + mres = nis_modify(name, obj); + if (mres->status != NIS_SUCCESS) { + nis_perror(mres->status, "can't add replica"); + exit(1); + } + nis_freeresult(mres); + /* + * do not free res because it contains pointers to structures + * it did not allocate. + */ +} + + +void +usage() +{ + fprintf(stderr, + "usage: nismkdir [-D defaults] [-m hostname] [-s hostname] dirname\n"); + exit(1); +} + + +void +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + int i; + char *host; + char *name; + int update_master = 0; + char *replicas[MAX_REPLICA]; + int nreplicas = 0; + + while ((c = getopt(argc, argv, "D:m:s:")) != -1) { + switch (c) { + case 'D': + defstr = optarg; + break; + case 'm': + if (update_master) { + fprintf(stderr, + "only one master can be specified\n"); + exit(1); + } + update_master = 1; + host = optarg; + break; + case 's': + if (nreplicas >= MAX_REPLICA) { + fprintf(stderr, "too many replicas\n"); + exit(1); + } + replicas[nreplicas++] = optarg; + break; + default: + usage(); + } + } + + if (argc - optind != 1) + usage(); + + name = argv[optind]; + if (name[strlen(name)-1] != '.') { + fprintf(stderr, "dirname must be fully qualified.\n"); + exit(1); + } + + /* + * If no master or replica flag, just create directory. + * The directory will have the same replication as its parent + * directory. + */ + if (! update_master && nreplicas == 0) + make_directory(name); + else { + if (update_master) + make_directory_master(name, host); + + for (i = 0; i < nreplicas; i++) + make_directory_replica(name, replicas[i]); + } + + exit(0); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nispath.c b/usr/src/cmd/rpcsvc/nis/utils/nispath.c new file mode 100644 index 0000000000..08617bb938 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nispath.c @@ -0,0 +1,90 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nispath.c + * + * Copyright (c) 1988-1992 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nispath.c + * + * This little utility will print out the search path for a given + * NIS+ name. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <rpcsvc/nis.h> + +void +usage(cmd) + char *cmd; +{ + fprintf(stderr, "usage : %s [-v] name\n", cmd); + exit(1); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + nis_name *result; + int i = 0; + char *name; + int verbose = 0; + + if ((argc == 1) || (argc > 3)) + usage(argv[0]); + + if (argc == 3) { + if (strcmp(argv[1], "-v") == 0) + verbose = 1; + else + usage(argv[0]); + name = argv[2]; + } else { + if (strcmp(argv[1], "-v") == 0) + usage(argv[0]); + name = argv[1]; + } + + result = nis_getnames(name); + if (verbose) { + printf("For NIS+ Name : \"%s\"\n", name); + printf("Search Path :\n"); + } + if (! result) { + if (verbose) + printf("\t**NONE**\n"); + exit(1); + } else + while (result[i]) { + if (verbose) + printf("\t"); + printf("\"%s\"\n", result[i++]); + } + exit(0); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisprefadm.c b/usr/src/cmd/rpcsvc/nis/utils/nisprefadm.c new file mode 100644 index 0000000000..fa7044a795 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisprefadm.c @@ -0,0 +1,1816 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1996-1999 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <rpcsvc/nis.h> + +#define LOCAL_PREF "/var/nis/client_info" +#define LOCAL_TMP "/var/nis/client_info.tmp" +#define TABLE "client_info" +#define TABLE_TYPE "client_info_tbl" +#define NCOLS 4 + +#define PREF_SRVR "pref_srvr" +#define PREF_TYPE "pref_type" + +#define OP_TEST 1 +#define OP_FLUSH 2 +#define OP_GLOBAL 3 +#define OP_LOCAL 4 +#define OP_ADD 5 +#define OP_MODIFY 6 +#define OP_UPDATE 7 +#define OP_REMOVE 8 +#define OP_DELETE 9 +#define OP_LIST 10 + +struct server_list { + char *client; + char *options; + char **list; + char **interface; + int *weight; + int count; + int alloc; + int ntoken; + char *tokens[20]; + char *token_buff; +}; +typedef struct server_list server_list; +int debug = 0; +int verbose = 0; +int entry_found = 0; +static int old_format = 0; + +void get_local_servers(server_list *, char *, int); +void parse_preference(server_list *, char *, char *, char *); +int parse_info(server_list *, char *, char **, char **); +void parse_server(char *, char **, char **, int *); +void server_list_init(server_list *); +void server_list_clear(server_list *); +void server_list_add(server_list *, char *); +int server_list_remove(server_list *, char *); +void server_list_print(server_list *, FILE *); +void get_servers(server_list *, char *, char *, int); +void remove_local_servers(server_list *); +void remove_servers(server_list *, char *); +void update_local_servers(server_list *); +void update_servers(server_list *, char *); +void create_table(char *, char *); +void convert_old2new(server_list *, char *); +int print_tokens(FILE *, server_list *, char *); +void print_local_servers(server_list *, char *); +void print_servers(server_list *, char *, char *); +int flush_cache(char *); + +extern nis_object nis_default_obj; + +extern ulong_t __inet_get_addr(void *, int); +extern void *__inet_get_local_interfaces(); +extern void __inet_free_local_interfaces(void *); +extern char *__inet_get_networka(void *, int); +extern int __inet_address_count(void *); + + +void +usage(char *s) +{ + fprintf(stderr, "\t%s -a {-L|-G} [-o <opt-string>] [-d domain] ", s); + fprintf(stderr, "[-C client] {<server-list>} ...\n"); + + fprintf(stderr, "\t%s -l {-L|-G} [-C client]\n", s); + + fprintf(stderr, "\t%s -m {-L|-G} [-o <opt-string>] [-d domain] ", s); + fprintf(stderr, "[-C client] {<old-server>=<new-server>} ...\n"); + + fprintf(stderr, "\t%s -r {-L|-G} [-o <opt-string>] [-d domain] ", s); + fprintf(stderr, "[-C client] {<server-list>} ...\n"); + + fprintf(stderr, "\t%s -u {-L|-G} [-o <opt-string>] [-d domain] ", s); + fprintf(stderr, "[-C client] {<server-list>}\n"); + + fprintf(stderr, "\t%s -x {-L|-G} [-d domain] [-C client]\n", s); + + fprintf(stderr, "\t%s -F\n", s); + + exit(1); +} + +main(int argc, char **argv) +{ + int c; + int op; + int subop; + int op_count = 0; + int subop_count = 0; + char *options = NULL; + char *domain = NULL; + char *client = NULL; + int create = 0; + server_list servers; + + while ((c = getopt(argc, argv, "vZTFLGalmurxo:d:C:")) != EOF) { + switch (c) { + case 'T': + op_count++; + op = OP_TEST; + break; + case 'F': + if (geteuid() != (uid_t)0) { + fprintf(stderr, + "%s: -F option must be run as root.\n", + argv[0]); + exit(1); + } + op_count++; + op = OP_FLUSH; + break; + case 'L': + op_count++; + op = OP_LOCAL; + break; + case 'G': + op_count++; + op = OP_GLOBAL; + break; + case 'a': + subop_count++; + subop = OP_ADD; + create = 1; + break; + case 'l': + subop_count++; + subop = OP_LIST; + break; + case 'm': + subop_count++; + subop = OP_MODIFY; + break; + case 'u': + subop_count++; + subop = OP_UPDATE; + break; + case 'r': + subop_count++; + subop = OP_REMOVE; + break; + case 'x': + subop_count++; + subop = OP_DELETE; + break; + case 'o': + options = optarg; + break; + case 'd': + domain = optarg; + break; + case 'C': + client = optarg; + break; + case 'Z': + debug = 1; + break; + case 'v': + verbose = 1; + break; + default: + usage(argv[0]); + break; + } + } + + if (op_count == 0) { + fprintf(stderr, + "%s: one of -L, -G, -T, or -F must be specified\n", + argv[0]); + usage(argv[0]); + } else if (op_count != 1) { + fprintf(stderr, + "%s: only one of -L, -G, -T, or -F may be specified\n", + argv[0]); + usage(argv[0]); + } + if (op == OP_LOCAL || op == OP_GLOBAL) { + if (subop_count == 0) { + fprintf(stderr, + "%s: one of -a, -l, -m, -r, or -x must be specified\n", + argv[0]); + usage(argv[0]); + } else if (subop_count != 1) { + fprintf(stderr, + "%s: only one of -a, -l, -m, -r, or -x may be specified\n", + argv[0]); + usage(argv[0]); + } + } + + if (!nis_defaults_init(NULL)) + exit(1); + + if (op == OP_TEST) { + exit(0); + } + + if (op == OP_FLUSH) { + exit(flush_cache(argv[0])); + } + + /* OP_LOCAL or OP_GLOBAL */ + if (subop == OP_LIST) { + /* listing the clint_info */ + if (op == OP_LOCAL) + print_local_servers(&servers, client); + else { + if (domain == NULL) + domain = nis_local_directory(); + print_servers(&servers, domain, client); + } + exit(0); + } + if (geteuid() != (uid_t)0) { + fprintf(stderr, + "%s: -L option must be run as root except to list " + "the preferred server information (the -l option)\n", + argv[0]); + exit(1); + } + + if (client == NULL) { + client = (char *)malloc(256 * sizeof (char)); + if (client == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + if (gethostname(client, 256)) { + perror("gethostname()"); + exit(1); + } + } + + if (op == OP_LOCAL) + get_local_servers(&servers, client, create); + else { + if (domain == NULL) + domain = nis_local_directory(); + get_servers(&servers, domain, client, create); + } + + if (client) + servers.client = client; + if (options) { + if (*options && (strcasecmp(options, "all") != 0) && + (strcasecmp(options, "pref_only") != 0)) { + fprintf(stderr, "%s: invalid option specified.\n", + options); + fprintf(stderr, + "Valid options are \"all\" and \"pref_only\".\n"); + exit(1); + } + servers.options = options; + } + + if (subop == OP_ADD) { + for (; optind < argc; optind++) { + char *buf, *name, *p; + buf = strdup(argv[optind]); + name = buf; + do { + if (p = strchr(name, ',')) + *p++ = '\0'; + if (verbose) + printf("Adding server %s...\n", + name); + server_list_add(&servers, name); + name = p; + } while (p); + free(buf); + } + } else if (subop == OP_UPDATE) { + server_list_clear(&servers); + for (; optind < argc; optind++) { + char *buf, *name, *p; + buf = strdup(argv[optind]); + name = buf; + do { + if (p = strchr(name, ',')) + *p++ = '\0'; + if (verbose) + printf("Updating server %s...\n", + name); + server_list_add(&servers, name); + name = p; + } while (p); + free(buf); + } + } else if (subop == OP_MODIFY) { + for (; optind < argc; optind++) { + char *p, tmp[100]; + strcpy(tmp, argv[optind]); + p = strchr(tmp, '='); + if (p == NULL) { + fprintf(stderr, + "%s: Ignored (invalid format)\n", tmp); + continue; + } + *p++ = '\0'; + if (!server_list_remove(&servers, tmp)) + fprintf(stderr, "%s: not found\n", tmp); + else { + if (verbose) + printf("Modifying server %s...\n", tmp); + server_list_add(&servers, p); + } + } + } else if (subop == OP_REMOVE) { + for (; optind < argc; optind++) { + char *buf, *name, *p; + buf = strdup(argv[optind]); + name = buf; + do { + if (p = strchr(name, ',')) + *p++ = '\0'; + if (verbose) + printf("Removing server %s...\n", + name); + (void) server_list_remove(&servers, name); + name = p; + } while (p); + free(buf); + } + } + + if (subop == OP_DELETE) { + if (verbose) + printf("Deleting entry %s...\n", servers.client); + if (op == OP_LOCAL) { + remove_local_servers(&servers); + (void) flush_cache(argv[0]); + } else + remove_servers(&servers, domain); + } else { + if (op == OP_LOCAL) { + update_local_servers(&servers); + (void) flush_cache(argv[0]); + } else + update_servers(&servers, domain); + } + return (0); +} + + +void +server_list_init(server_list *servers) +{ + int i; + + servers->options = ""; + servers->client = ""; + servers->count = 0; + servers->alloc = 5; + servers->list = (char **)malloc(servers->alloc * sizeof (char *)); + servers->interface = (char **)malloc(servers->alloc * sizeof (char *)); + servers->weight = (int *)malloc(servers->alloc * sizeof (int)); + if (servers->weight == NULL || servers->list == NULL) { + fprintf(stderr, "out of memory (1)\n"); + exit(1); + } + for (i = 0; i < servers->count; i++) { + servers->interface[i] = NULL; + servers->list[i] = NULL; + servers->weight[i] = -1; + } + servers->ntoken = 0; + servers->token_buff = NULL; +} + +void +server_list_reinit(server_list *servers) +{ + server_list_clear(servers); + servers->options = ""; + servers->client = ""; +} + +void +server_list_clear(server_list *servers) +{ + int i; + + for (i = 0; i < servers->count; i++) { + if (servers->list[i]) + free(servers->list[i]); + servers->list[i] = NULL; + if (servers->interface[i]) + free(servers->interface[i]); + servers->interface[i] = NULL; + servers->weight[i] = -1; + } + servers->count = 0; + if (servers->ntoken > 0) { + servers->ntoken = 0; + free(servers->token_buff); + servers->token_buff = NULL; + } +} + +void +server_list_add(server_list *servers, char *s) +{ + int i; + char *host, *interface; + int weight; + + if (s == NULL || *s == NULL) + return; + + if (debug) + printf("server_list_add: [%d] %s\n", servers->count, s); + + parse_server(s, &host, &interface, &weight); + + for (i = 0; i < servers->count; i++) { + if (strcasecmp(servers->list[i], host) == 0) { + if (interface) { + if (servers->interface[i] && + strcasecmp(servers->interface[i], + interface) == 0) { + servers->weight[i] = weight; + return; + } + } else { + if (!servers->interface[i]) { + /* + * For exact match. Both interfaces + * must be NULL. + */ + servers->weight[i] = weight; + return; + } + } + } + } + + if (servers->count + 1 > servers->alloc) { + servers->alloc += 5; + servers->list = (char **)realloc((char *)servers->list, + servers->alloc * sizeof (char *)); + servers->interface = + (char **)realloc((char *)servers->interface, + servers->alloc * sizeof (char *)); + + if (servers->list == NULL || servers->interface == NULL) { + fprintf(stderr, "out of memory (2)\n"); + exit(1); + } + } + servers->list[servers->count] = host; + servers->interface[servers->count] = interface; + servers->weight[servers->count] = weight; + if (debug) + printf("server_list_add: count=%d l=<%s> i=<%s> w=%d\n", + servers->count, servers->list[servers->count], + (servers->interface[servers->count]) ? + servers->interface[servers->count] : "", + servers->weight[servers->count]); + servers->count += 1; +} + +int +server_list_remove(server_list *servers, char *s) +{ + int i; + char *host, *interface; + int weight; + + if (s == NULL || *s == NULL) + return (0); + + if (debug) + printf("server_list_remove: [%d] %s\n", servers->count, s); + + parse_server(s, &host, &interface, &weight); + + for (i = 0; i < servers->count; i++) { + if (strcasecmp(servers->list[i], host) == 0) { + /* + * If interface is not defined, then it will match + * the first client entry. + * If interface is defined, then it must match + * the interface in the server list. + */ + if (interface) { + if (servers->interface[i] && + strcasecmp(servers->interface[i], + interface) == 0) + break; + } else { + if (!servers->interface[i]) { + /* + * For exact match. Both interfaces + * must be NULL. + */ + break; + } + } + } + } + + if (i >= servers->count) + return (0); /* not in list */ + + if (servers->list[i]) { + free(servers->list[i]); + } + if (servers->interface[i]) { + free(servers->interface[i]); + } + servers->list[i] = NULL; + servers->interface[i] = NULL; + servers->weight[i] = -1; + + for (; i < servers->count - 1; i++) { + servers->list[i] = servers->list[i+1]; + servers->interface[i] = servers->interface[i+1]; + servers->weight[i] = servers->weight[i+1]; + } + + servers->count -= 1; + if (debug) + printf("server_list_remove: removed =%s\n", host); + return (1); +} + +void +server_list_print(server_list *servers, FILE *fp) +{ + int i; + int len, l; + + if (debug) { + printf("server_list_print: client=%s", servers->client); + } + if (servers->count == 0) { + char buf[50]; + strcpy(buf, servers->client); + strcat(buf, "\t"); + if (print_tokens(fp, servers, buf)) { + if (debug) + printf("\n"); + fprintf(fp, "\n"); + } else + fprintf(stderr, + "This became an empty entry. Removing it from the file\n"); + return; + } + + fprintf(fp, "%s", servers->client); + len = strlen(servers->client); + for (i = 0; i < servers->count; i++) { + if (i == 0) { + if (debug) + printf("\t%s=", PREF_SRVR); + fprintf(fp, "\t%s=", PREF_SRVR); + len += strlen(PREF_SRVR) + 1; + } else { + if (debug) + printf(","); + fprintf(fp, ","); + l = strlen(servers->list[i]) + 1; + if (servers->interface[i]) + l += strlen(servers->interface[i]) + 2; + len += l; + if (len > 75) { + if (debug) + printf("\\\n\t"); + fprintf(fp, "\\\n\t"); + len = l; + } + } + if (debug) + printf("%s", servers->list[i]); + fprintf(fp, "%s", servers->list[i]); + if (servers->interface[i]) { + if (debug) + printf("/%s", servers->interface[i]); + fprintf(fp, "/%s", servers->interface[i]); + } + if (servers->weight[i] != -1) { + if (debug) + printf("(%d)", servers->weight[i]); + fprintf(fp, "(%d)", servers->weight[i]); + } + } + if (*servers->options) { + char *tab; + if (servers->count > 0) + tab = " "; + else + tab = "\t"; + if (debug) + printf("%s%s=%s", tab, PREF_TYPE, + servers->options); + fprintf(fp, "%s%s=%s", tab, PREF_TYPE, + servers->options); + } + (void) print_tokens(fp, servers, " "); + + if (debug) + printf("\n"); + fprintf(fp, "\n"); + +} + + +void +server_list_dump(char *value, FILE *fp) +{ + int len, n; + char *p, *pe; + char buf[100]; + + len = strlen(value); + if (debug) + printf("server_list_dump: [%d] <%s>\n", len, value); + p = value; + for (; len > 80; ) { + pe = strpbrk(p + 70, " ,\t"); + n = pe - p + 1; + strncpy(buf, p, n); + buf[n] = '\0'; + if (debug) + printf("%s\\\n\t", buf); + fprintf(fp, "%s\\\n\t", buf); + len -= n; + p = pe + 1; + } + if (debug) + printf("%s\n", p); + fprintf(fp, "%s\n", p); +} + + + +void +parse_server(char *value, char **server, char **interface, int *weight) +{ + char *p, *buf; + char *s = NULL, *i = NULL; + int w = -1; + + *weight = -1; + *server = NULL; + *interface = NULL; + + if (value == NULL || *value == '\0') + return; + + buf = strdup(value); + s = buf; + + while ((p = strpbrk(buf, "/(")) != NULL) { + switch (*p) { + case '/': + if (i != NULL) { + fprintf(stderr, "%s : invalid format\n", + value); + exit(1); + } + *p = '\0'; + i = ++p; + if (debug) + printf("parse_server: interface=%s\n", i); + break; + case '(': + if (w != -1) { + fprintf(stderr, "%s : invalid format\n", + value); + exit(1); + } + *p = '\0'; + w = atoi(++p); + p = strchr(p, ')'); + if (debug) + printf("parse_server: weight=%d\n", w); + break; + } + buf = p; + } + + *server = strdup(s); + if (*server == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + *weight = w; + if (i != NULL) { + /* check if the interface specified is a legal IP address */ + struct in_addr in4; + struct in6_addr in6; + sa_family_t af; + + af = strchr(i, ':') != 0 ? AF_INET6 : AF_INET; + if (inet_pton(af, i, + (af == AF_INET6) ? (void *)&in6 : (void *)&in4) != 1) { + fprintf(stderr, "%s: invalid interface.\n", i); + fprintf(stderr, + "This should be the internet address for the interface.\n"); + exit(1); + } + *interface = strdup(i); + if (*interface == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + } + + if (debug) + printf("parse_server: server=%s, weight=%d, interface=%s\n", + *server, *weight, (*interface)? *interface : ""); +} + + +int +break_tokens(server_list *servers, char *info) +{ + char *p; + + if (debug) + printf("break_tokens: %s\n", info); + + if (servers->ntoken != 0 && servers->token_buff) { + free(servers->token_buff); + servers->token_buff = NULL; + servers->ntoken = 0; + } + servers->token_buff = strdup(info); + p = servers->token_buff; + while (*p) { + /* skip spaces */ + while (*p && isspace(*p)) + p++; + if (*p == '\0') + break; + /* assign token */ + servers->tokens[servers->ntoken] = p; + + /* find the end of token */ + while (*p && !isspace(*p)) + p++; + if (*p != '\0') { + *p = '\0'; + p++; + } + if (debug) + printf("break_tokens: [%d]=%s\n", servers->ntoken, + servers->tokens[servers->ntoken]); + servers->ntoken++; + } + return (servers->ntoken); +} + + + +int +print_tokens(FILE *fp, server_list * servers, char *h) +{ + int i; + int count = 0; + int first = 1; + + for (i = 0; i < servers->ntoken; i++) { + if (servers->tokens[i][0] != '\0') { + if (debug) + printf("%s%s", (first) ? h : " ", + servers->tokens[i]); + fprintf(fp, "%s%s", (first) ? h : " ", + servers->tokens[i]); + first = 0; + count++; + } + } + return (count); +} + + + +int +parse_info(server_list *servers, char *info, char **hosts, char **options) +{ + char *p1 = NULL; + int i, n; + + if ((info == NULL) || (*info == '\0')) { + *hosts = NULL; + *options = NULL; + return (1); + } + + /* parse hosts */ + n = break_tokens(servers, info); + for (i = 0; i < n; i++) { + if (debug) + printf("parse_info: servers->tokens[%d]=%s\n", i, + servers->tokens[i]); + if (strncmp(servers->tokens[i], PREF_SRVR, + strlen(PREF_SRVR)) == 0) { + /* preferred servers */ + p1 = servers->tokens[i] + strlen(PREF_SRVR); + if (*p1 != '=' || *hosts != NULL) + /* invalid format */ + return (0); + *hosts = strdup(p1 + 1); + if (*hosts == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + servers->tokens[i][0] = '\0'; + if (debug) + printf("parse_info: hosts=%s\n", *hosts); + } else if (strncmp(servers->tokens[i], PREF_TYPE, + strlen(PREF_TYPE)) == 0) { + /* preferred types */ + p1 = servers->tokens[i] + strlen(PREF_TYPE); + if (*p1 != '=' || *options != NULL) + /* invalid format */ + return (0); + *options = strdup(p1 + 1); + if (*options == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + servers->tokens[i][0] = '\0'; + if (*options[0] && (strcasecmp(*options, "all") != 0) && + (strcasecmp(*options, "pref_only") != 0)) { + fprintf(stderr, "%s: invalid option.\n", + *options); + fprintf(stderr, + "Valid options are \"all\" and \"pref_only\".\n"); + exit(1); + } + if (debug) + printf("parse_info: options=%s\n", *options); + } + } + + return (1); +} + + +void +parse_preference(server_list *servers, char *client, char *hosts, char *options) +{ + char *value; + + /* parse client */ + servers->client = client; + if (debug) + printf("parse_preference: hosts=%s\n", hosts); + + /* parse hosts */ + while (*hosts) { + value = hosts; + while (*hosts && !isspace(*hosts) && *hosts != ',') + hosts++; + + if (hosts && *hosts) + *hosts++ = '\0'; + + while (hosts && *hosts == ',') + hosts++; + + server_list_add(servers, value); + } + + /* parse options */ + servers->options = options; +} + + +char * +get_line(FILE *fp) +{ + char *p; + int len, cont = 0; + char *value = NULL; + char buf[1024]; + + while ((p = fgets(buf, sizeof (buf), fp)) != NULL) { + cont = 0; + len = strlen(p); + if ((len - 1 >= 0) && (p[len - 1] == '\n')) + p[len - 1] = '\0'; + if ((len - 2 >= 0) && (p[len - 2] == '\\')) { + cont = 1; + p[len - 2] = '\0'; + } + + if (value == NULL) { + value = strdup(p); + if (value == 0) { + fprintf(stderr, "out of memory (3)\n"); + exit(1); + } + } else { + value = (char *)realloc(value, + strlen(value) + len + 2); + if (value == 0) { + fprintf(stderr, "out of memory (3)\n"); + exit(1); + } + while (*p && isspace(*p)) + p++; + strcat(value, p); + } + if (!cont) + break; /* complete line */ + } + if (debug && value != NULL) + printf("get_line: text=%s\n", value); + + return (value); +} + + +void +get_local_servers(server_list *servers, char *target, int create) +{ + FILE *fp; + char *value = NULL; + char *client = NULL; + char *hosts = NULL; + char *option = NULL; + char *info = NULL; + char *p = NULL; + + server_list_init(servers); + fp = fopen(LOCAL_PREF, "r"); + if (fp == NULL) { + if (create) { + /* + * Allow it to continue... + * File will be created in update_local_servers() + */ + return; + } else { + fprintf(stderr, "%s does not exist\n", LOCAL_PREF); + exit(1); + } + } + + while ((value = get_line(fp)) != NULL) { + hosts = NULL; + option = NULL; + client = value; + p = strpbrk(value, " \t"); + if (p == NULL) { + fprintf(stderr, "%s: %s\n", LOCAL_PREF, value); + fprintf(stderr, "Invalid format\n"); + exit(1); + } + + *p++ = '\0'; + while (*p && isspace(*p)) + p++; + info = p; + if (!parse_info(servers, info, &hosts, &option)) { + fprintf(stderr, + "Error found while parsing local file\n"); + fprintf(stderr, "Please see manpage\n"); + exit(1); + } + + if (client == NULL) + client = ""; + if (hosts == NULL) + hosts = ""; + if (option == NULL) + option = ""; + + parse_preference(servers, client, hosts, option); + + if (strcasecmp(client, target) == 0) + break; + + server_list_reinit(servers); + free(value); + value = NULL; + if (*hosts == '\0') + free(hosts); + if (*option == '\0') + free(option); + } + + fclose(fp); +} + +void +get_servers(server_list *servers, char *domain, char *client, int create) +{ + char *hosts = NULL; + char *options = NULL; + char name[NIS_MAXNAMELEN]; + nis_result *res; + nis_object *obj; + ulong_t flags = FOLLOW_PATH|FOLLOW_LINKS|EXPAND_NAME; + + entry_found = 0; + /* first check to see if table exists */ + sprintf(name, "%s.org_dir.%s", TABLE, domain); + res = nis_lookup(name, flags); + if (res == 0) { + fprintf(stderr, "nis_lookup failed\n"); + nis_perror(NIS_NOMEMORY, name); + exit(1); + } + + server_list_init(servers); + if (res->status == NIS_NOTFOUND || res->status == NIS_NOSUCHTABLE) { + if (create) { + create_table(name, domain); + res->status = NIS_SUCCESS; + return; + } else { + nis_perror(res->status, "finding table"); + exit(1); + } + } + + obj = res->objects.objects_val; + if (obj->TA_data.ta_cols.ta_cols_len == 2) + old_format = 1; /* need old format for back compat */ + else + old_format = 0; + + if (old_format) + sprintf(name, + "[client=%s],%s.org_dir.%s", client, TABLE, domain); + else + sprintf(name, + "[client=%s,attr=%s],%s.org_dir.%s", client, PREF_SRVR, + TABLE, domain); + res = nis_list(name, flags, 0, 0); + if (res == 0) { + fprintf(stderr, "nis_list failed\n"); + nis_perror(NIS_NOMEMORY, name); + exit(1); + } + if (res->status == NIS_NOTFOUND) { + return; + } + + if (res->status != NIS_SUCCESS) { + nis_perror(res->status, name); + exit(1); + } + + entry_found = 1; + obj = res->objects.objects_val; + client = ENTRY_VAL(obj, 0); + if (old_format) { + char *info = ENTRY_VAL(obj, 1); + if (!parse_info(servers, info, &hosts, &options)) { + printf( + "Error found while parsing the table information\n"); + printf("Please see manpage.\n"); + exit(1); + } + } else { + hosts = ENTRY_VAL(obj, 2); + options = ENTRY_VAL(obj, 3); + } + + if (client == NULL) + client = ""; + if (hosts == NULL) + hosts = ""; + if (options == NULL) + options = ""; + + parse_preference(servers, client, hosts, options); +} + +void +remove_local_servers(server_list *target) +{ + FILE *fpin; + FILE *fpout; + char *value; + char *client; + char *p; + + fpout = fopen(LOCAL_TMP, "w"); + if (fpout == NULL) { + fprintf(stderr, "can't open %s for output\n", LOCAL_TMP); + exit(1); + } + + fpin = fopen(LOCAL_PREF, "r"); + if (fpin != NULL) { + while ((value = get_line(fpin)) != NULL) { + client = value; + p = strpbrk(value, " \t"); + if (p == NULL) { + fprintf(stderr, "%s: %s\n", LOCAL_PREF, value); + fprintf(stderr, "Invalid format\n"); + exit(1); + } + if (strncasecmp(client, target->client, p - value) + != 0) { + /* skip this line */ + server_list_dump(value, fpout); + } + } + fclose(fpin); + } else { + /* local file does not exist */ + fclose(fpout); + unlink(LOCAL_TMP); + fprintf(stderr, "%s does not exist\n", LOCAL_PREF); + exit(1); + } + if (fclose(fpout) != 0 || rename(LOCAL_TMP, LOCAL_PREF) == -1) { + unlink(LOCAL_TMP); + perror(LOCAL_TMP); + exit(1); + } +} + +void +remove_servers(server_list *servers, char *domain) +{ + char name[NIS_MAXNAMELEN]; + nis_result *res; + ulong_t flags = FOLLOW_PATH|FOLLOW_LINKS|EXPAND_NAME; + + if (debug) + printf("remove_servers: removing %s\n", servers->client); + if (old_format) + sprintf(name, "[client=%s],%s.org_dir.%s", + servers->client, TABLE, domain); + else + sprintf(name, "[client=%s,attr=%s],%s.org_dir.%s", + servers->client, PREF_SRVR, TABLE, domain); + res = nis_remove_entry(name, NULL, flags); + if (res == 0) { + fprintf(stderr, "nis_remove_entry failed\n"); + nis_perror(NIS_NOMEMORY, name); + exit(1); + } + if (res->status == NIS_NOTFOUND) { + return; + } + if (res->status != NIS_SUCCESS) { + nis_perror(res->status, name); + exit(1); + } +} + +void +update_local_servers(server_list *target) +{ + FILE *fpin; + FILE *fpout; + char *value = NULL; + char *client; + char *p; + + fpout = fopen(LOCAL_TMP, "w"); + if (fpout == NULL) { + fprintf(stderr, "can't open %s for output\n", LOCAL_TMP); + exit(1); + } + + fpin = fopen(LOCAL_PREF, "r"); + if (fpin != NULL) { + while ((value = get_line(fpin)) != NULL) { + client = value; + p = strpbrk(value, " \t"); + if (p == NULL) { + fprintf(stderr, "%s: %s\n", LOCAL_PREF, value); + fprintf(stderr, "Invalid format\n"); + exit(1); + } + if (strncasecmp(client, target->client, p - value) + != 0) { + /* skip this line */ + server_list_dump(value, fpout); + } + } + fclose(fpin); + } + + server_list_print(target, fpout); + + if (fclose(fpout) != 0 || rename(LOCAL_TMP, LOCAL_PREF) == -1) { + unlink(LOCAL_TMP); + perror(LOCAL_TMP); + exit(1); + } +} + +void +update_servers(server_list *servers, char *domain) +{ + int i; + int len = 0, bufsize = 1000; + char *buf, *optbuf; + entry_col cols[NCOLS]; + nis_object eobj; + nis_result *res; + char table[NIS_MAXNAMELEN]; + char tmp[10], lbuf[100]; + int first = 1; + + if (old_format) + convert_old2new(servers, domain); + + buf = (char *)malloc(bufsize * sizeof (char)); + if (buf == NULL) { + fprintf(stderr, "out of memory (4)\n"); + exit(1); + } + optbuf = (char *)malloc(bufsize * sizeof (char)); + if (optbuf == NULL) { + fprintf(stderr, "out of memory (4)\n"); + exit(1); + } + + buf[0] = optbuf[0] = '\0'; + if (servers->count > 0) { + if (debug) + printf("update_servers:\n"); + for (i = 0; i < servers->count; i++) { + if (i != 0) { + lbuf[0] = ','; + lbuf[1] = '\0'; + } else + lbuf[0] = '\0'; + if (debug) + printf("adding %d: %s\n", i, servers->list[i]); + strcat(lbuf, servers->list[i]); + if (servers->interface[i]) { + if (debug) + printf("/%s", servers->interface[i]); + strcat(lbuf, "/"); + strcat(lbuf, servers->interface[i]); + } + if (servers->weight[i] != -1) { + if (debug) + printf("(%d)", servers->weight[i]); + sprintf(tmp, "(%d)", servers->weight[i]); + strcat(lbuf, tmp); + } + len += strlen(lbuf); + if (len > bufsize) { + bufsize += 1000; + buf = (char *)realloc(buf, bufsize * + sizeof (char)); + } + strcat(buf, lbuf); + first = 0; + } + } + + bufsize = 1000; + len = 0; + if (servers->options && *(servers->options)) { + lbuf[0] = '\0'; + if (debug) + printf("update_servers: pref_type = %s\n", + servers->options); + strcat(lbuf, servers->options); + len += strlen(lbuf); + if (len > bufsize) { + bufsize += 50; + optbuf = (char *)realloc + (optbuf, bufsize * sizeof (char)); + } + strcat(optbuf, lbuf); + first = 0; + } + + if (strlen(buf) == 0) { + if (entry_found) { + fprintf(stderr, + "This became an empty entry. Removing it from the table\n"); + remove_servers(servers, domain); + } + exit(0); + } + + if (debug) + printf("info=%s, %s\n", buf, optbuf); + + cols[0].ec_flags = 0; + cols[0].ec_value.ec_value_val = servers->client; + cols[0].ec_value.ec_value_len = strlen(servers->client) + 1; + + cols[1].ec_flags = 0; + cols[1].ec_value.ec_value_val = PREF_SRVR; + cols[1].ec_value.ec_value_len = strlen(PREF_SRVR) + 1; + + cols[2].ec_flags = 0; + cols[2].ec_value.ec_value_val = buf; + cols[2].ec_value.ec_value_len = strlen(buf) + 1; + + cols[3].ec_flags = 0; + cols[3].ec_value.ec_value_val = optbuf; + cols[3].ec_value.ec_value_len = strlen(optbuf) + 1; + + eobj = nis_default_obj; + eobj.zo_data.zo_type = ENTRY_OBJ; + eobj.EN_data.en_type = TABLE_TYPE; + eobj.EN_data.en_cols.en_cols_len = NCOLS; + eobj.EN_data.en_cols.en_cols_val = cols; + + sprintf(table, "%s.org_dir.%s", TABLE, domain); + if (debug) + printf("calling nis_add_entry..."); + res = nis_add_entry(table, &eobj, ADD_OVERWRITE); + if (debug) + printf("Done\n"); + if (res == 0) { + fprintf(stderr, "nis_add_entry failed\n"); + nis_perror(NIS_NOMEMORY, table); + exit(1); + } else if (res->status != NIS_SUCCESS) { + nis_perror(res->status, table); + exit(1); + } +} + +/* + * table_col cols[] = { + * "client", TA_SEARCHABLE, 0, + * "servers", 0, 0, + * "options", 0, 0, + * }; + */ + +void +create_table(char *name, char *domain) +{ + nis_object tobj; + nis_result *res; + int ncols = NCOLS; + table_col cols[NCOLS]; + char gname[NIS_MAXNAMELEN]; + + if (debug) + printf("creating table\n"); + cols[0].tc_name = "client"; + cols[0].tc_flags = TA_SEARCHABLE | TA_CASE; + cols[0].tc_rights = nis_default_obj.zo_access; + + cols[1].tc_name = "attr"; + cols[1].tc_flags = TA_SEARCHABLE | TA_CASE; + cols[1].tc_rights = nis_default_obj.zo_access; + + cols[2].tc_name = "info"; + cols[2].tc_flags = 0; + cols[2].tc_rights = nis_default_obj.zo_access; + + cols[3].tc_name = "flags"; + cols[3].tc_flags = 0; + cols[3].tc_rights = nis_default_obj.zo_access; + + tobj = nis_default_obj; + sprintf(gname, "admin.%s", domain); + tobj.zo_group = gname; + tobj.zo_access |= ((NIS_READ_ACC | NIS_MODIFY_ACC | NIS_CREATE_ACC | + NIS_DESTROY_ACC) << 8); + tobj.zo_data.zo_type = TABLE_OBJ; + tobj.TA_data.ta_type = TABLE_TYPE; + tobj.TA_data.ta_maxcol = ncols; + tobj.TA_data.ta_sep = ' '; + tobj.TA_data.ta_path = ""; + tobj.TA_data.ta_cols.ta_cols_len = ncols; + tobj.TA_data.ta_cols.ta_cols_val = cols; + if (debug) + printf("calling nis_add %s\n", name); + res = nis_add(name, &tobj); + if (res == NULL) { + fprintf(stderr, "nis_add failed\n"); + nis_perror(NIS_NOMEMORY, name); + exit(1); + } + if (res->status != NIS_SUCCESS) { + nis_perror(res->status, name); + exit(1); + } + +} + +void +convert_old2new(server_list *servers, char *domain) +{ + nis_result *old_res, *res; + char name[NIS_MAXNAMELEN]; + nis_object eobj; + int i; + ulong_t flags = FOLLOW_PATH|FOLLOW_LINKS|EXPAND_NAME; + + if (debug) + printf("Converting table\n"); + /* dump old table */ + if (debug) + printf("dumping old table..."); + sprintf(name, "%s.org_dir.%s", TABLE, domain); + old_res = nis_list(name, flags, 0, 0); + if (old_res == 0) { + fprintf(stderr, "failed to convert %s to new format\n", + TABLE); + fprintf(stderr, "while dumping table\n"); + fprintf(stderr, "nis_list failed\n"); + nis_perror(NIS_NOMEMORY, name); + exit(1); + } + if (old_res->status != NIS_SUCCESS) { + fprintf(stderr, "failed to convert %s to new format\n", + TABLE); + fprintf(stderr, "while dumping table\n"); + nis_perror(old_res->status, name); + exit(1); + } + if (debug) + printf("done\n"); + + /* delete old table */ + if (debug) + printf("cleaning out old table..."); + res = nis_remove_entry(name, NULL, flags|REM_MULTIPLE); + if (res == 0) { + fprintf(stderr, "failed to convert %s to new format\n", + TABLE); + fprintf(stderr, "while cleaning out table\n"); + fprintf(stderr, "nis_remove_entry failed\n"); + nis_perror(NIS_NOMEMORY, name); + exit(1); + } + if (res->status != NIS_SUCCESS) { + fprintf(stderr, "failed to convert %s to new format\n", + TABLE); + fprintf(stderr, "while cleaning out table\n"); + nis_perror(res->status, name); + exit(1); + } + if (debug) + printf("done\n"); + if (debug) + printf("removing old table..."); + res = nis_remove(name, NULL); + if (res == 0) { + fprintf(stderr, "failed to convert %s to new format\n", + TABLE); + fprintf(stderr, "while deleting table\n"); + fprintf(stderr, "nis_remove failed\n"); + nis_perror(NIS_NOMEMORY, name); + exit(1); + } + if (res->status != NIS_SUCCESS) { + fprintf(stderr, "failed to convert %s to new format\n", + TABLE); + fprintf(stderr, "while deleting table\n"); + nis_perror(res->status, name); + exit(1); + } + if (debug) + printf("done\n"); + + /* create new table */ + create_table(name, domain); + + /* reload table with info from old table */ + if (debug) + printf("reloading table in new format\n"); + eobj = nis_default_obj; + eobj.zo_data.zo_type = ENTRY_OBJ; + eobj.EN_data.en_type = TABLE_TYPE; + eobj.EN_data.en_cols.en_cols_len = NCOLS; + + for (i = 0; i < old_res->objects.objects_len; i++) { + nis_object *obj; + char *hosts, *options; + entry_col cols[NCOLS]; + + hosts = options = NULL; + obj = &(old_res->objects.objects_val[i]); + /* copy client column */ + cols[0].ec_flags = 0; + cols[0].ec_value.ec_value_len = ENTRY_LEN(obj, 0); + cols[0].ec_value.ec_value_val = ENTRY_VAL(obj, 0); + + /* fill in pref_srvr column */ + cols[1].ec_flags = 0; + cols[1].ec_value.ec_value_val = PREF_SRVR; + cols[1].ec_value.ec_value_len = strlen(PREF_SRVR) + 1; + + /* extract servers and options */ + parse_info(servers, ENTRY_VAL(obj, 1), &hosts, &options); + if (hosts == NULL) + hosts = ""; + if (options == NULL) + options = ""; + if (debug) { + printf("convert_old2new: hosts = %s\n", hosts); + printf("convert_old2new: options = %s\n", options); + } + + /* fill in servers column */ + cols[2].ec_flags = 0; + cols[2].ec_value.ec_value_val = hosts; + cols[2].ec_value.ec_value_len = strlen(hosts) + 1; + + /* fill in options column */ + cols[3].ec_flags = 0; + cols[3].ec_value.ec_value_val = options; + cols[3].ec_value.ec_value_len = strlen(options) + 1; + + /* add the new entry */ + eobj.EN_data.en_cols.en_cols_val = cols; + if (debug) + printf("convert_old2new: calling nis_add_entry..."); + res = nis_add_entry(name, &eobj, ADD_OVERWRITE); + if (debug) + printf("Done\n"); + if (res == 0) { + fprintf(stderr, "failed to convert %s to new format\n", + TABLE); + fprintf(stderr, "while repopulating table\n"); + fprintf(stderr, "nis_add_entry failed\n"); + nis_perror(NIS_NOMEMORY, TABLE); + exit(1); + } else if (res->status != NIS_SUCCESS) { + fprintf(stderr, "failed to convert %s to new format\n", + TABLE); + fprintf(stderr, "while repopulating table\n"); + nis_perror(res->status, TABLE); + exit(1); + } + } + nis_freeresult(old_res); + if (debug) + printf("finished convert_old2new\n"); +} + +void +print_info(char *client, char *hosts, char *option) +{ + static first = 1; + + if (!(client && *client && hosts && *hosts && option)) + return; + + if (first) { + fprintf(stdout, "client\tinformation\n"); + fprintf(stdout, "------\t-----------\n"); + first = 0; + } + fprintf(stdout, "%s\t%s=%s", client, PREF_SRVR, hosts); + if (*option) + fprintf(stdout, "\t%s=%s", PREF_TYPE, option); + fprintf(stdout, "\n"); +} + +void +print_local_servers(server_list *servers, char *target) +{ + FILE *fp; + char *value = NULL; + char *client = NULL; + char *hosts = NULL; + char *option = NULL; + char *info = NULL; + char *p = NULL; + + fp = fopen(LOCAL_PREF, "r"); + if (fp == NULL) + return; + + server_list_init(servers); + while ((value = get_line(fp)) != NULL) { + hosts = NULL; + option = NULL; + client = value; + p = strpbrk(value, " \t"); + if (p == NULL) { + fprintf(stderr, "%s: %s\n", LOCAL_PREF, value); + fprintf(stderr, "Invalid format\n"); + exit(1); + } + + *p++ = '\0'; + while (*p && isspace(*p)) + p++; + info = p; + if (!parse_info(servers, info, &hosts, &option)) { + fprintf(stderr, + "Error found while parsing local file\n"); + fprintf(stderr, "Please see manpage\n"); + exit(1); + } + + if (client == NULL) + client = ""; + if (hosts == NULL) + hosts = ""; + if (option == NULL) + option = ""; + + if (target && *target) { + if (strcasecmp(client, target) == 0) { + print_info(client, hosts, option); + fclose(fp); + return; + } + } else { + /* list all entries */ + print_info(client, hosts, option); + } + + server_list_reinit(servers); + free(value); + value = NULL; + if (*hosts == '\0') + free(hosts); + if (*option == '\0') + free(option); + } + + fclose(fp); +} + + +int +print_line(char *tab, nis_object *ent, void *udata) +{ + static first = 1; + char *hosts = NULL, *client = NULL, *options = NULL, *info = NULL; + server_list servers; + + server_list_init(&servers); + client = ENTRY_VAL(ent, 0); + if (old_format) { + info = ENTRY_VAL(ent, 1); + if (!parse_info(&servers, info, &hosts, &options)) { + printf( + "Error found while parsing the table information\n"); + printf("Please see manpage.\n"); + exit(1); + } + } else { + hosts = ENTRY_VAL(ent, 2); + options = ENTRY_VAL(ent, 3); + } + + if (!(client && *client && hosts && *hosts)) + return (0); + + if (first) { + fprintf(stdout, "client\t\tinformation\n"); + fprintf(stdout, "------\t\t-----------\n"); + first = 0; + } + fprintf(stdout, "%s\t%s=%s", client, PREF_SRVR, hosts); + if (options && *options) + fprintf(stdout, "\t%s=%s", PREF_TYPE, options); + fprintf(stdout, "\n"); + return (0); +} + + +void +print_servers(server_list *servers, char *domain, char *client) +{ + char *hosts = NULL; + char *options = NULL; + char *info = NULL; + char name[NIS_MAXNAMELEN]; + nis_result *res; + nis_object *obj; + ulong_t flags = FOLLOW_PATH|FOLLOW_LINKS|EXPAND_NAME; + + /* first check to see if table exists */ + sprintf(name, "%s.org_dir.%s", TABLE, domain); + res = nis_lookup(name, flags); + if (res == 0 || res->status != NIS_SUCCESS) + return; + + obj = res->objects.objects_val; + if (obj->TA_data.ta_cols.ta_cols_len == 2) + old_format = 1; /* need old format for back compat */ + else + old_format = 0; + + if (client && *client) { + server_list_init(servers); + /* specific client lookcup */ + if (old_format) + sprintf(name, "[client=%s],%s.org_dir.%s", + client, TABLE, domain); + else + sprintf(name, "[client=%s,attr=%s],%s.org_dir.%s", + client, PREF_SRVR, TABLE, domain); + res = nis_list(name, flags, 0, 0); + if (res == 0) { + fprintf(stderr, "nis_list failed\n"); + nis_perror(NIS_NOMEMORY, name); + exit(1); + } + if (res->status == NIS_NOTFOUND) { + return; + } else if (res->status != NIS_SUCCESS) { + nis_perror(res->status, name); + exit(1); + } + + obj = res->objects.objects_val; + client = ENTRY_VAL(obj, 0); + if (old_format) { + info = ENTRY_VAL(obj, 1); + if (!parse_info(servers, info, &hosts, &options)) { + printf( + "Error found while parsing the table information\n"); + printf("Please see manpage.\n"); + exit(1); + } + } else { + hosts = ENTRY_VAL(obj, 2); + options = ENTRY_VAL(obj, 3); + } + + if (client == NULL) + client = ""; + if (hosts == NULL) + hosts = ""; + if (options == NULL) + options = ""; + + print_info(client, hosts, options); + return; + } + + /* list all entries */ + sprintf(name, "%s.org_dir.%s", TABLE, domain); + + res = nis_list(name, flags, print_line, 0); + if ((res->status != NIS_CBRESULTS) && (res->status != NIS_NOTFOUND)) { + nis_perror(res->status, "can't list table"); + exit(1); + } + +} + +/* + * This routine sends a signal to the local Cache manager to flush the + * internal server list. + * Return values: + * 0: signal sent sucessfully + * 1: signal failed + */ +int +flush_cache(char *prog) +{ + + char *cmd = "kill -HUP `ps -e |" + " awk '{if ($4 == \"nis_cach\") printf(\"%d \", $1);}'`" + " 2> /dev/null"; + + if (system(cmd) != 0) { + /* error or nis_cachemgr not running */ + fprintf(stderr, + "%s: Unable to flush the cache manager.", prog); + fprintf(stderr, + "\tThe cache manager is possibly down."); + return (1); + } + return (0); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisrestore.cc b/usr/src/cmd/rpcsvc/nis/utils/nisrestore.cc new file mode 100644 index 0000000000..a95fb6cba3 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisrestore.cc @@ -0,0 +1,1301 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisrestore.cc + * + * Copyright (c) 1988-1998 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisrestore.cc + * + * A NIS+ utility for restoring archived databases, as well as an + * out-of-band resync mechanism. + */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <unistd.h> +#include <signal.h> +#include <wait.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <sys/stat.h> +#include <dirent.h> +#include <errno.h> +#include <rpcsvc/nis_db.h> +#include <rpcsvc/nis.h> +#include "nis_bkrst.h" +#include "../rpc.nisd/log.h" +#include "../rpc.nisd/nis_proc.h" + +/* + * Global state variables. + */ +static bool_t force = FALSE; +static bool_t aflg = FALSE; +static bool_t tocflg = FALSE; +static bool_t olddata = TRUE; +static bool_t verbiage = FALSE; +static nis_server *srv_ptr = NULL; +static char *syncfile = "/var/nis/nisrestore.pid"; +static char *tempdir = "/var/nis/.nisrestore"; +static int fd_sync; + +static void abort_handler(int); + +/* + * Extern variables related to the trans.log header. + */ +extern log_hdr *__nis_log; +extern int __nis_logfd; +extern unsigned long __maxloglen, __loghiwater, __nis_logsize, __nis_filesize; +extern "C" char *relative_name(char *); + +/* + * We are masquerading as the master server, so we can create and write + * to a temporary transaction file. This will get us around the + * CHILDPROC check. + */ +extern pid_t master_pid; + +/* + * This restoration utility assumes the backup was performed by the nisbackup + * utility and not via some other tools. Using nisbackup will validate the + * following assumptions being made during the restoration sequence: + * + * - the backup directory is organized on a per-directory basis. + * - there are no log files. No table logs and no dictionary log. + * (the backup utility _simulates_ a checkpoint) + * - the dictionary file has entries only for the table files relevant + * to the directory being restored. In other words, the complete + * dictionary file is merged without validating each entry. + * - the transaction log has only entry and that is the UPD_TIME_STAMP + * entry for the directory being restored. + * + */ + +/* + * Needs to be cleaned up in nislog as well as here. nis_log_common.o + * is shared by rpc.nisd as well as nislog and nisbackup and rpc.nisd + * source already defines this. + */ +int +abort_transaction(int xid) +{ + xid = 0; + return (0); +} + +static void +init_signals() +{ + sigset(SIGHUP, abort_handler); + sigset(SIGINT, abort_handler); + sigset(SIGTERM, abort_handler); +} + +static void +abort_handler(int sig) +{ + sig = 0; + exit(1); +} + +/* + * clean_up() called atexit() + */ +static void +clean_up() +{ + close(fd_sync); + unlink(syncfile); +} + +void +usage() +{ + fprintf(stderr, "usage: nisrestore [-fv] backup-dir directory...\n"); + fprintf(stderr, " nisrestore [-fv] -a backup-dir\n"); + fprintf(stderr, " nisrestore -t backup-dir\n"); + exit(1); +} + +/* + * This routine vforks "rm -r" to remove a non-empty directory, for cleanup. + */ +int +_rm_dir(char * name) +{ + struct stat s; + pid_t child; + siginfo_t si; + + if (stat(name, &s) == -1) { + fprintf(stderr, + "nisbackup: directory %s does not exist.", name); + return (1); + } + switch (child = vfork()) { + case -1: /* error */ + fprintf(stderr, "Cannot remove files in %s", name); + return (1); + case 0: /* child */ + if (execl("/bin/rm", "rm", + "-r", + name, + NULL) < 0) + _exit(1); + _exit(0); /* Since this is the child proc, we'll exit */ + default: /* parent, we'll wait for the rm to complete */ + if (waitid(P_PID, child, &si, WEXITED) == -1) { + fprintf(stderr, "Cannot remove files in %s", name); + return (1); + } else { + if (si.si_status != 0) { + fprintf(stderr, + "Can't remove files: error = %d", + si.si_status); + return (1); + } + } + } + return (0); +} + +/* + * This routine takes the old name of the data file, a token to search for, + * and the replacement string to replace the token with. This will change + * names like org_dir.foo.bar to org_dir.foo (tok=.foo.bar, repl=.foo). + */ +char * +change_name(char *filename, char *tok, char *repl) +{ + char *newname; + char *loc_end, *loc_beg; + char *s; + + newname = (char *) calloc(1, sizeof (char) * strlen(filename) + + strlen(repl) - strlen(tok) + 1); + if (!newname) { + fprintf(stderr, "Unable to allocate memory.\n"); + return (NULL); + } + if (strlen(tok) == 0) { + strcpy(newname, filename); + strcat(newname, repl); + } else { + if (s = strstr(filename, tok)) { + int sublen = s - filename; + memcpy(newname, filename, sublen); + strcat(newname, repl); + } else { + free(newname); + return (NULL); + } + } + return (newname); +} + +/* + * This routine checks the data file names in the backup with their relative + * name on this server. If the master server that generated this backup lived + * in a different domain than this (replica) server, the files will need to + * be renamed and their names changed in the data dictionary. + */ +bool_t +files_need_renaming(char * full_obj_name, char * backup_data_dir, + char ** oldsuf, char ** newsuf) +{ + + char buf[BUFSIZ]; + struct stat st; + char *domainof; + DIR *dirptr; + struct dirent *dent; + char *p; + + *oldsuf = NULL; + *newsuf = NULL; + /* + * If we're restoring a root domain server, no renaming. + */ + sprintf(buf, "%s/%s", backup_data_dir, ROOT_OBJ); + if (stat(buf, &st) == 0) { + return (FALSE); + } + /* + * The only way the localized name of the object is NULL is by + * using the -f(orce) option on a machine that's in the wrong + * domain for this object. All bets are off with the -f option. Punt. + */ + if (nis_name_of(full_obj_name) == NULL) + return (FALSE); + + /* + * If localized name of directory exist in backup dir, no renaming. + */ + p = relative_name(full_obj_name); + sprintf(buf, "%s/%s", backup_data_dir, p); + if (stat(buf, &st) == 0) { + free((void *)p); + return (FALSE); + } + + if ((dirptr = opendir(backup_data_dir)) == NULL) { + fprintf(stderr, "Unable to open %s: %m", backup_data_dir); + exit(1); + } + while ((dent = readdir(dirptr)) != NULL) { + if (strcmp(dent->d_name, ".") == 0) + continue; /* Ignore */ + if (strcmp(dent->d_name, "..") == 0) + continue; /* Ignore */ + + /* + * Extrace the domain part of the entry, which will be the old suffix. + * Then get the domain part of the entry as it would be stored on this + * server (new suffix). + */ + if (strcmp(nis_domain_of(dent->d_name), ".") == 0) { + *oldsuf = strdup(""); + } else { + sprintf(buf, ".%s", nis_domain_of(dent->d_name)); + *oldsuf = strdup(buf); + } + + if (strcmp(nis_domain_of(p), ".") == 0) { + *newsuf = strdup(""); + } else { + sprintf(buf, ".%s", nis_domain_of(p)); + *newsuf = strdup(buf); + } + break; + } + return (TRUE); +} + +bool_t +rename_data_files(char * oldsuf, char * newsuf) +{ + + char buf[BUFSIZ]; + char oldname[BUFSIZ]; + char newname[BUFSIZ]; + char *newtablename; + char dotlog[BUFSIZ]; + DIR *dirptr; + struct dirent *dent; + + sprintf(buf, "%s/data", tempdir); + if ((dirptr = opendir(buf)) == NULL) { + fprintf(stderr, "Unable to open %s: %m", buf); + return (FALSE); + } + while ((dent = readdir(dirptr)) != NULL) { + if (strcmp(dent->d_name, ".") == 0) + continue; /* Ignore */ + if (strcmp(dent->d_name, "..") == 0) + continue; /* Ignore */ + /* + * Take the leaf name of the table, append the new suffix, then rename + * the file. Attempt to unlink any .log file that may still be present. + */ + if ((newtablename = change_name(dent->d_name, oldsuf, newsuf)) + == FALSE) { + fprintf(stderr, "Unable to rename data files\n"); + return (FALSE); + } + sprintf(newname, "/var/nis/data/%s", newtablename); + sprintf(oldname, "%s/%s", buf, dent->d_name); + rename(oldname, newname); + sprintf(dotlog, "%s.log", newname); + unlink(dotlog); + } + /* The temporary directory should be empty now, so we can remove it. */ + if (_rm_dir(tempdir)) { + fprintf(stderr, "Unable to remove directory:%s", tempdir); + return (FALSE); + } + + return (TRUE); +} + +/* + * bool_t is_server_of() + * + * Boolean function to check if local host is a server for the object + * passed in nis_obj. If the passed in object is not fully qualified, then + * nis_getnames() is called to resolve the object's full name. The parameter, + * full_name, is used to return the fully qualified name (ignored if set to + * NULL). The nis_server struct is returned in *srv. + */ +bool_t +is_server_of(char *nis_obj, char *full_name, nis_server **srv) +{ + + char myname[NIS_MAXNAMELEN]; + nis_name *namelist; + nis_error err; + int i; + directory_obj d_obj; + + /* + * We will use __nis_CacheBind instead of nis_lookup() so that we + * can take advantage of the NIS_SHARED_DIRCACHE. + */ + if (nis_obj[strlen(nis_obj) - 1] != '.') { /* Partially qualified */ + namelist = nis_getnames(nis_obj); + for (i = 0; namelist[i]; i++) { +#ifdef DEBUG +printf("name: %s\n", namelist[i]); +#endif + err = __nis_CacheBind(namelist[i], &d_obj); + if (err == NIS_SUCCESS) { + if (full_name) + sprintf(full_name, "%s", namelist[i]); + break; + } + } + } else { /* Fully qualified */ + err = __nis_CacheBind(nis_obj, &d_obj); + if (err == NIS_SUCCESS) + if (full_name) + sprintf(full_name, "%s", nis_obj); + } + if (err != NIS_SUCCESS) { + fprintf(stderr, "Unable to lookup %s\n", nis_obj); +#ifdef DEBUG +fprintf(stderr, "Error code=%d\n", err); +#endif + return (0); + } + /* + * If we can get a directory object, we check for membership. + */ + if (err == NIS_SUCCESS) { + sprintf(myname, "%s", nis_local_host()); + for (i = 0; i < d_obj.do_servers.do_servers_len; i++) { + if (strcasecmp(myname, + d_obj.do_servers.do_servers_val[i].name) + == 0) { + *srv = &(d_obj.do_servers.do_servers_val[i]); + return (1); + } + } + } + if (!force) + fprintf(stderr, "Not server for %s\n", nis_obj); + return (0); +} + +/* + * This is a link list of the directories being restored on this server. + */ +struct nis_dir_list { + char *name; + uint32_t upd_time; + struct nis_dir_list *next; +}; +static struct nis_dir_list *dirlisthead = NULL; +enum CACHE_OP {CACHE_INIT, CACHE_ADD, CACHE_SAVE, CACHE_FIND, CACHE_DUMP}; +/* + * dirobj_list() maintains list of directory objects that are backed up + * with the -a(ll) option of the nisbackup utility. + */ +struct nis_dir_list * +dirobj_list(enum CACHE_OP op, char *argp) +{ + char buf[BUFSIZ]; + FILE *fr, *fw; + char *name, *end; + int ss; + struct nis_dir_list *tmp = NULL; + struct stat st; + + switch (op) { + case CACHE_INIT: + /* Initialize cache with the file name passed in argp */ + fr = fopen(argp, "r"); + if (fr == NULL) { + return ((struct nis_dir_list *) NULL); + } + while (fgets(buf, BUFSIZ, fr)) { + name = buf; + while (isspace(*name)) + name++; + end = name; + while (!isspace(*end)) + end++; + *end = NULL; + tmp = (struct nis_dir_list *) + XMALLOC(sizeof (struct nis_dir_list)); + if (tmp == NULL) { + fclose(fr); + return ((struct nis_dir_list *) NULL); + } + if ((tmp->name = strdup(name)) == NULL) { + free(tmp); + fclose(fr); + return ((struct nis_dir_list *) NULL); + } + tmp->next = dirlisthead; + dirlisthead = tmp; + } + fclose(fr); + return (dirlisthead); + + case CACHE_ADD: + if (argp == NULL) + return ((struct nis_dir_list *) NULL); + /* Check whether already in cache */ + for (tmp = dirlisthead; tmp; tmp = tmp->next) + if (strcasecmp(tmp->name, (char *)argp) == 0) + return (tmp); + + /* Add it */ + tmp = (struct nis_dir_list *) + malloc(sizeof (struct nis_dir_list)); + if (tmp == NULL) { + return ((struct nis_dir_list *) NULL); + } + if ((tmp->name = strdup((char *)argp)) == NULL) { + free(tmp); + return ((struct nis_dir_list *) NULL); + } + tmp->next = dirlisthead; + dirlisthead = tmp; +#ifdef DEBUG + fprintf(stderr, "objdir_list: added : %s\n", tmp->name); +#endif + return (tmp); + + case CACHE_SAVE: /* Save back'd up objects to a file */ + if (argp == NULL) + return (NULL); + fw = fopen(argp, "w"); + if (fw == NULL) { + ss = stat(argp, &st); + if (ss == -1 && errno == ENOENT) { + fw = fopen(argp, "w+"); + } + if (fw == NULL) { + fprintf(stderr, + "Could not open file %s for updating\n", + argp); + return ((struct nis_dir_list *) NULL); + } + } + for (tmp = dirlisthead; tmp; tmp = tmp->next) + fprintf(fw, "%s\n", (char *)tmp->name); + if (fclose(fw) == EOF) + return (NULL); + return (dirlisthead); + + case CACHE_FIND: /* Search for object in cache */ + if (argp == NULL) + return (NULL); + for (tmp = dirlisthead; tmp; tmp = tmp->next) + if (strcasecmp(tmp->name, (char *)argp) == 0) + return (tmp); + return (NULL); + + case CACHE_DUMP: /* Show 'em what we've got */ + for (tmp = dirlisthead; tmp; tmp = tmp->next) + fprintf(stderr, "%s : %d\n", + tmp->name, tmp->upd_time); + return ((struct nis_dir_list *) NULL); + + default: + return ((struct nis_dir_list *) NULL); + } +} + +int +merge_trans_log() +{ + log_upd *cur, *nxt; + u_long addr_p, upd_size; + int error; + int fd, num = 0; + int ret; + char backup[1024]; + char buf[NIS_MAXNAMELEN]; + ulong last_xid = 0; + struct nis_dir_list *tmp = NULL; + + if (verbiage) + fprintf(stderr, "Merging the transaction log.\n"); + + /* + * Map in transaction log file. + */ + sprintf(buf, "%s", LOG_FILE); + if (map_log(buf, FNISD)) { + fprintf(stderr, + "Unable to map transaction log : %s\n", buf); + return (0); + } + + if (__nis_log->lh_state != LOG_STABLE) { + fprintf(stderr, + "Unable to merge transaction log, log unstable.\n"); + return (0); + } + + strcpy(backup, nis_data(BACKUP_LOG)); + + fd = open(backup, O_WRONLY+O_SYNC+O_CREAT+O_TRUNC, 0600); + if (fd == -1) { + fprintf(stderr, + "Unable to open backup log (%s).\n", backup); + return (0); + } + + /* + * Make a backup of the log in two steps, write the size of the log + * and then a copy of the log. + */ + __nis_logsize = (__nis_log->lh_tail) ? LOG_SIZE(__nis_log) : + sizeof (log_hdr); + if (write(fd, &__nis_logsize, sizeof (long)) != sizeof (long)) { + fprintf(stderr, + "Unable to merge transaction log, disk full.\n"); + close(fd); + unlink(backup); + return (0); + } + /* + * Now set the state to RESYNC so if we have to recover and read + * this back in, and we get screwed while reading it, we won't + * get into major trouble. This still leaves open one window : + * a) Start checkpoint + * b) Successfully backup to BACKUP + * c) Set log state to CHECKPOINT + * c) crash. + * d) Reboot and start reading in the backup log + * e) crash. + * f) reboot and now the log appears to be resync'ing without + * all of its data. + */ + __nis_log->lh_state = LOG_RESYNC; + if (write(fd, __nis_log, (size_t) __nis_logsize) != __nis_logsize) { + fprintf(stderr, + "Unable to merge transaction log, disk full.\n"); + close(fd); + unlink(backup); + __nis_log->lh_state = LOG_STABLE; + sync_header(); + return (0); + } + close(fd); + + /* If we crash here we're ok since the log hasn't changed. */ + __nis_log->lh_state = LOG_CHECKPOINT; + sync_header(); + + addr_p = (u_long)(__nis_log->lh_head); + for (cur = __nis_log->lh_head, num = 0; cur; cur = nxt) { + nxt = cur->lu_next; + for (tmp = dirlisthead; tmp; tmp = tmp->next) { + if (nis_dir_cmp(cur->lu_dirname, tmp->name) == + SAME_NAME) + break; + } + if (tmp == NULL) { + upd_size = XID_SIZE(cur); + memmove((char *)addr_p, (char *)cur, (size_t) upd_size); + last_xid = cur->lu_xid; + addr_p += upd_size; + num++; + } + } + + if (num == 0) { + /* Deleted all of the entries. */ +#ifdef DEBUG + fprintf(stderr, + "merge_trans_log: all entries removed.\n"); +#endif + __nis_log->lh_head = NULL; + __nis_log->lh_tail = NULL; + __nis_log->lh_num = 0; + sync_header(); + ret = ftruncate(__nis_logfd, FILE_BLK_SZ); + if (ret == -1) { + fprintf(stderr, + "Cannot truncate transaction log file\n"); + return (0); + } + __nis_filesize = FILE_BLK_SZ; + ret = (int)lseek(__nis_logfd, __nis_filesize, SEEK_SET); + if (ret == -1) { + fprintf(stderr, + "merge_trans_log: cannot increase transaction log file size\n"); + return (0); + } + ret = (int) write(__nis_logfd, "+", 1); + if (ret != 1) { + fprintf(stderr, + "cannot write one character to transaction log file\n"); + return (0); + } + __nis_logsize = sizeof (log_hdr); + if (msync((caddr_t)__nis_log, + (size_t) __nis_logsize, MS_SYNC)) { + perror("msync:"); + fprintf(stderr, "unable to mysnc() LOG\n"); + return (0); + } + + } else { +#ifdef DEBUG + fprintf(stderr, + "merge_trans_log: some entries removed.\n"); +#endif + __nis_log->lh_xid = last_xid; + __nis_log->lh_num = num; + sync_header(); + error = __log_resync(__nis_log, FCHKPT); + if (error) { + fprintf(stderr, + "Transaction log merge failed, unable to resync.\n"); + return (0); + } + } + + /* + * Write back "last update" stamps for all of the directories + * we're restoring. + */ + __nis_log->lh_state = LOG_STABLE; + sync_header(); + for (tmp = dirlisthead; tmp; tmp = tmp->next) + make_stamp(tmp->name, (u_long) tmp->upd_time); + unlink(backup); + return (1); +} + +int +copydir(char *src, bool_t rename_files) +{ + char dest[1024]; + struct stat s; + pid_t child; + siginfo_t si; + + if (stat(src, &s) == -1) { + fprintf(stderr, "Directory %s does not exist.\n", src); + exit(1); + } + if (rename_files) { + /* + * We need to rename each file! We'll copy them into the temporary + * directory, to be renamed into the /var/nis/data directory in + * rename_data_files(). + */ + strcpy(dest, tempdir); + if (stat(dest, &s) == -1) { + if (errno == ENOENT) { + if (mkdir(dest, 0700)) { + fprintf(stderr, + "Unable to create NIS+ directory %s\n", + dest); + exit(1); + } + } else { + fprintf(stderr, + "Unable to stat NIS+ directory %s.\n", dest); + exit(1); + } + } else { + _rm_dir(dest); + if (mkdir(dest, 0700)) { + fprintf(stderr, + "Unable to create NIS+ directory %s\n", + dest); + exit(1); + } + } + } else { + strcpy(dest, "/var/nis"); + } + + switch (child = vfork()) { + case -1: /* error */ + fprintf(stderr, "Can't copy backed up files\n"); + exit(1); + case 0: /* child */ + if (execl("/bin/cp", "cp", "-r", src, dest, NULL) < 0) + _exit(1); + _exit(0); + default: /* parent, we'll wait for the cp to complete */ + if (waitid(P_PID, child, &si, WEXITED) == -1) { + fprintf(stderr, "Can't copy backed up files\n"); + exit(1); + } else { + if (si.si_status != 0) { + fprintf(stderr, + "Can't copy backed up files: error = %d\n", + si.si_status); + exit(1); + } + } + } + return (1); +} + +void +sanity_checks(char *backupdir) +{ + struct stat s; + char buf[NIS_MAXNAMELEN]; + char backup_path[NIS_MAXNAMELEN]; + char trans_log[NIS_MAXNAMELEN]; + struct nis_dir_list *tmp = NULL; + nis_tag *res = NULL; + nis_error error = NIS_SUCCESS; + nis_tag tagctl; + unsigned char u_nl[16]; + uint32_t *unl_p; + FILE *fr = NULL; + char pid[16]; + int fd_s; +#ifdef DEBUG + struct timeval ctime; + int elapse; +#endif + + /* + * Create a synchronization file, to prevent multiple invocations + * of nisrestore from running simultaneously. + */ + if ((fd_sync = open(syncfile, O_RDWR | O_CREAT, 0644)) == -1) { + fprintf(stderr, + "Unable to create pid file: %s\n", syncfile); + exit(1); + } + /* + * Lock without blocking. If we can't get it, another nisrestore + * must have it. + */ + lseek(fd_sync, 0L, SEEK_SET); + if ((fd_s = lockf(fd_sync, F_TLOCK, 1)) == -1) { + close(fd_sync); + fprintf(stderr, "Unable to lock pid file: %s\n", syncfile); + fprintf(stderr, "Is nisrestore already running?\n"); + exit(1); + } + /* + * We'll write our pid to the file, for good measure. Leave the + * fd_sync open for the duration of the restore. + */ + sprintf(pid, "%d\n", getpid()); + if (write(fd_sync, pid, strlen(pid) + 1) < 0) { + close(fd_sync); + fprintf(stderr, "Cannot write pid file: %s\n", syncfile); + exit(1); + } + + /* + * Catch signals and clean up files before exit. + */ + init_signals(); + + /* + * Clean up any files before we exit. + */ + atexit(clean_up); + + if (*backupdir != '/') { + fprintf(stderr, + "Backup directory %s is not a absolute path name.\n", + backupdir); + exit(1); + } + + /* + * Check if backup directory exists. + */ + if (stat(backupdir, &s) == -1) { + fprintf(stderr, + "Backup directory %s does not exist.\n", + backupdir); + exit(1); + } + + if (aflg) { + sprintf(buf, "%s/%s", backupdir, BACKUPLIST); + if (dirobj_list(CACHE_INIT, buf) == NULL) { + fprintf(stderr, + "Unable to access backup list : %s\n", buf); + exit(1); + } + /* + * Sanity check: make sure that we serve the directory object(s) + * listed in the backup_list file. Note: backup_list is created by + * nisbackup(1), so we know the objects are fully qualified. + */ + for (tmp = dirlisthead; tmp; tmp = tmp->next) { + if (!is_server_of(tmp->name, NULL, &srv_ptr) && + !force) { + fprintf(stderr, "Use -f option to override.\n"); + exit(1); + } + + } + } + + /* + * Check if the NIS+ server is running. Report an error if it is. + * If srv_ptr is not set, they must be using the -f(orce), so all + * bets are off. Punt + */ + if (srv_ptr) { + tagctl.tag_val = ""; + tagctl.tag_type = 0; +#ifdef DEBUG + fprintf(stderr, "Server name: %s\n", srv_ptr->name); + gettimeofday(&ctime, 0); + elapse = ctime.tv_sec; +#endif + if (verbiage) + fprintf(stderr, + "Verifying rpc.nisd process is not running\n"); + error = nis_servstate(srv_ptr, &tagctl, 1, &res); + if (error == NIS_SUCCESS) { + fprintf(stderr, + "rpc.nisd is active, kill server before running nisrestore\n"); + exit(1); + } +#ifdef DEBUG + gettimeofday(&ctime, 0); + fprintf(stderr, "Server access: %d seconds\n", + ctime.tv_sec - elapse); +#endif + if (res != NULL) + nis_freetags(res, 1); + } +#ifdef DEBUG + else + fprintf(stderr, "No Server found.\n"); +#endif + /* + * Check if backup directory(ies) exist and are valid. + */ + for (tmp = dirlisthead; tmp; tmp = tmp->next) { + + sprintf(backup_path, "%s/%s/%s", backupdir, tmp->name, NIS_DIR); + /* + * Check for the "data" subdirectory in the backup. + */ + if (stat(backup_path, &s) == -1) { + fprintf(stderr, + "%s not found.\n", backup_path); + exit(1); + } + /* + * Check for the existence of a dictionary file in the backup-dir. + */ + sprintf(buf, "%s.dict", backup_path); + if (stat(buf, &s) == -1) { + fprintf(stderr, + "Dictionary file %s not found.\n", buf); + exit(1); + } + + /* + * Check for the last update transaction file in the backup-dir. + */ + sprintf(trans_log, "%s/%s/%s", backupdir, tmp->name, + LASTUPDATE); + if (stat(trans_log, &s) == -1) { + fprintf(stderr, "Transaction log %s not found in.\n", + trans_log); + exit(1); + } + /* + * Read in the last update file, and cache the time stamp. + */ + if ((fr = fopen(trans_log, "r")) == NULL) { + fprintf(stderr, "Error accessing file %s: %d\n", + trans_log, errno); + exit(1); + } + /* + * Read in the backup revision number, and verify. + */ + fread((void *) u_nl, sizeof (unsigned long), 1, fr); + unl_p = (uint32_t *) u_nl; + if (ntohl(*unl_p) != BACKUPREV) { + fprintf(stderr, + "Invalid backup version #%X, should be #%X\n", + ntohl(*unl_p), BACKUPREV); + exit(1); + } + + fread((void *) u_nl, sizeof (uint32_t), 1, fr); + unl_p = (uint32_t *) u_nl; + tmp->upd_time = ntohl(*unl_p); +#ifdef DEBUG +fprintf(stderr, "sanity_checks: update time: %d\n", tmp->upd_time); +#endif + if (fclose(fr) == EOF) { + fprintf(stderr, "Error closing file\n"); + exit(1); + } + } + /* + * Check for the local "data" directory existing. If not, create it. + */ + sprintf(buf, "%s", nis_data(NULL)); + if (stat(buf, &s) == -1) { + if (errno == ENOENT) { + sprintf(buf, nis_data(NULL)); + if (mkdir(buf, 0700)) { + fprintf(stderr, + "Unable to create NIS+ directory %s\n", buf); + exit(1); + } + olddata = FALSE; + } else { + fprintf(stderr, + "Unable to stat NIS+ directory %s.\n", buf); + exit(1); + } + } + strcat(buf, ".dict"); + if (!db_initialize(buf)) { + fprintf(stderr, + "Unable to initialize data dictionary %s.", buf); + exit(1); + } +} + +bool_t +merge_serving_list() +{ + char buf[BUFSIZ]; + char filename[BUFSIZ]; + FILE *fr; + char *name, *end; + int ss; + struct stat st; + + strcpy(filename, nis_data("serving_list")); + ss = stat(filename, &st); + if (ss == -1 && errno == ENOENT) { + /* + * If it doesn't exist, CACHE_SAVE will create it. + */ + return ((dirobj_list(CACHE_SAVE, filename) == dirlisthead)); + } else { + fr = fopen(filename, "r"); + if (fr == NULL) + return (FALSE); + while (fgets(buf, BUFSIZ, fr)) { + name = buf; + while (isspace(*name)) + name++; + end = name; + while (!isspace(*end)) + end++; + *end = NULL; + if (dirobj_list(CACHE_ADD, name) == NULL) { + fclose(fr); + return (FALSE); + } + } + fclose(fr); + return ((dirobj_list(CACHE_SAVE, filename) == dirlisthead)); + } +} + +/* + * rm_logs() + * + * This routine checks for each database file restored, if a .log file + * exists. The .log files are removed from the /var/nis/data directory, + * as they will be invalid after the restore is performed. As an optimization, + * a flag is set in sanity_checks that indicates if the /var/nis/data + * directory has been created (new install) and therefore this routine is + * unnecessary. + */ +bool_t +rm_logs(char *src) +{ + char localdir[NIS_MAXNAMELEN]; + char buf[NIS_MAXNAMELEN]; + char full_name[NIS_MAXNAMELEN]; + char *domainof; + DIR *dirptr; + struct dirent *dent; + struct stat st; + +#ifdef DEBUG + printf("rm_logs: entry point\n"); +#endif + sprintf(localdir, "%s", nis_data(NULL)); + if ((dirptr = opendir(src)) == NULL) { + fprintf(stderr, "Unable to open %s.\n", src); + return (FALSE); + } + + while ((dent = readdir(dirptr)) != NULL) { + if (strcmp(dent->d_name, ".") == 0) + continue; /* Ignore */ + if (strcmp(dent->d_name, "..") == 0) + continue; /* Ignore */ + if (strcmp(dent->d_name, ROOT_OBJ) == 0) + continue; /* root.object doesn't have log file */ + + sprintf(buf, "%s/%s.log", localdir, dent->d_name); + sprintf(full_name, "%s.%s", dent->d_name, + nis_local_directory()); +#ifdef DEBUG + printf("rm_logs: stat'ng: %s\n", buf); +#endif + if (stat(buf, &st) == -1) + if (errno == ENOENT) + continue; +/* + * Determine if this .log file belongs to a directory object that has been + * restored. + */ + if (!dirobj_list(CACHE_FIND, full_name)) { + domainof = nis_domain_of(full_name); + if (!dirobj_list(CACHE_FIND, domainof)) + continue; + } +/* + * It's a match, delete it! + */ + if (unlink(buf)) { + fprintf(stderr, "Unable to remove %s.\n", buf); + return (FALSE); + } + } /* while (readdir) */ + closedir(dirptr); + return (TRUE); +} + + +bool_t +print_backuplist(char *backupdir) + +{ + struct stat s; + char buf[NIS_MAXNAMELEN]; + struct nis_dir_list *dl = NULL; + + if (*backupdir != '/') { + fprintf(stderr, + "Backup directory %s is not a absolute path name.\n", + backupdir); + return (FALSE); + } + + /* + * Check if backup directory exists. + */ + if (stat(backupdir, &s) == -1) { + fprintf(stderr, "Backup directory %s does not exist.\n", + backupdir); + return (FALSE); + } + + sprintf(buf, "%s/%s", backupdir, BACKUPLIST); + if (dirobj_list(CACHE_INIT, buf) == NULL) { + fprintf(stderr, "Unable to access backup list : %s\n", buf); + return (FALSE); + } + for (dl = dirlisthead; dl; dl = dl->next) + fprintf(stdout, "%s\n", dl->name); + return (TRUE); +} + +int +main(int argc, char *argv[]) +{ + char *backupdir; + char nisdomain[NIS_MAXNAMELEN]; + char newdict[NIS_MAXNAMELEN]; + char buf[NIS_MAXNAMELEN]; + int c; + db_status dbstat; + char *oldsuf = NULL; + char *newsuf = NULL; + char *tmp; + struct nis_dir_list *dl = NULL; + bool_t rename_files; + + if (geteuid() != 0) { + fprintf(stderr, "You must be root to run nisrestore.\n"); + exit(1); + } + if (argc < 3) + usage(); + + while ((c = getopt(argc, argv, "aftv")) != -1) { + switch (c) { + case 'a' : + aflg = TRUE; + break; + case 'f' : + force = TRUE; + break; + case 't' : + tocflg = TRUE; + break; + case 'v' : + verbiage = TRUE; + break; + default: + usage(); + } + } + if (aflg || tocflg) { + if (argc - optind != 1) + usage(); + backupdir = argv[optind++]; + } else { + if (argc - optind < 2) + usage(); + backupdir = argv[optind++]; + /* + * Yes, I really mean "=", not "==". tmp is assigned in while. + */ + while (tmp = argv[optind++]) { + memset(nisdomain, 0, sizeof (nisdomain)); + if (!is_server_of(tmp, nisdomain, &srv_ptr) && + !force) { /* Not server for object specd */ + fprintf(stderr, "Use -f option to override.\n"); + exit(1); + } else { + if (dirobj_list(CACHE_ADD, + ((strlen(nisdomain)) ? nisdomain:tmp)) + == NULL) { + fprintf(stderr, + "Internal error, unable to add to cache\n"); + exit(1); + } + } + } + } + + if (tocflg) { + if (!print_backuplist(backupdir)) { + fprintf(stderr, + "Could not list objects in backup directory %s\n", + backupdir); + exit(1); + } + exit(0); + } + master_pid = getpid(); + sanity_checks(backupdir); +/* + * Loop through dirobj_list, merge in dictionary entries. After looping + * through, close out the data dictionary, merge_trans_log(), update + * the serving_list and were done! + */ + for (dl = dirlisthead; dl; dl = dl->next) { + sprintf(newdict, "%s/%s/%s.dict", backupdir, dl->name, NIS_DIR); + /* + * Now, copy the contents of the backup directory to our local + * directory. Overwrite any local files if they exist. First we + * check to see if the files need to be renamed. If the master + * lives in a different domain than the server being restored, + * the database files have different names. + */ + if (verbiage) + fprintf(stderr, "Restoring %s\n", dl->name); + sprintf(buf, "%s/%s/%s", backupdir, dl->name, NIS_DIR); + rename_files = files_need_renaming(dl->name, buf, &oldsuf, + &newsuf); + if (!copydir(buf, rename_files)) { + fprintf(stderr, + "Unable to copy file from backup directory %s.\n", buf); + db_abort_merge_dict(); + exit(1); + } + if ((rename_files) && (rename_data_files(oldsuf, newsuf) + == FALSE)) { + fprintf(stderr, + "Unable to move data files into place.\n"); + db_abort_merge_dict(); + exit(1); + } + if (verbiage) + fprintf(stderr, + "Adding the %s objects into the data dictionary.\n", + dl->name); + if ((dbstat = db_begin_merge_dict(newdict, oldsuf, newsuf)) + != DB_SUCCESS) { + fprintf(stderr, "Unable to merge dictionary %s: %s.\n", + newdict, db_perror(dbstat)); + db_abort_merge_dict(); + exit(1); + } + /* + * Since the backup doesn't have any .log files, any reminant + * .log's need to be removed. This is taken care of by + * rename_data_files() if rename_files is TRUE. + */ + if ((olddata) && (!rename_files) && (!rm_logs(buf))) { + fprintf(stderr, "Error from rm_logs.\n"); + db_abort_merge_dict(); + exit(1); + } + free(oldsuf); + free(newsuf); + } + /* + * Now closeout the data dictionary. + */ + if ((dbstat = db_end_merge_dict()) != DB_SUCCESS) { + fprintf(stderr, "Error from db_end_merge_dict: %d\n", + dbstat); + db_abort_merge_dict(); + exit(1); + } + if (verbiage) + fprintf(stderr, "Updating the trans.log file.\n"); + if (!merge_trans_log()) { + fprintf(stderr, + "Unable to merge backed up transaction log.\n"); + exit(1); + } + if (!merge_serving_list()) { + fprintf(stderr, "Error from merge_serving_list.\n"); + db_abort_merge_dict(); + exit(1); + } + exit(0); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisrm.c b/usr/src/cmd/rpcsvc/nis/utils/nisrm.c new file mode 100644 index 0000000000..22864ac6e5 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisrm.c @@ -0,0 +1,166 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nisrm.c + * + * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisrm.c + * + * nis+ object removal utility + */ + +#include <stdio.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <strings.h> +#include <stdlib.h> +#include <ctype.h> + +extern nis_object nis_default_obj; + +extern char *nisname_index(); + + +void +usage() +{ + fprintf(stderr, "usage: nisrm [-if] name ...\n"); + exit(1); +} + +void +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + char ask_remove = 0, force_remove = 0; + ulong_t expand; + char *name; + int error = 0; + int bad_name; + nis_result *res, *rres; + nis_object *obj; + char fname[NIS_MAXNAMELEN], buf[BUFSIZ]; + + while ((c = getopt(argc, argv, "if")) != -1) { + switch (c) { + case 'i': + ask_remove = 1; + break; + case 'f' : + force_remove = 1; + break; + default : + usage(); + } + } + + if (optind == argc) + usage(); + + while (optind < argc) { + name = argv[optind++]; + + if (name[strlen(name)-1] != '.') + expand = EXPAND_NAME; + else + expand = 0; + + /* + * Get the object to remove. + */ + res = nis_lookup(name, expand|MASTER_ONLY); + if (res->status != NIS_SUCCESS) { + if (!force_remove) { + nis_perror(res->status, name); + error = 1; + } + goto loop; + } + + bad_name = (snprintf(fname, sizeof (fname), "%s.", + res->objects.objects_val[0].zo_name) + >= sizeof (fname)); + if (!bad_name && + *(res->objects.objects_val[0].zo_domain) != '.') + bad_name = (strlcat(fname, + res->objects.objects_val[0].zo_domain, + sizeof (fname)) >= sizeof (fname)); + + if (bad_name) { + if (!force_remove) { + nis_perror(NIS_BADNAME, name); + error = 1; + } + goto loop; + } + + if (res->objects.objects_val[0].zo_data.zo_type == + NIS_DIRECTORY_OBJ) { + if (!force_remove) { + fprintf(stderr, "\"%s\" is a directory!\n", + fname); + error = 1; + } + goto loop; + } + + if (ask_remove || expand) { + printf("remove %s? ", fname); + *buf = '\0'; + (void) fgets(buf, sizeof (buf), stdin); + if (tolower(*buf) != 'y') + goto loop; + } + + obj = res->objects.objects_val; + rres = nis_remove(fname, obj); + if ((rres->status == NIS_PERMISSION) && force_remove) { + obj->zo_access |= 0x08080808; + nis_freeresult(rres); + rres = nis_modify(fname, obj); + if (rres->status == NIS_SUCCESS) { + nis_freeresult(rres); + rres = nis_remove(fname, NULL); + } + } + if (rres->status != NIS_SUCCESS) { + if (!force_remove) { + nis_perror(rres->status, name); + error = 1; + } + } + nis_freeresult(rres); + + loop: + nis_freeresult(res); + } + + exit(error); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisrmdir.c b/usr/src/cmd/rpcsvc/nis/utils/nisrmdir.c new file mode 100644 index 0000000000..698bfb2f78 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nisrmdir.c @@ -0,0 +1,433 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 1988-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nisrmdir.c + * + * nis+ dir remove utility + */ + +#include <stdio.h> +#include <string.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> +#include <netdb.h> +#include <signal.h> +#include <netdir.h> +#include <netconfig.h> +#include <sys/socket.h> +#include <rpcsvc/nis.h> +#include <rpcsvc/nis_dhext.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <unistd.h> + +#define ROOT_OBJ "root.object" +extern nis_name __nis_local_root(); + +char fname[NIS_MAXNAMELEN]; +nis_object *obj; +int nserv; +nis_server *servers; +int errcode = 0; + +static +int +match_host(char *host, char *target) +{ + int len = strlen(host); + + if (strncasecmp(host, target, len) == 0 && + (target[len] == '.' || target[len] == '\0')) + return (1); + + return (0); +} + +void +cleanup_rmdir() +{ + int ns, i; + + /* + * put any servers that weren't removed (non-nil name) back into the + * directory. + */ + for (ns = 0, i = 0; i < nserv; i++) { + if (servers[i].name) { + if (ns == i) + ns++; + else + servers[ns++] = servers[i]; + } + } + + if (ns) { + obj->DI_data.do_servers.do_servers_len = ns; + (void) nis_add(fname, obj); + } + + exit(1); +} + +nis_server sserv; +int sservi; + +void +cleanup_rmslave() +{ + nis_result *res; + + /* + * put the slave back in the directory. + */ + res = nis_lookup(fname, MASTER_ONLY); + if (res->status == NIS_SUCCESS) { + obj->zo_oid = NIS_RES_OBJECT(res)[0].zo_oid; + obj->DI_data.do_servers.do_servers_len++; + servers[sservi] = sserv; + (void) nis_modify(fname, obj); + } + + exit(1); +} + +/* + * remove_directory is a special rmdir that takes care of the root case. + * This is needed only for the -s option when the host or directory object + * specified in the removal no longer exists. Otherwise, + * the nis_remove operation on the directory object would result in + * the replicas receiving pings to remove the root object in the root case. + */ +nis_error +remove_directory(nis_name directory, nis_server* server, int verbose) +{ + nis_error s = nis_rmdir(directory, server); + + if (s != NIS_SUCCESS) { + if (verbose) + fprintf(stderr, "cannot remove replica \"%s\": %s.\n", + server->name, nis_sperrno(s)); + return (s); + } + /* root replica: send ping to remove root object. */ + if (nis_dir_cmp(__nis_local_root(), directory) == SAME_NAME) { + __nis_pingproc(server, ROOT_OBJ, time(0)); + } + + return (s); +} + +#define OP_RMDIR 0 +#define OP_RMSLAVE 2 + +void +usage() +{ + fprintf(stderr, "usage: nisrmdir [-if] [-s hostname] dirname\n"); + exit(1); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + char ask_remove = 0, force_remove = 0; + int op = OP_RMDIR; + ulong_t expand; + char buf[BUFSIZ]; + char *host = 0; + char *name; + nis_result *res, *rres, *mres; + nis_error s; + int i, nur, found; + int bad_name; + nis_server *sservp; + + while ((c = getopt(argc, argv, "ifs:")) != -1) { + switch (c) { + case 'i': + ask_remove = 1; + break; + case 'f': + force_remove = 1; + break; + case 's': + op = OP_RMSLAVE; + host = optarg; + break; + default: + usage(); + } + } + + if (argc - optind != 1) + usage(); + + name = argv[optind]; + + if (name[strlen(name)-1] != '.') + expand = EXPAND_NAME; + else + expand = 0; + + /* + * Get the directory object. + */ + res = nis_lookup(name, expand|MASTER_ONLY); + if (res->status != NIS_SUCCESS) { + if (force_remove) { + /* + * If specifying host, maybe trying to clean up + * directory that has already been removed. + */ + if (host && (res->status == NIS_NOTFOUND || + res->status == NIS_NOSUCHNAME)) { + sservp = __nis_host2nis_server_g(host, + FALSE, TRUE, &errcode); + + if (sservp == NULL) { + nis_perror(errcode, host); + exit(1); + } + + sserv = *sservp; + if (expand == 0) + bad_name = (strlcpy(fname, name, + sizeof (fname)) >= + sizeof (fname)); + else + bad_name = (snprintf(fname, + sizeof (fname), + "%s.%s", name, + nis_local_directory()) >= + sizeof (fname)); + if (bad_name) { + nis_perror(NIS_BADNAME, name); + exit(1); + } + + s = remove_directory(fname, &sserv, 0); + } + exit(0); + } + nis_perror(res->status, name); + exit(1); + } + + bad_name = (snprintf(fname, sizeof (fname), "%s.", + res->objects.objects_val[0].zo_name) >= sizeof (fname)); + if (!bad_name && *(res->objects.objects_val[0].zo_domain) != '.') + bad_name = strlcat(fname, res->objects.objects_val[0].zo_domain, + sizeof (fname)) >= sizeof (fname); + + if (bad_name) { + nis_perror(NIS_BADNAME, fname); + exit(1); + } + if (res->objects.objects_val[0].zo_data.zo_type != NIS_DIRECTORY_OBJ) { + fprintf(stderr, "%s is not a directory!\n", fname); + exit(1); + } + + if (ask_remove || expand) { + printf("remove \"%s\"? ", fname); + *buf = '\0'; + (void) fgets(buf, sizeof (buf), stdin); + if (tolower(*buf) != 'y') + exit(0); + } + + obj = &(NIS_RES_OBJECT(res)[0]); + nserv = obj->DI_data.do_servers.do_servers_len; + servers = obj->DI_data.do_servers.do_servers_val; + + switch (op) { + case OP_RMDIR: + /* + * remove directory object + */ + rres = nis_remove(fname, 0); + if ((rres->status == NIS_PERMISSION) && force_remove) { + obj->zo_access |= 0x08080808; + nis_freeresult(rres); + rres = nis_modify(fname, obj); + if (rres->status == NIS_SUCCESS) { + nis_freeresult(rres); + rres = nis_remove(fname, 0); + } + } + if (rres->status != NIS_SUCCESS) { + if (force_remove) + exit(0); + nis_perror(rres->status, "can't remove directory"); + exit(1); + } + + /* + * fork a child and *try* to do nis_rmdirs. this may take + * a while since we may try to talk to servers that aren't + * up/responding. + */ + if (force_remove) + switch (fork()) { + case 0: + for (i = nserv-1; i >= 0; i--) + nis_rmdir(fname, &(servers[i])); + default: + exit(0); + } + + signal(SIGINT, (void(*)(int))cleanup_rmdir); + + /* + * remove slave directories + */ + for (nur = 0, i = 1; i < nserv; i++) { + s = nis_rmdir(fname, &(servers[i])); + if (s != NIS_SUCCESS) { + nur++; + fprintf(stderr, + "cannot remove replica \"%s\": %s.\n", + servers[i].name, nis_sperrno(s)); + } else + servers[i].name = 0; + } + if (nur) + cleanup_rmdir(); + + /* + * if all slave directories were removed, remove master + */ + s = nis_rmdir(fname, &(servers[0])); + if (s != NIS_SUCCESS) { + fprintf(stderr, "cannot remove master \"%s\": %s.\n", + servers[0].name, nis_sperrno(s)); + cleanup_rmdir(); + } + + exit(0); + + case OP_RMSLAVE: + /* + * find the slave. + */ + for (found = -1, i = 0; i < nserv; i++) { + if (match_host(host, servers[i].name)) { + if (found >= 0) { + fprintf(stderr, + "\"%s\" is not unique, please use full host name.\n", + host); + exit(1); + } + found = i; + } + } + + /* + * Host no longer listed as a replica. Try anyhow; perhaps + * user trying to clean up. + */ + if (found == -1) { + if (!force_remove) { + fprintf(stderr, + "Host \"%s\" is not listed as a replica for \"%s\".\n", + host, fname); + fprintf(stderr, + "Use the -fs option to attempt rmdir anyhow.\n"); + exit(1); + } + + sservp = __nis_host2nis_server_g(host, FALSE, + TRUE, &errcode); + if (sservp == NULL) { + nis_perror(errcode, host); + exit(1); + } + s = remove_directory(fname, sservp, 0); + exit(0); + } + + /* Host named is master */ + if (found == 0) { + if (force_remove) + exit(0); + fprintf(stderr, "\"%s\" is master for \"%s\"!\n", + servers[0].name, fname); + exit(1); + } + sserv = servers[found]; + sservi = found; + + /* + * remove slave from the directory object. + */ + nserv = --(obj->DI_data.do_servers.do_servers_len); + if (found < nserv) { + servers[found] = servers[nserv]; + } + mres = nis_modify(fname, obj); + if (mres->status != NIS_SUCCESS) { + if (force_remove) + exit(0); + fprintf(stderr, "cannot remove replica \"%s\": %s.\n", + sserv.name, nis_sperrno(mres->status)); + exit(1); + } + + /* + * fork a child and *try* to do nis_rmdir. this may take + * a while since we may try to talk to servers that aren't + * up/responding. + */ + if (force_remove) + switch (fork()) { + case 0: + nis_rmdir(fname, &sserv); + default: + exit(0); + } + + signal(SIGINT, (void(*)(int))cleanup_rmslave); + + /* + * remove the actual directory. + */ + s = nis_rmdir(fname, &sserv); + if (s != NIS_SUCCESS) { + fprintf(stderr, "cannot remove replica \"%s\": %s.\n", + sserv.name, nis_sperrno(s)); + cleanup_rmslave(); + exit(1); + } + exit(0); + } +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nistbladm.c b/usr/src/cmd/rpcsvc/nis/utils/nistbladm.c new file mode 100644 index 0000000000..e25d8244cf --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nistbladm.c @@ -0,0 +1,722 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nistbladm.c + * + * Copyright (c) 1988-2000 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nistbladm.c + * + * nis+ table admin utility + */ + +#include <stdio.h> +#include <string.h> +#include <malloc.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> + +extern int optind; +extern char *optarg; + +extern nis_object nis_default_obj; + +extern char *nisname_index(); + + +char *DEFAULT_PATH = ""; +char *DEFAULT_SEP = " "; + +#define OP_CREATE 1 +#define OP_UPDATE 2 +#define OP_DESTROY 3 +#define OP_ADD 4 +#define OP_MODIFY 5 +#define OP_REMOVE 6 + +/* change defines */ +#define CHG_PATH 1 +#define CHG_SEP 2 +#define CHG_ACCESS 4 +#define CHG_TYPE 5 + +struct buf { + char *s; + int len; + int alloc; +}; +typedef struct buf buf; + +static +void +buf_init(buf *b) +{ + b->len = 0; + b->alloc = 100; /* start with a fair amount of room */ + b->s = (char *)malloc(b->alloc); + if (b->s == NULL) { + fprintf(stderr, "buf_init: out of memory\n"); + exit(1); + } +} + +static +void +buf_add(buf *b, char c) +{ + if (b->len >= b->alloc) { + b->alloc += 25; /* make room for extra chars */ + b->s = (char *)realloc((void *)b->s, b->alloc); + if (b->s == NULL) { + fprintf(stderr, "buf_check: out of memory\n"); + exit(1); + } + } + b->s[b->len] = c; + b->len += 1; +} + +static +void +buf_cat(buf *b, char *src) +{ + while (*src) + buf_add(b, *src++); +} + +/* + * This function is used to copy a "colname=value" string into another + * string that will be passed to nis_get_request. Any unquoted "," + * or "]" characters are quoted so that they won't be interpreted as + * the end of the indexed name. + */ +static +void +buf_cat_quote(buf *b, char *src) +{ + /* + * Process each character in src. If it is a quote, copy + * characters until reaching the terminating quote. If it + * is a ',' or a ']', then quote it into dest. All other + * characters are copied "as is". + */ + while (*src) { + if (*src == '"') { + buf_add(b, *src++); + while (*src && *src != '"') + buf_add(b, *src++); + if (*src) + buf_add(b, *src++); + } else if (*src == ',' || *src == ']') { + buf_add(b, '"'); + buf_add(b, *src++); + buf_add(b, '"'); + } else { + buf_add(b, *src++); + } + } +} + +static +void +buf_cat_n(buf *b, char *s, int n) +{ + int i; + + for (i = 0; i < n && *s; i++) { + buf_add(b, *s++); + } +} + +static +char * +buf_value(buf *b) +{ + char *p; + + p = malloc(b->len + 1); + if (p == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + memcpy(p, b->s, b->len); + p[b->len] = 0; + return (p); +} + + +void +usage() +{ + fprintf(stderr, + "usage:\tnistbladm [-D defaults] -c [-p path] [-s sep] type\n"); + fprintf(stderr, "\t\tcolname=[flags][,access] ... tablename\n"); + fprintf(stderr, +"\tnistbladm -u [-p path] [-s sep] [-t type] [colname=access ...] tablename\n"); + fprintf(stderr, "\tnistbladm -d tablename\n"); + fprintf(stderr, + "\tnistbladm [-D defaults] -a|A colname=val ... tablename\n"); + fprintf(stderr, "\tnistbladm [-D defaults] -a|A indexedname\n"); + fprintf(stderr, "\tnistbladm -e|E colname=val ... indexedname\n"); + fprintf(stderr, "\tnistbladm -m colname=val ... indexedname\n"); + fprintf(stderr, "\tnistbladm -r|R [colname=val ...] tablename\n"); + fprintf(stderr, "\tnistbladm -r|R indexedname\n"); + exit(1); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + int len; + char *defstr = 0; + int op = 0; + unsigned flags = 0; + char *name; + nis_result *res, *ares, *mres, *rres; + nis_object *obj, tobj, eobj; + char *ta_type = 0, *ta_path = DEFAULT_PATH, *ta_sep = DEFAULT_SEP; + int ta_maxcol, i, j; + char *p, *p1; + table_col *tcol, *ocol; + char *mpred; + char *spred; + char *sname; + char *tname; + buf mpred_buf; + buf spred_buf; + nis_error s; + ib_request ibr; + entry_col *ecol; + int nrights = 0; + char **colname; + char **rights; + + while ((c = getopt(argc, argv, "D:cudaAeEmrRp:s:t:")) != -1) { + switch (c) { + case 'D': + defstr = optarg; + break; + case 'c': + if (op) + usage(); + op = OP_CREATE; + break; + case 'u': + if (op) + usage(); + op = OP_UPDATE; + break; + case 'd': + if (op) + usage(); + op = OP_DESTROY; + break; + case 'A': + flags |= ADD_OVERWRITE; + case 'a': + if (op) + usage(); + op = OP_ADD; + break; + case 'e': + flags |= MOD_EXCLUSIVE; + case 'm': + case 'E': + if (op) + usage(); + op = OP_MODIFY; + break; + case 'R': + flags |= REM_MULTIPLE; + case 'r': + if (op) + usage(); + op = OP_REMOVE; + break; + case 'p': + ta_path = optarg; + break; + case 's': + ta_sep = optarg; + if (strlen(optarg) != 1) { + fprintf(stderr, + "separator must be a single character\n"); + exit(1); + } + break; + case 't': + ta_type = optarg; + break; + default: + usage(); + } + } + + if (!op) + usage(); + + if (!nis_defaults_init(defstr)) + exit(1); + + + buf_init(&mpred_buf); + buf_init(&spred_buf); + + switch (op) { + case OP_CREATE: + if (argc - optind < 3) + usage(); + ta_type = argv[optind++]; + ta_maxcol = argc - optind - 1; + + /* + * Most standard tables are created by nissetup, so they + * get the correct number of columns. However, automount_map + * tables are an exception, and are often added by the admin. + * Thus we check that they have exactly two columns, in order + * to avoid confusing applications like automountd. + */ + if (!strcasecmp(ta_type, "automount_map") && ta_maxcol != 2) { + fprintf(stderr, + "automount_map type nis+ tables must have exactly " + "two columns.\n"); + exit(1); + } + + if ((tcol = (table_col*)malloc(ta_maxcol*sizeof (table_col))) + == 0) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + for (i = 0; i < ta_maxcol; i++) { + p = tcol[i].tc_name = argv[optind++]; + if ((p = strchr(p, '=')) == 0) + usage(); + *(p++) = 0; + if (*p) { + p1 = strchr(p, ','); + if (p1) + *(p1++) = 0; + } else + p1 = 0; + tcol[i].tc_flags = 0; + if (!parse_flags(&(tcol[i].tc_flags), p)) + usage(); + tcol[i].tc_rights = nis_default_obj.zo_access; + if (!parse_rights(&(tcol[i].tc_rights), p1)) + usage(); + } + + name = argv[optind]; + if (name[strlen(name)-1] != '.') { + fprintf(stderr, "tablename must be fully qualified.\n"); + exit(1); + } + + tobj = nis_default_obj; + tobj.zo_data.zo_type = NIS_TABLE_OBJ; + tobj.TA_data.ta_type = ta_type; + tobj.TA_data.ta_maxcol = ta_maxcol; + tobj.TA_data.ta_sep = *ta_sep; + tobj.TA_data.ta_path = ta_path; + tobj.TA_data.ta_cols.ta_cols_len = ta_maxcol; + tobj.TA_data.ta_cols.ta_cols_val = tcol; + + ares = nis_add(name, &tobj); + if (ares->status != NIS_SUCCESS) { + nis_perror(ares->status, "can't create table"); + exit(1); + } + exit(0); + + case OP_UPDATE: + if (argc <= optind) + usage(); + if (argc - optind > 1) { + nrights = argc - optind - 1; + colname = (char **)malloc(nrights * sizeof (char *)); + rights = (char **)malloc(nrights * sizeof (char *)); + if (colname == 0 || rights == 0) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + for (i = 0; i < nrights; i++) { + p = strchr(argv[optind], '='); + if (p == 0) { + fprintf(stderr, + "Missing access rights for " + "\"%s\"\n", argv[optind]); + exit(1); + } + *p++ = 0; + colname[i] = strdup(argv[optind]); + rights[i] = strdup(p); + if (colname[i] == 0 || rights[i] == 0) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + optind++; + } + } + + name = argv[optind]; + if (*name == '[') + usage(); + break; + + case OP_DESTROY: + if (argc - optind < 1) + usage(); + name = argv[optind]; + break; + + case OP_ADD: + case OP_REMOVE: + if (argc - optind < 1) + usage(); + p = argv[optind++]; + if (*p == '[') { + name = nisname_index(p, ']'); + if (name == 0) + usage(); + name++; + buf_cat_n(&spred_buf, p, name-p); + if (*name == ',') + name++; + } else if (argc - optind < 1) { + if (op == OP_ADD) + usage(); + name = p; + } else { + buf_cat_quote(&spred_buf, p); + while (argc - optind > 1) { + buf_cat(&spred_buf, ","); + buf_cat_quote(&spred_buf, argv[optind++]); + } + name = argv[optind++]; + if (*name == '[') + usage(); + } + spred = buf_value(&spred_buf); + break; + + case OP_MODIFY: + if (argc - optind < 2) + usage(); + buf_cat_quote(&mpred_buf, argv[optind++]); + while (argc - optind > 1) { + buf_cat(&mpred_buf, ","); + buf_cat_quote(&mpred_buf, argv[optind++]); + } + mpred = buf_value(&mpred_buf); + + p = argv[optind++]; + if (*p != '[') + usage(); + name = nisname_index(p, ']'); + if (name == 0) + usage(); + name++; + buf_cat_n(&spred_buf, p, name-p); + if (*name == ',') + name++; + spred = buf_value(&spred_buf); + break; + } + + /* + * Get the table object. + */ + res = nis_lookup(name, MASTER_ONLY|FOLLOW_LINKS|EXPAND_NAME); + if (res->status != NIS_SUCCESS) { + nis_perror(res->status, name); + exit(1); + } + + obj = &(NIS_RES_OBJECT(res)[0]); + len = strlen(res->objects.objects_val[0].zo_name) + + strlen(res->objects.objects_val[0].zo_domain) + + strlen(".") + 1; + tname = (char *)malloc(len); + if (tname == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + sprintf(tname, "%s.", res->objects.objects_val[0].zo_name); + if (*(res->objects.objects_val[0].zo_domain) != '.') + strcat(tname, res->objects.objects_val[0].zo_domain); + + if (obj->zo_data.zo_type != NIS_TABLE_OBJ) { + fprintf(stderr, "\"%s\" is not a table!\n", name); + exit(1); + } + + ta_maxcol = obj->TA_data.ta_maxcol; + + switch (op) { + case OP_UPDATE: + + /* Now we morph the table object, first start with orig */ + tobj = *obj; + + /* Change its type if desired */ + if (ta_type) + tobj.TA_data.ta_type = ta_type; + + /* Change the separator if desired */ + if (ta_sep != DEFAULT_SEP) + tobj.TA_data.ta_sep = *ta_sep; + + /* Change the path if desired */ + if (ta_path != DEFAULT_PATH) + tobj.TA_data.ta_path = ta_path; + + /* Allocate some column descriptors */ + tcol = (table_col *)calloc(ta_maxcol, sizeof (table_col)); + if (tcol == 0) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + /* Set the new table to have these malloc'd columns */ + tobj.TA_data.ta_cols.ta_cols_val = tcol; + ocol = obj->TA_data.ta_cols.ta_cols_val; + + /* check the table schema */ + for (i = 0; i < nrights; ++i) { + for (j = 0; j < ta_maxcol; ++j) + if (strcmp(ocol[j].tc_name, colname[i]) == 0) + break; + if (j == ta_maxcol) { + fprintf(stderr, + "Invalid table schema: \"%s\" is an invalid " + "column.\n", colname[i]); + exit(1); + } + } + + for (j = 0; j < ta_maxcol; j++) { + + /* Copy the old column into the new column */ + tcol[j] = ocol[j]; + + /* Check to see if new access rights were specified */ + for (i = 0; i < nrights; i++) { + + /* If so change them appropriately */ + if (strcmp(ocol[j].tc_name, colname[i]) == 0) { + if (! parse_rights(&(tcol[j].tc_rights), + rights[i])) { + fprintf(stderr, + "Couldn't parse access rights (%s) for column \"%s\"\n", + rights[i], + colname[i]); + exit(1); + } else { + tcol[j].tc_flags |= TA_MODIFIED; + break; + } /* parsed correctly */ + } /* same name as index */ + } /* for all indices */ + } /* for all columns */ + + mres = nis_modify(tname, &tobj); + if (mres->status != NIS_SUCCESS) { + nis_perror(mres->status, "can't modify table"); + exit(1); + } + exit(0); + + case OP_DESTROY: + rres = nis_remove(tname, 0); + if (rres->status != NIS_SUCCESS) { + nis_perror(rres->status, "can't remove table"); + exit(1); + } + exit(0); + + case OP_ADD: + len = strlen(spred) + strlen(tname) + strlen("[]") + 1; + sname = (char *)malloc(len); + if (sname == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + if (*spred == '[') + sprintf(sname, "%s%s", spred, tname); + else + sprintf(sname, "[%s]%s", spred, tname); + + /* + * Parse search criteria. + */ + s = nis_get_request(sname, 0, 0, &ibr); + if (s != NIS_SUCCESS) { + nis_perror(s, "can't parse column values"); + exit(1); + } + + eobj = nis_default_obj; + if (eobj.zo_ttl > obj->zo_ttl) + eobj.zo_ttl = obj->zo_ttl; + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = obj->TA_data.ta_type; + + if ((ecol = (entry_col*)malloc(ta_maxcol*sizeof (entry_col))) + == 0) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + memset((char *)ecol, 0, ta_maxcol*sizeof (entry_col)); + eobj.EN_data.en_cols.en_cols_len = ta_maxcol; + eobj.EN_data.en_cols.en_cols_val = ecol; + + for (i = 0; i < ibr.ibr_srch.ibr_srch_len; i++) { + for (j = 0; j < ta_maxcol; j++) + if (strcmp( + obj->TA_data.ta_cols.ta_cols_val[j].tc_name, + ibr.ibr_srch.ibr_srch_val[i].zattr_ndx) == 0) + break; + if (j < ta_maxcol) { + ecol[j].ec_value.ec_value_len = + ibr.ibr_srch.ibr_srch_val[i].zattr_val.zattr_val_len; + ecol[j].ec_value.ec_value_val = + ibr.ibr_srch.ibr_srch_val[i].zattr_val.zattr_val_val; + ecol[j].ec_flags = + obj->TA_data.ta_cols.ta_cols_val[j].tc_flags & + (TA_BINARY|TA_CRYPT|TA_XDR|TA_ASN1); + } else { + fprintf(stderr, + "table has no column named \"%s\"\n", + ibr.ibr_srch.ibr_srch_val[i].zattr_ndx); + exit(1); + } + } + + ares = nis_add_entry(tname, &eobj, flags); + if (ares->status != NIS_SUCCESS) { + nis_perror(ares->status, "can't add entry"); + exit(1); + } + break; + + case OP_MODIFY: + /* + * Parse modify criteria. + */ + len = strlen(mpred) + strlen(tname) + strlen("[]") + 1; + sname = (char *)malloc(len); + if (sname == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + sprintf(sname, "[%s]%s", mpred, tname); + s = nis_get_request(sname, 0, 0, &ibr); + if (s != NIS_SUCCESS) { + nis_perror(s, "can't parse column values"); + exit(1); + } + + memset((char *)&eobj, 0, sizeof (eobj)); + eobj.zo_data.zo_type = NIS_ENTRY_OBJ; + eobj.EN_data.en_type = obj->TA_data.ta_type; + + if ((ecol = (entry_col*)malloc(ta_maxcol*sizeof (entry_col))) + == 0) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + memset((char *)ecol, 0, ta_maxcol*sizeof (entry_col)); + eobj.EN_data.en_cols.en_cols_len = ta_maxcol; + eobj.EN_data.en_cols.en_cols_val = ecol; + + for (i = 0; i < ibr.ibr_srch.ibr_srch_len; i++) { + for (j = 0; j < ta_maxcol; j++) + if (strcmp( + obj->TA_data.ta_cols.ta_cols_val[j].tc_name, + ibr.ibr_srch.ibr_srch_val[i].zattr_ndx) == 0) + break; + if (j < ta_maxcol) { + ecol[j].ec_value.ec_value_len = + ibr.ibr_srch.ibr_srch_val[i].zattr_val.zattr_val_len; + ecol[j].ec_value.ec_value_val = + ibr.ibr_srch.ibr_srch_val[i].zattr_val.zattr_val_val; + ecol[j].ec_flags = + (obj->TA_data.ta_cols.ta_cols_val[j].tc_flags & + (TA_BINARY|TA_CRYPT|TA_XDR|TA_ASN1)) | + EN_MODIFIED; + } else { + fprintf(stderr, + "table has no column named \"%s\"\n", + ibr.ibr_srch.ibr_srch_val[i].zattr_ndx); + exit(1); + } + } + + free(sname); /* old value no longer needed */ + + len = strlen(spred) + strlen(tname) + 1; + sname = (char *)malloc(len); + if (sname == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + sprintf(sname, "%s%s", spred, tname); + + mres = nis_modify_entry(sname, &eobj, flags); + if (mres->status != NIS_SUCCESS) { + nis_perror(mres->status, "can't modify entry"); + exit(1); + } + break; + + case OP_REMOVE: + len = strlen(spred) + strlen(tname) + strlen("[]") + 1; + sname = (char *)malloc(len); + if (sname == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + if (*spred == '[') + sprintf(sname, "%s%s", spred, tname); + else + sprintf(sname, "[%s]%s", spred, tname); + + rres = nis_remove_entry(sname, 0, flags); + if (rres->status != NIS_SUCCESS) { + nis_perror(rres->status, "can't remove entry"); + exit(1); + } + break; + } + + exit(0); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/nistest.c b/usr/src/cmd/rpcsvc/nis/utils/nistest.c new file mode 100644 index 0000000000..b2f77ea4b4 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/nistest.c @@ -0,0 +1,320 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +/* + * nistest.c + * + * Copyright (c) 1988-1992 Sun Microsystems Inc + * All Rights Reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * nistest.c + * + * nis+ object test utility + */ + +#include <stdio.h> +#include <rpc/rpc.h> +#include <rpcsvc/nis.h> + +extern int optind; +extern char *optarg; + + +#define EXIT_TRUE 0 +#define EXIT_FALSE 1 +#define EXIT_ERROR 2 + +#define CMP_LT 0 +#define CMP_LE 1 +#define CMP_EQ 2 +#define CMP_HT 3 +#define CMP_HE 4 +#define CMP_NE 5 +#define CMP_NS 6 + +struct cmp_key { + char *key; + int value; +}; + +struct cmp_key cmp[] = { + "<", CMP_LT, + "lt", CMP_LT, + "<=", CMP_LE, + "le", CMP_LE, + "=", CMP_EQ, + "eq", CMP_EQ, + ">", CMP_HT, + "ht", CMP_HT, + "gt", CMP_HT, + ">=", CMP_HE, + "he", CMP_HE, + "ge", CMP_HE, + "!=", CMP_NE, + "ne", CMP_NE, + "ns", CMP_NS, + NULL, 0, +}; + +void +usage() +{ + fprintf(stderr, + "usage: nistest [-LPAM] [-t G|D|L|T|E|P] [-a mode] name\n"); + fprintf(stderr, + " nistest -c [dir op dir]\n"); + exit(EXIT_ERROR); +} + +#define TEST_TYPE 1 +#define TEST_ACCESS 2 +#define TEST_CMP 3 + +unsigned int flags = 0; +zotypes otype; +u_long oaccess; +int dotest_called = 0; + +static +void +cmp_list() +{ + struct cmp_key *p; + + printf("Op\tMeaning\n"); + printf("----\t-------\n"); + for (p = cmp; p->key; p++) { + printf("%s\t", p->key); + switch (p->value) { + case CMP_LT: + printf("lower than"); + break; + case CMP_LE: + printf("lower than or equal"); + break; + case CMP_EQ: + printf("equal"); + break; + case CMP_HT: + printf("higher than"); + break; + case CMP_HE: + printf("higher than or equal"); + break; + case CMP_NE: + printf("not equal"); + break; + case CMP_NS: + printf("not sequential"); + break; + } + printf("\n"); + } +} + +static +int +cmp_test(char *d1, char *op, char *d2) +{ + int ret; + enum name_pos st; + struct cmp_key *p; + + for (p = cmp; p->key; p++) { + if (strcasecmp(p->key, op) == 0) + break; + } + if (p->key == NULL) { + fprintf(stderr, "nistest: bad op\n"); + return (EXIT_ERROR); + } + + st = nis_dir_cmp(d1, d2); + if (st == BAD_NAME) { + fprintf(stderr, "nistest: bad name\n"); + return (EXIT_ERROR); + } + + switch (p->value) { + case CMP_LT: + ret = (st == LOWER_NAME); + break; + case CMP_LE: + ret = (st == LOWER_NAME || st == SAME_NAME); + break; + case CMP_EQ: + ret = (st == SAME_NAME); + break; + case CMP_HT: + ret = (st == HIGHER_NAME); + break; + case CMP_HE: + ret = (st == HIGHER_NAME || st == SAME_NAME); + break; + case CMP_NE: + ret = !(st == SAME_NAME); + break; + case CMP_NS: + ret = (st == NOT_SEQUENTIAL); + break; + } + + if (ret) + return (EXIT_TRUE); + return (EXIT_FALSE); +} + +int +dotest(tab, obj, udata) + char *tab; + nis_object *obj; + void *udata; +{ + if ((flags & TEST_TYPE) && + (obj->zo_data.zo_type != otype)) + exit(EXIT_FALSE); + + if ((flags & TEST_ACCESS) && + ((obj->zo_access & oaccess) != oaccess)) + exit(EXIT_FALSE); + + dotest_called = 1; +} + + +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + u_long flinks = 0, fpath = 0, allres = 0, master = 0; + char *name; + nis_result *ores; + + while ((c = getopt(argc, argv, "LPAMt:a:c")) != -1) + switch (c) { + case 'L': + flinks = FOLLOW_LINKS; + break; + case 'P': + fpath = FOLLOW_PATH; + break; + case 'A': + allres = ALL_RESULTS; + break; + case 'M': + master = MASTER_ONLY; + break; + case 't': + flags |= TEST_TYPE; + switch (*optarg) { + case 'G': + otype = NIS_GROUP_OBJ; + break; + case 'D': + otype = NIS_DIRECTORY_OBJ; + break; + case 'T': + otype = NIS_TABLE_OBJ; + break; + case 'L': + otype = NIS_LINK_OBJ; + break; + case 'E': + otype = NIS_ENTRY_OBJ; + break; + case 'P': + otype = NIS_PRIVATE_OBJ; + break; + default: + usage(); + break; + } + break; + case 'a': + flags |= TEST_ACCESS; + oaccess = 0; + if (!parse_rights(&oaccess, optarg)) + usage(); + break; + case 'c': + if (flags == TEST_CMP) { + fprintf(stderr, "%s: %s\n", + "nistest", + "-c can only be specified once"); + exit(EXIT_ERROR); + } else if (flags != 0) { + fprintf(stderr, "%s: %s\n", + "nistest", + "-c cannot be combined with other tests"); + exit(EXIT_ERROR); + } + flags = TEST_CMP; + break; + default: + usage(); + } + + if (flags == TEST_CMP) { + switch (argc - optind) { + case 0: + cmp_list(); + exit(EXIT_TRUE); + break; + case 3: + exit(cmp_test(argv[optind], + argv[optind+1], argv[optind+2])); + break; + default: + usage(); + break; + } + } + + if (argc - optind != 1) + usage(); + + name = argv[optind]; + + /* + * Get the object using expand name magic, and test it. + */ + if (*name == '[') { + ores = nis_list(name, + fpath|allres|master|FOLLOW_LINKS|EXPAND_NAME, dotest, 0); + if (ores->status != NIS_CBRESULTS) + exit(EXIT_FALSE); + } else { + ores = nis_lookup(name, flinks|master|EXPAND_NAME); + if (ores->status != NIS_SUCCESS) + exit(EXIT_FALSE); + dotest(0, ores->objects.objects_val, 0); + } + + if (dotest_called) + exit(EXIT_TRUE); + + exit(EXIT_FALSE); +} diff --git a/usr/src/cmd/rpcsvc/nis/utils/req.flg b/usr/src/cmd/rpcsvc/nis/utils/req.flg new file mode 100644 index 0000000000..9841aaddb9 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nis/utils/req.flg @@ -0,0 +1,49 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# + +echo_file usr/src/lib/libnsl/include/rpcsvc/nis_dhext.h +echo_file usr/src/lib/libnisdb/nisdb_ldap.h +echo_file usr/src/lib/libnisdb/ldap_attr.h +echo_file usr/src/lib/libnisdb/ldap_map.h +echo_file usr/src/lib/libnisdb/ldap_nisdbquery.h +echo_file usr/src/lib/libnisdb/ldap_nisplus.h +echo_file usr/src/lib/libnisdb/ldap_op.h +echo_file usr/src/lib/libnisdb/ldap_parse.h +echo_file usr/src/lib/libnisdb/ldap_print.h +echo_file usr/src/lib/libnisdb/ldap_ruleval.h +echo_file usr/src/lib/libnisdb/ldap_structs.h +echo_file usr/src/lib/libnisdb/ldap_util.h +echo_file usr/src/lib/libnisdb/ldap_xdr.h +echo_file usr/src/lib/libnisdb/nis_hashitem.h +echo_file usr/src/lib/libnisdb/nis_ldap.h +echo_file usr/src/lib/libnisdb/nis_parse_ldap_conf.h +echo_file usr/src/lib/libnisdb/nis_servlist.h +echo_file usr/src/lib/libnisdb/nisdb_ldap.h +echo_file usr/src/lib/libnisdb/nisdb_mt.h +echo_file usr/src/lib/libnisdb/nisdb_rw.h diff --git a/usr/src/cmd/rpcsvc/nisplus b/usr/src/cmd/rpcsvc/nisplus new file mode 100644 index 0000000000..d6e804bf82 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nisplus @@ -0,0 +1,80 @@ +#!/sbin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +. /lib/svc/share/smf_include.sh + +cachemgr=0 +if [ -f /var/nis/NIS_COLD_START ]; then + cache=`/usr/bin/svcprop -p application_ovr/clear_cache $SMF_FMRI \ + 2>/dev/null` + if [ $? != 0 ]; then + cache=`/usr/bin/svcprop -p application/clear_cache $SMF_FMRI \ + 2>/dev/null` + fi + + [ "$cache" = "true" ] && cachemgr_flags="$cachemgr_flags -i" + + /usr/sbin/nis_cachemgr $cachemgr_flags || exit $? + cachemgr=1 +fi + +[ -z "$_INIT_UTS_NODENAME" ] && \ + _INIT_UTS_NODENAME=`/usr/bin/uname -n` + +hostname=`echo "$_INIT_UTS_NODENAME" | /usr/bin/cut -d. -f1 | \ + /usr/bin/tr '[A-Z]' '[a-z]'` + +nisd=0 +if [ -d /var/nis/data -o -d /var/nis/$hostname ]; then + emulate=`/usr/bin/svcprop -p application/emulate_yp $SMF_FMRI \ + 2>/dev/null` + [ $? = 0 ] && [ "$emulate" = "true" ] && nisd_flags="$nisd_flags -Y" + + security=`/usr/bin/svcprop -p application/security $SMF_FMRI \ + 2>/dev/null` + [ $? = 0 ] && nisd_flags="$nisd_flags -S $security" + + /usr/sbin/rpc.nisd $nisd_flags || exit $? + nisd=1 + + # We always start the NIS+ Password Update Daemon. If it + # finds the NIS+ server is not a Master it will just exit. + # It also determines if the server is running in NIS (YP) + # compat mode and automatically registers a yppasswdd so + # NIS (YP) clients can change their passwords. + + /usr/sbin/rpc.nispasswdd || exit $? +fi + +if [ "$cachemgr" = 0 -a "$nisd" = 0 ]; then + # If we're neither able to be a client or a server, then + # we are misconfigured. + exit $SMF_EXIT_ERR_CONFIG +fi + +exit $SMF_EXIT_OK diff --git a/usr/src/cmd/rpcsvc/nisplus.xml b/usr/src/cmd/rpcsvc/nisplus.xml new file mode 100644 index 0000000000..ff29f5afe5 --- /dev/null +++ b/usr/src/cmd/rpcsvc/nisplus.xml @@ -0,0 +1,85 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2005 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License, Version 1.0 only + (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 + + ident "%Z%%M% %I% %E% SMI" + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. +--> + +<service_bundle type='manifest' name='SUNWnisr:nisplus'> + +<service + name='network/rpc/nisplus' + type='service' + version='1'> + + <dependency + name='keyserv' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/network/rpc/keyserv' /> + </dependency> + + <exec_method + type='method' + name='start' + exec='/lib/svc/method/nisplus' + timeout_seconds='60' /> + + <exec_method + type='method' + name='stop' + exec=':kill' + timeout_seconds='60' /> + + <instance name='default' enabled='false'> + <property_group name='application' type='application'> + <stability value='Unstable' /> + <propval name='emulate_yp' type='boolean' + value='false' /> + </property_group> + </instance> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + NIS+ + </loctext> + </common_name> + <documentation> + <manpage title='rpc.nisd' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/Makefile b/usr/src/cmd/rpcsvc/rpc.bootparamd/Makefile new file mode 100644 index 0000000000..28abf8b2ce --- /dev/null +++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/Makefile @@ -0,0 +1,63 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# cmd/rpcsvc/rpc.bootparamd/Makefile + +PROG= rpc.bootparamd +MANIFEST= bootparams.xml + +OBJS= bootparam_ip_route.o bootparam_prot_svc.o \ + bootparam_prot_xdr.o bootparam_subr.o + +include ../Makefile.rpc + +CPPFLAGS= -I. $(CPPFLAGS.master) +CFLAGS += -DSYSV +LDLIBS += -lsocket -lnsl + +SRCS= $(OBJS:%.o=%.c) + +.KEEP_STATE: + +all: $(PROG) + +# multi-object targets + +rpc.bootparamd: $(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +install: all $(ROOTUSRSBINPROG) $(ROOTMANIFEST) + +check: $(CHKMANIFEST) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +include ../../Makefile.targ diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_ip_route.c b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_ip_route.c new file mode 100644 index 0000000000..f011e17d38 --- /dev/null +++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_ip_route.c @@ -0,0 +1,523 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 1991-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/tihdr.h> +#include <sys/tiuser.h> +#include <sys/timod.h> + +#include <sys/socket.h> +#include <sys/sockio.h> +#include <netinet/in.h> +#include <net/if.h> + +#include <inet/common.h> +#include <inet/mib2.h> +#include <inet/ip.h> +#include <netinet/igmp_var.h> +#include <netinet/ip_mroute.h> + +#include <arpa/inet.h> + +#include <netdb.h> +#include <nss_dbdefs.h> +#include <fcntl.h> +#include <stropts.h> + +#include "bootparam_private.h" + +typedef struct mib_item_s { + struct mib_item_s *next_item; + long group; + long mib_id; + long length; + char *valp; +} mib_item_t; + +static void free_itemlist(mib_item_t *); + +static mib_item_t * +mibget(int sd) +{ + char buf[512]; + int flags; + int i, j, getcode; + struct strbuf ctlbuf, databuf; + struct T_optmgmt_req *tor = (struct T_optmgmt_req *)(void *)buf; + struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)(void *)buf; + struct T_error_ack *tea = (struct T_error_ack *)(void *)buf; + struct opthdr *req; + mib_item_t *first_item = nilp(mib_item_t); + mib_item_t *last_item = nilp(mib_item_t); + mib_item_t *temp; + + tor->PRIM_type = T_SVR4_OPTMGMT_REQ; + tor->OPT_offset = sizeof (struct T_optmgmt_req); + tor->OPT_length = sizeof (struct opthdr); + tor->MGMT_flags = T_CURRENT; + req = (struct opthdr *)&tor[1]; + req->level = MIB2_IP; /* any MIB2_xxx value ok here */ + req->name = 0; + req->len = 0; + + ctlbuf.buf = buf; + ctlbuf.len = tor->OPT_length + tor->OPT_offset; + flags = 0; + if (putmsg(sd, &ctlbuf, nilp(struct strbuf), flags) == -1) { + perror("mibget: putmsg(ctl) failed"); + goto error_exit; + } + /* + * each reply consists of a ctl part for one fixed structure + * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK, + * containing an opthdr structure. level/name identify the entry, + * len is the size of the data part of the message. + */ + req = (struct opthdr *)&toa[1]; + ctlbuf.maxlen = sizeof (buf); + for (j = 1; ; j++) { + flags = 0; + getcode = getmsg(sd, &ctlbuf, nilp(struct strbuf), &flags); + if (getcode == -1) { + perror("mibget getmsg(ctl) failed"); + if (debug) { + msgout("# level name len"); + i = 0; + for (last_item = first_item; last_item; + last_item = last_item->next_item) + msgout("%d %4ld %5ld %ld", ++i, + last_item->group, + last_item->mib_id, + last_item->length); + } + goto error_exit; + } + if ((getcode == 0) && + (ctlbuf.len >= sizeof (struct T_optmgmt_ack))&& + (toa->PRIM_type == T_OPTMGMT_ACK) && + (toa->MGMT_flags == T_SUCCESS) && + (req->len == 0)) { + if (debug) + msgout("mibget getmsg() %d returned EOD " + "(level %lu, name %lu)", + j, req->level, req->name); + return (first_item); /* this is EOD msg */ + } + + if (ctlbuf.len >= sizeof (struct T_error_ack) && + tea->PRIM_type == T_ERROR_ACK) { + msgout("mibget %d gives T_ERROR_ACK: " + "TLI_error = 0x%lx, UNIX_error = 0x%lx", + j, tea->TLI_error, tea->UNIX_error); + errno = (tea->TLI_error == TSYSERR) + ? tea->UNIX_error : EPROTO; + goto error_exit; + } + + if (getcode != MOREDATA || + ctlbuf.len < sizeof (struct T_optmgmt_ack) || + toa->PRIM_type != T_OPTMGMT_ACK || + toa->MGMT_flags != T_SUCCESS) { + msgout("mibget getmsg(ctl) %d returned %d, " + "ctlbuf.len = %d, PRIM_type = %ld", + j, getcode, ctlbuf.len, toa->PRIM_type); + if (toa->PRIM_type == T_OPTMGMT_ACK) + msgout("T_OPTMGMT_ACK: MGMT_flags = 0x%lx, " + "req->len = %lu", + toa->MGMT_flags, req->len); + errno = ENOMSG; + goto error_exit; + } + + temp = (mib_item_t *)malloc(sizeof (mib_item_t)); + if (!temp) { + perror("mibget malloc failed"); + goto error_exit; + } + if (last_item) + last_item->next_item = temp; + else + first_item = temp; + last_item = temp; + last_item->next_item = nilp(mib_item_t); + last_item->group = req->level; + last_item->mib_id = req->name; + last_item->length = req->len; + last_item->valp = (char *)malloc(req->len); + if (debug) + msgout( + "msg %d: group = %4ld mib_id = %5ld length = %ld", + j, last_item->group, last_item->mib_id, + last_item->length); + + databuf.maxlen = last_item->length; + databuf.buf = last_item->valp; + databuf.len = 0; + flags = 0; + getcode = getmsg(sd, nilp(struct strbuf), &databuf, &flags); + if (getcode == -1) { + perror("mibget getmsg(data) failed"); + goto error_exit; + } else if (getcode != 0) { + msgout("xmibget getmsg(data) returned %d, " + "databuf.maxlen = %d, databuf.len = %d", + getcode, databuf.maxlen, databuf.len); + goto error_exit; + } + } + +error_exit: + free_itemlist(first_item); + return (NULL); +} + +static void +free_itemlist(mib_item_t *item_list) +{ + mib_item_t *item; + + while (item_list) { + item = item_list; + item_list = item->next_item; + if (item->valp) + free(item->valp); + free(item); + } +} + +/* + * If we are a router, return address of interface closest to client. + * If we are not a router, look through our routing table and return + * address of "best" router that is on same net as client. + * + * We expect the router flag to show up first, followed by interface + * addr group, followed by the routing table. + */ + +in_addr_t +get_ip_route(struct in_addr client_addr) +{ + boolean_t found; + mib_item_t *item_list; + mib_item_t *item; + int sd; + mib2_ip_t *mip; + mib2_ipAddrEntry_t *map; + mib2_ipRouteEntry_t *rp; + int ip_forwarding = 2; /* off */ + /* mask of interface used to route to client and best_router */ + struct in_addr interface_mask; + /* address of interface used to route to client and best_router */ + struct in_addr interface_addr; + /* address of "best router"; i.e. the answer */ + struct in_addr best_router; + + interface_mask.s_addr = 0L; + interface_addr.s_addr = 0L; + best_router.s_addr = 0L; + + /* open a stream to IP */ + sd = open("/dev/ip", O_RDWR); + if (sd == -1) { + perror("ip open"); + (void) close(sd); + msgout("can't open mib stream"); + return (0); + } + + /* send down a request and suck up all the mib info from IP */ + if ((item_list = mibget(sd)) == nilp(mib_item_t)) { + msgout("mibget() failed"); + (void) close(sd); + return (0); + } + + /* + * We make three passes through the list of collected IP mib + * information. First we figure out if we are a router. Next, + * we find which of our interfaces is on the same subnet as + * the client. Third, we paw through our own routing table + * looking for a useful router address. + */ + + /* + * The general IP group. + */ + for (item = item_list; item; item = item->next_item) { + if ((item->group == MIB2_IP) && (item->mib_id == 0)) { + /* are we an IP router? */ + mip = (mib2_ip_t *)(void *)item->valp; + ip_forwarding = mip->ipForwarding; + break; + } + } + + /* + * The interface group. + */ + for (item = item_list, found = B_FALSE; item != NULL && !found; + item = item->next_item) { + if ((item->group == MIB2_IP) && (item->mib_id == MIB2_IP_20)) { + /* + * Try to find out which interface is up, configured, + * not loopback, and on the same subnet as the client. + * Save its address and netmask. + */ + map = (mib2_ipAddrEntry_t *)(void *)item->valp; + while ((char *)map < item->valp + item->length) { + in_addr_t addr, mask, net; + int ifflags; + + ifflags = map->ipAdEntInfo.ae_flags; + addr = map->ipAdEntAddr; + mask = map->ipAdEntNetMask; + net = addr & mask; + + if ((ifflags & IFF_LOOPBACK | IFF_UP) == + IFF_UP && addr != INADDR_ANY && + net == (client_addr.s_addr & mask)) { + interface_addr.s_addr = addr; + interface_mask.s_addr = mask; + found = B_TRUE; + break; + } + map++; + } + } + } + + /* + * If this exercise found no interface on the same subnet as + * the client, then we can't suggest any router address to + * use. + */ + if (interface_addr.s_addr == 0) { + if (debug) + msgout("get_ip_route: no interface on same net " + "as client"); + (void) close(sd); + free_itemlist(item_list); + return (0); + } + + /* + * If we are a router, we return to client the address of our + * interface on the same net as the client. + */ + if (ip_forwarding == 1) { + if (debug) + msgout("get_ip_route: returning local addr %s", + inet_ntoa(interface_addr)); + (void) close(sd); + free_itemlist(item_list); + return (interface_addr.s_addr); + } + + if (debug) { + msgout("interface_addr = %s.", inet_ntoa(interface_addr)); + msgout("interface_mask = %s", inet_ntoa(interface_mask)); + } + + + /* + * The routing table group. + */ + for (item = item_list; item; item = item->next_item) { + if ((item->group == MIB2_IP) && (item->mib_id == MIB2_IP_21)) { + if (debug) + msgout("%lu records for ipRouteEntryTable", + item->length / + sizeof (mib2_ipRouteEntry_t)); + + for (rp = (mib2_ipRouteEntry_t *)(void *)item->valp; + (char *)rp < item->valp + item->length; + rp++) { + if (debug >= 2) + msgout("ire_type = %d, next_hop = 0x%x", + rp->ipRouteInfo.re_ire_type, + rp->ipRouteNextHop); + + /* + * We are only interested in real + * gateway routes. + */ + if ((rp->ipRouteInfo.re_ire_type != + IRE_DEFAULT) && + (rp->ipRouteInfo.re_ire_type != + IRE_PREFIX) && + (rp->ipRouteInfo.re_ire_type != + IRE_HOST) && + (rp->ipRouteInfo.re_ire_type != + IRE_HOST_REDIRECT)) + continue; + + /* + * We are only interested in routes with + * a next hop on the same subnet as + * the client. + */ + if ((rp->ipRouteNextHop & + interface_mask.s_addr) != + (interface_addr.s_addr & + interface_mask.s_addr)) + continue; + + /* + * We have a valid route. Give preference + * to default routes. + */ + if ((rp->ipRouteDest == 0) || + (best_router.s_addr == 0)) + best_router.s_addr = + rp->ipRouteNextHop; + } + } + } + + if (debug && (best_router.s_addr == 0)) + msgout("get_ip_route: no route found for client"); + + (void) close(sd); + free_itemlist(item_list); + return (best_router.s_addr); +} + +/* + * Return address of server interface closest to client. + * + * If the server has only a single IP address return it. Otherwise check + * if the server has an interface on the same subnet as the client and + * return the address of that interface. + */ + +in_addr_t +find_best_server_int(char **addr_list, char *client_name) +{ + in_addr_t server_addr = 0; + struct hostent h, *hp; + char hbuf[NSS_BUFLEN_HOSTS]; + int err; + struct in_addr client_addr; + mib_item_t *item_list; + mib_item_t *item; + int sd; + mib2_ipAddrEntry_t *map; + in_addr_t client_net = 0, client_mask = 0; + boolean_t found_client_int; + + (void) memcpy(&server_addr, addr_list[0], sizeof (in_addr_t)); + if (addr_list[1] == NULL) + return (server_addr); + + hp = gethostbyname_r(client_name, &h, hbuf, sizeof (hbuf), &err); + if (hp == NULL) + return (server_addr); + (void) memcpy(&client_addr, hp->h_addr_list[0], sizeof (client_addr)); + + /* open a stream to IP */ + sd = open("/dev/ip", O_RDWR); + if (sd == -1) { + perror("ip open"); + (void) close(sd); + msgout("can't open mib stream"); + return (server_addr); + } + + /* send down a request and suck up all the mib info from IP */ + if ((item_list = mibget(sd)) == nilp(mib_item_t)) { + msgout("mibget() failed"); + (void) close(sd); + return (server_addr); + } + (void) close(sd); + + /* + * Search through the list for our interface which is on the same + * subnet as the client and get the netmask. + */ + for (item = item_list, found_client_int = B_FALSE; + item != NULL && !found_client_int; item = item->next_item) { + if ((item->group == MIB2_IP) && (item->mib_id == MIB2_IP_20)) { + /* + * Try to find out which interface is up, configured, + * not loopback, and on the same subnet as the client. + * Save its address and netmask. + */ + map = (mib2_ipAddrEntry_t *)(void *)item->valp; + while ((char *)map < item->valp + item->length) { + in_addr_t addr, mask, net; + int ifflags; + + ifflags = map->ipAdEntInfo.ae_flags; + addr = map->ipAdEntAddr; + mask = map->ipAdEntNetMask; + net = addr & mask; + + if ((ifflags & IFF_LOOPBACK|IFF_UP) == IFF_UP && + addr != INADDR_ANY && + (client_addr.s_addr & mask) == net) { + client_net = net; + client_mask = mask; + found_client_int = B_TRUE; + break; + } + map++; + } + } + } + + /* + * If we found the interface check which is the best IP address. + */ + if (found_client_int) { + while (*addr_list != NULL) { + in_addr_t addr; + + (void) memcpy(&addr, *addr_list, sizeof (in_addr_t)); + if ((addr & client_mask) == client_net) { + server_addr = addr; + break; + } + addr_list++; + } + } + + if (debug && server_addr == 0) + msgout("No usable interface for returning reply"); + + free_itemlist(item_list); + return (server_addr); +} diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_private.h b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_private.h new file mode 100644 index 0000000000..4a5dcb00ab --- /dev/null +++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_private.h @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2001-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _BOOTPARAM_PRIVATE_H +#define _BOOTPARAM_PRIVATE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +extern int debug; + +extern void msgout(char *, ...); +extern in_addr_t get_ip_route(struct in_addr); +extern in_addr_t find_best_server_int(char **, char *); + +#ifdef __cplusplus +} +#endif + +#endif /* _BOOTPARAM_PRIVATE_H */ diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_svc.c b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_svc.c new file mode 100644 index 0000000000..b4095c5fe2 --- /dev/null +++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_svc.c @@ -0,0 +1,261 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1998-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <rpc/rpc.h> +#include <memory.h> +#include <stropts.h> +#include <netconfig.h> +#include <stropts.h> +#include <sys/termios.h> +#include <syslog.h> +#include <rpcsvc/bootparam_prot.h> + +#include "bootparam_private.h" + +#define _RPCSVC_CLOSEDOWN 120 + +int debug = 0; + +static void bootparamprog_1(struct svc_req *, register SVCXPRT *); +static void closedown(int); + +static int server_child = 0; /* program was started by another server */ +static int _rpcsvcdirty; /* Still serving ? */ + +int +main(int argc, char *argv[]) +{ + pid_t pid; + int c; + char *progname = argv[0]; + int connmaxrec = RPC_MAXDATASIZE; + + while ((c = getopt(argc, argv, "d")) != -1) + switch ((char)c) { + case 'd': + debug++; + break; + default: + (void) fprintf(stderr, "usage: %s [-d]\n", progname); + exit(EXIT_FAILURE); + } + + + /* + * Set non-blocking mode and maximum record size for + * connection oriented RPC transports. + */ + if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) { + msgout("unable to set maximum RPC record size"); + } + + /* + * If stdin looks like a TLI endpoint, we assume + * that we were started by a port monitor. If + * t_getstate fails with TBADF, this is not a + * TLI endpoint. + */ + if (t_getstate(0) != -1 || t_errno != TBADF) { + char *netid; + struct netconfig *nconf = NULL; + SVCXPRT *transp; + int pmclose; + + if ((netid = getenv("NLSPROVIDER")) == NULL) { + if (debug) + msgout("cannot get transport name"); + } else if ((nconf = getnetconfigent(netid)) == NULL) { + if (debug) + msgout("cannot get transport info"); + } + pmclose = (t_getstate(0) != T_DATAXFER); + if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) { + msgout("cannot create server handle"); + exit(EXIT_FAILURE); + } + if (nconf) + freenetconfigent(nconf); + if (!svc_reg(transp, BOOTPARAMPROG, BOOTPARAMVERS, + bootparamprog_1, 0)) { + msgout("unable to register (BOOTPARAMPROG, " + "BOOTPARAMVERS)."); + exit(EXIT_FAILURE); + } + if (pmclose) { + (void) signal(SIGALRM, closedown); + (void) alarm(_RPCSVC_CLOSEDOWN); + } + + svc_run(); + exit(EXIT_FAILURE); + /* NOTREACHED */ + } + + /* + * run this process in the background only if it was started from + * a shell and the debug flag was not given. + */ + if (!server_child && !debug) { + pid = fork(); + if (pid < 0) { + perror("cannot fork"); + exit(EXIT_FAILURE); + } + if (pid) + exit(EXIT_SUCCESS); + + closefrom(0); + (void) setsid(); + } + + /* + * messges go to syslog if the program was started by + * another server, or if it was run from the command line without + * the debug flag. + */ + if (server_child || !debug) + openlog("bootparam_prot", LOG_PID, LOG_DAEMON); + + if (debug) { + if (debug == 1) + msgout("in debug mode."); + else + msgout("in debug mode (level %d).", debug); + } + + if (!svc_create(bootparamprog_1, BOOTPARAMPROG, BOOTPARAMVERS, + "netpath")) { + msgout("unable to create (BOOTPARAMPROG, BOOTPARAMVERS) " + "for netpath."); + exit(EXIT_FAILURE); + } + + svc_run(); + msgout("svc_run returned"); + return (EXIT_FAILURE); +} + +static void +bootparamprog_1(struct svc_req *rqstp, register SVCXPRT *transp) +{ + union { + bp_whoami_arg bootparamproc_whoami_1_arg; + bp_getfile_arg bootparamproc_getfile_1_arg; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + _rpcsvcdirty = 1; + switch (rqstp->rq_proc) { + case NULLPROC: + (void) svc_sendreply(transp, xdr_void, (char *)NULL); + _rpcsvcdirty = 0; + return; + + case BOOTPARAMPROC_WHOAMI: + xdr_argument = xdr_bp_whoami_arg; + xdr_result = xdr_bp_whoami_res; + local = (char *(*)()) bootparamproc_whoami_1; + break; + + case BOOTPARAMPROC_GETFILE: + xdr_argument = xdr_bp_getfile_arg; + xdr_result = xdr_bp_getfile_res; + local = (char *(*)()) bootparamproc_getfile_1; + break; + + default: + svcerr_noproc(transp); + _rpcsvcdirty = 0; + return; + } + (void) memset((char *)&argument, 0, sizeof (argument)); + if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) { + svcerr_decode(transp); + _rpcsvcdirty = 0; + return; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) { + msgout("unable to free arguments"); + exit(EXIT_FAILURE); + } + _rpcsvcdirty = 0; +} + +/*PRINTFLIKE1*/ +void +msgout(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + /* + * messges go to syslog if the program was started by + * another server, or if it was run from the command line without + * the debug flag. + */ + if (server_child || !debug) + vsyslog(LOG_ERR, fmt, ap); + else { + (void) vfprintf(stderr, fmt, ap); + (void) fputc('\n', stderr); + } + va_end(ap); +} + +/* ARGSUSED */ +static void +closedown(int sig) +{ + if (_rpcsvcdirty == 0) { + int size; + int i, openfd; + struct t_info tinfo; + + if (!t_getinfo(0, &tinfo) && (tinfo.servtype == T_CLTS)) + exit(EXIT_SUCCESS); + size = svc_max_pollfd; + for (i = 0, openfd = 0; i < size && openfd < 2; i++) + if (svc_pollfd[i].fd >= 0) + openfd++; + if (openfd <= 1) + exit(EXIT_SUCCESS); + } + (void) alarm(_RPCSVC_CLOSEDOWN); +} diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_xdr.c b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_xdr.c new file mode 100644 index 0000000000..0c9ba4da70 --- /dev/null +++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_xdr.c @@ -0,0 +1,151 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1999,2001 by Sun Microsystems, Inc. + * All rights reserved. + */ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +#include <rpc/rpc.h> +#include <rpcsvc/bootparam_prot.h> + +/* LINTLIBRARY */ + +bool_t +xdr_bp_machine_name_t(XDR *xdrs, bp_machine_name_t *objp) +{ + if (!xdr_string(xdrs, objp, MAX_MACHINE_NAME)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_bp_path_t(XDR *xdrs, bp_path_t *objp) +{ + if (!xdr_string(xdrs, objp, MAX_PATH_LEN)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_bp_fileid_t(XDR *xdrs, bp_fileid_t *objp) +{ + if (!xdr_string(xdrs, objp, MAX_FILEID)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_ip_addr_t(XDR *xdrs, ip_addr_t *objp) +{ + if (!xdr_char(xdrs, &objp->net)) { + return (FALSE); + } + if (!xdr_char(xdrs, &objp->host)) { + return (FALSE); + } + if (!xdr_char(xdrs, &objp->lh)) { + return (FALSE); + } + if (!xdr_char(xdrs, &objp->impno)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_bp_address(XDR *xdrs, bp_address *objp) +{ + if (!xdr_int(xdrs, &objp->address_type)) { + return (FALSE); + } + switch (objp->address_type) { + case IP_ADDR_TYPE: + if (!xdr_ip_addr_t(xdrs, &objp->bp_address_u.ip_addr)) { + return (FALSE); + } + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_bp_whoami_arg(XDR *xdrs, bp_whoami_arg *objp) +{ + if (!xdr_bp_address(xdrs, &objp->client_address)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_bp_whoami_res(XDR *xdrs, bp_whoami_res *objp) +{ + if (!xdr_bp_machine_name_t(xdrs, &objp->client_name)) { + return (FALSE); + } + if (!xdr_bp_machine_name_t(xdrs, &objp->domain_name)) { + return (FALSE); + } + if (!xdr_bp_address(xdrs, &objp->router_address)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_bp_getfile_arg(XDR *xdrs, bp_getfile_arg *objp) +{ + if (!xdr_bp_machine_name_t(xdrs, &objp->client_name)) { + return (FALSE); + } + if (!xdr_bp_fileid_t(xdrs, &objp->file_id)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_bp_getfile_res(XDR *xdrs, bp_getfile_res *objp) +{ + if (!xdr_bp_machine_name_t(xdrs, &objp->server_name)) { + return (FALSE); + } + if (!xdr_bp_address(xdrs, &objp->server_address)) { + return (FALSE); + } + if (!xdr_bp_path_t(xdrs, &objp->server_path)) { + return (FALSE); + } + return (TRUE); +} diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_subr.c b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_subr.c new file mode 100644 index 0000000000..9a9dd01af3 --- /dev/null +++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_subr.c @@ -0,0 +1,299 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 1999-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Subroutines that implement the bootparam services. + */ + +#include <rpcsvc/bootparam_prot.h> +#include <netdb.h> +#include <nlist.h> +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <nsswitch.h> +#include <sys/types.h> +#include <sys/file.h> +#include <sys/socket.h> +#define KERNEL /* to get RTHASHSIZ */ +#include <sys/stream.h> +#include <net/route.h> +#undef KERNEL +#include <net/if.h> /* for structs ifnet and ifaddr */ +#include <netinet/in.h> +#include <netinet/in_var.h> /* for struct in_ifaddr */ +#include <arpa/inet.h> + +#include "bootparam_private.h" + +#define LINESIZE 1024 + +extern int getdomainname(char *, int); +extern int bootparams_getbyname(char *, char *, int); + +static char *wildcard = "*"; +static char domainkey[] = "domain="; +static void getf_printres(bp_getfile_res *); +static void copydomain(char *, char *, int); + +/* + * Whoami turns a client address into a client name + * and suggested route machine. + */ +/*ARGSUSED1*/ +bp_whoami_res * +bootparamproc_whoami_1(bp_whoami_arg *argp, CLIENT *cl) +{ + static bp_whoami_res res; + struct in_addr clnt_addr; + struct in_addr route_addr; + struct hostent *hp; + static char clnt_entry[LINESIZE]; + static char domain[MAX_MACHINE_NAME]; + char *cp; + + if (argp->client_address.address_type != IP_ADDR_TYPE) { + if (debug) { + msgout("Whoami failed: unknown address type %d", + argp->client_address.address_type); + } + return (NULL); + } + (void) memcpy(&clnt_addr, &argp->client_address.bp_address_u.ip_addr, + sizeof (clnt_addr)); + hp = gethostbyaddr((char *)&clnt_addr, sizeof (clnt_addr), AF_INET); + if (hp == NULL) { + if (debug) { + msgout("Whoami failed: gethostbyaddr for %s.", + inet_ntoa(clnt_addr)); + } + return (NULL); + } + + /* + * We only answer requests from clients listed in the database. + */ + if ((bootparams_getbyname(hp->h_name, clnt_entry, + sizeof (clnt_entry)) != __NSW_SUCCESS) && + (bootparams_getbyname(wildcard, clnt_entry, + sizeof (clnt_entry)) != __NSW_SUCCESS)) + return (NULL); + + res.client_name = hp->h_name; + + /* + * The algorithm for determining the client's domain name is: + * 1) look for "domain=" in the client's bootparams line. + * If found, use its value. + * 2) look for a "domain=" entry in the wildcard bootparams + * line (if any). If found, use its value. Otherwise, + * 3) return the domain name of the server answering the + * request. + */ + if (cp = strstr(clnt_entry, domainkey)) { + copydomain(cp + sizeof (domainkey) - 1, domain, + sizeof (domain)); + } else { + /* "domain=" not found - try for wildcard */ + if ((bootparams_getbyname(wildcard, clnt_entry, + sizeof (clnt_entry)) == __NSW_SUCCESS) && + (cp = strstr(clnt_entry, domainkey))) { + copydomain(cp + sizeof (domainkey) - 1, domain, + sizeof (domain)); + } else { + (void) getdomainname(domain, sizeof (domain)); + } + } + res.domain_name = domain; + + res.router_address.address_type = IP_ADDR_TYPE; + route_addr.s_addr = get_ip_route(clnt_addr); + (void) memcpy(&res.router_address.bp_address_u.ip_addr, + &route_addr, + sizeof (res.router_address.bp_address_u.ip_addr)); + + if (debug) { + struct in_addr in; + + (void) memcpy(&in.s_addr, + &res.router_address.bp_address_u.ip_addr, + sizeof (in.s_addr)); + msgout("Whoami returning name = %s, router address = %s", + res.client_name, + inet_ntoa(in)); + } + return (&res); +} + +/* + * Getfile gets the client name and the key and returns its server + * and the pathname for that key. + */ +/*ARGSUSED1*/ +bp_getfile_res * +bootparamproc_getfile_1(bp_getfile_arg *argp, CLIENT *cl) +{ + static bp_getfile_res res; + static char clnt_entry[LINESIZE]; + struct hostent *hp; + char *cp; + char filekey[LINESIZE]; + char *server_hostname; + char *path_on_server; + int do_wildcard = 0; + static char *zero_len_string = ""; + + /* + * The bootparams_getbyname() library function looks up a + * "client entry" using using the client's hostname as the + * key. A client entry consists of a string of "file entries" + * separated by white space. Each file entry is of the form: + * + * file_key=server_hostname:path_on_server + * + * In the getfile RPC call, the client gives us his hostname + * and a file_key. We lookup his client entry, then locate a + * file entry matching that file_key. We then parse out the + * server_hostname and path_on_server from the file entry, map + * the server_hostname to an IP address, and return both the + * IP address and path_on_server back to the client. + */ + + /* make the client's file key int a string we can use for matching */ + (void) strncpy(filekey, argp->file_id, sizeof (filekey) - 2); + filekey[sizeof (filekey) - 2] = '\0'; + (void) strcat(filekey, "="); + + if (bootparams_getbyname(argp->client_name, clnt_entry, + sizeof (clnt_entry)) == __NSW_SUCCESS) { + /* locate the file_key in the client's entry */ + cp = strstr(clnt_entry, filekey); + if (cp == NULL) + do_wildcard++; + + } else + do_wildcard++; + + if (do_wildcard) { + if (bootparams_getbyname(wildcard, clnt_entry, + sizeof (clnt_entry)) != __NSW_SUCCESS) + return (NULL); + + /* locate the file_key in the client's entry */ + cp = strstr(clnt_entry, filekey); + if (cp == NULL) + return (NULL); + } + + /* locate the "data" part of file entry (r.h.s. of "=") */ + cp = strchr(cp, '='); + if (cp == NULL) + return (NULL); + cp++; + if (*cp == '\0') + return (NULL); + server_hostname = cp; + + /* null-terminate server_hostname and parse path_on_server */ + cp = strchr(server_hostname, ':'); + if (cp == NULL) + return (NULL); + *cp = '\0'; + cp++; + /* strtok() will null-terminate path_on_server */ + path_on_server = strtok(cp, "\t\n "); + if (path_on_server == NULL) + path_on_server = zero_len_string; + + res.server_name = server_hostname; + res.server_path = path_on_server; + if (*res.server_name == 0) { + res.server_address.address_type = IP_ADDR_TYPE; + (void) memset(&res.server_address.bp_address_u.ip_addr, 0, + sizeof (res.server_address.bp_address_u.ip_addr)); + } else { + in_addr_t addr; + + if ((hp = gethostbyname(server_hostname)) != NULL) { + addr = find_best_server_int(hp->h_addr_list, + argp->client_name); + } else { + addr = inet_addr(server_hostname); + if (addr == INADDR_BROADCAST) { + if (debug) { + msgout("getfile_1: gethostbyname(%s) " + "failed", res.server_name); + } + return (NULL); + } + } + res.server_address.address_type = IP_ADDR_TYPE; + (void) memcpy(&res.server_address.bp_address_u.ip_addr, + &addr, sizeof (res.server_address.bp_address_u.ip_addr)); + } + if (debug) { + getf_printres(&res); + } + return (&res); +} + +void +getf_printres(bp_getfile_res *res) +{ + struct in_addr in; + + (void) memcpy(&in.s_addr, &res->server_address.bp_address_u.ip_addr, + sizeof (in.s_addr)); + msgout("getfile_1: file is \"%s\" %s \"%s\"", + res->server_name, + inet_ntoa(in), + res->server_path); +} + +/* + * Used when we've found a "domain=" key, this function copies characters + * from source to target until we come upon either a NULL or whitespace is + * found in the source string, or we run out of room in the target. + * + */ +void +copydomain(char *source, char *target, int len) +{ + int n; /* number of characters copies */; + + len--; /* leave room for terminating '\0' */ + if (source) + for (n = 0; *source != '\0' && n < len; n++) + if (isspace((int)*source)) + break; + else + *target++ = *source++; + + *target = '\0'; +} diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparams.xml b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparams.xml new file mode 100644 index 0000000000..d15b26e454 --- /dev/null +++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparams.xml @@ -0,0 +1,113 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2005 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License, Version 1.0 only + (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 + + ident "%Z%%M% %I% %E% SMI" + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. + + Service manifest for the rpc.bootparamd service. +--> + +<service_bundle type='manifest' name='SUNWbsr:bootparams'> + +<service + name='network/rpc/bootparams' + type='service' + version='1'> + + <create_default_instance enabled='false' /> + + <single_instance/> + + <dependency + name='network' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/milestone/network' /> + </dependency> + + <dependency + name='name-services' + grouping='require_all' + restart_on='refresh' + type='service'> + <service_fmri value='svc:/milestone/name-services' /> + </dependency> + + <dependency name='rpcbind' + grouping='require_all' + restart_on='restart' + type='service'> + <service_fmri value='svc:/network/rpc/bind'/> + </dependency> + + <dependent + name='rpc-bootparams_multi-user-server' + grouping='optional_all' + restart_on='none'> + <service_fmri + value='svc:/milestone/multi-user-server' /> + </dependent> + + <exec_method + type='method' + name='start' + exec='/usr/sbin/rpc.bootparamd' + timeout_seconds='60' /> + + <exec_method + type='method' + name='stop' + exec=':kill' + timeout_seconds='60' /> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + boot parameter server + </loctext> + </common_name> + + <description> + <loctext xml:lang='C'> +rpc.bootparamd provides information from a bootparams database to +diskless clients at boot time. + </loctext> + </description> + <documentation> + <manpage title='rpc.bootparamd' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/rpcsvc/rpc.rusersd.c b/usr/src/cmd/rpcsvc/rpc.rusersd.c new file mode 100644 index 0000000000..190b76c6b2 --- /dev/null +++ b/usr/src/cmd/rpcsvc/rpc.rusersd.c @@ -0,0 +1,484 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 1998 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ +/* + * University Copyright- Copyright (c) 1982, 1986, 1988 + * The Regents of the University of California + * All Rights Reserved + * + * University Acknowledgment- Portions of this document are derived from + * software developed by the University of California, Berkeley, and its + * contributors. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <signal.h> +#include <sys/stat.h> +#include <rpc/rpc.h> +#include <memory.h> +#include <netconfig.h> +#include <stropts.h> +#include <syslog.h> +#include <utmpx.h> +#include <rpcsvc/rusers.h> +#include <sys/resource.h> +#include <limits.h> + +#ifdef DEBUG +#define RPC_SVC_FG +#endif + +#define _RPCSVC_CLOSEDOWN 120 + +static void rusers_service(); +static void closedown(); +static void msgout(); +static unsigned min(); + +static int _rpcpmstart; /* Started by a port monitor ? */ +static int _rpcfdtype; /* Whether Stream or Datagram ? */ +static int _rpcsvcdirty; /* Still serving ? */ +static int _rpcsvcrecent; /* set when we serivce a request; tested */ + /* and cleared by closedown() routine */ + +#define DIV60(t) ((t+30)/60) /* x/60 rounded */ + +#define ALL_ENTRIES 1 +#define REAL_USERS 0 + +utmp_array utmp_array_res; +int used_array_len = 0; +struct utmpidlearr utmpidlearr; + +main() +{ + pid_t pid; + int i; + int connmaxrec = RPC_MAXDATASIZE; + + /* + * Set non-blocking mode and maximum record size for + * connection oriented RPC transports. + */ + if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) { + msgout("unable to set maximum RPC record size"); + } + + /* + * If stdin looks like a TLI endpoint, we assume + * that we were started by a port monitor. If + * t_getstate fails with TBADF, this is not a + * TLI endpoint. + */ + if (t_getstate(0) != -1 || t_errno != TBADF) { + char *netid; + struct netconfig *nconf = NULL; + SVCXPRT *transp; + int pmclose; + extern char *getenv(); + + _rpcpmstart = 1; + openlog("rusers", LOG_PID, LOG_DAEMON); + if ((netid = getenv("NLSPROVIDER")) == NULL) { +#ifdef DEBUG + msgout("cannot get transport name"); +#endif + } else if ((nconf = getnetconfigent(netid)) == NULL) { +#ifdef DEBUG + msgout("cannot get transport info"); +#endif + } + if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) { + msgout("cannot create server handle"); + exit(1); + } + if (nconf) + freenetconfigent(nconf); + if (!svc_reg(transp, RUSERSPROG, RUSERSVERS_3, rusers_service, + 0)) { + msgout("unable to register (RUSERSPROG, RUSERSVERS_3)."); + exit(1); + } + if (!svc_reg(transp, RUSERSPROG, RUSERSVERS_IDLE, + rusers_service, 0)) { + msgout("unable to register (RUSERSPROG, RUSERSVERS_IDLE)."); + exit(1); + } + (void) signal(SIGALRM, closedown); + (void) alarm(_RPCSVC_CLOSEDOWN); + svc_run(); + msgout("svc_run returned"); + exit(1); + /* NOTREACHED */ + } +#ifndef RPC_SVC_FG + pid = fork(); + if (pid < 0) { + perror("rpc.rusersd: cannot fork"); + exit(1); + } + if (pid) + exit(0); + for (i = 0; i < 20; i++) + (void) close(i); + setsid(); + openlog("rusers", LOG_PID, LOG_DAEMON); +#endif + if (!svc_create(rusers_service, RUSERSPROG, RUSERSVERS_3, "netpath")) { + msgout("unable to create (RUSERSPROG, RUSERSVERS_3) for netpath"); + exit(1); + } + if (!svc_create(rusers_service, RUSERSPROG, RUSERSVERS_IDLE, + "netpath")) { + msgout( + "unable to create (RUSERSPROG, RUSERSVERS_IDLE) for netpath"); + exit(1); + } + + svc_run(); + msgout("svc_run returned"); + exit(1); + /* NOTREACHED */ +} + + +/* + * This routine gets the user information. + * "all" specifies whether all listings should be counted, or only those of + * type "USER_PROCESS". + * "version" is either RUSERSVERS_IDLE or RUSERSVERS_3. If anything else, + * just a count is returned. + * "limit" specifies the maximum number of entries to be processed. + * + * For both versions, the results are placed into an external variable. + * For RUSERSVERS_IDLE, this routine mallocs entries in a vector as it + * processed each utmpx entry. These malloc'd entries must be freed after the + * results are returned. + * For RUSERSVERS_3, this routine uses array entries that are malloc'd prior + * to this routine being called. "limit" is the number of elements available. + */ +int +getutmpx_3(all, version, limit) + int all; /* give all listings? */ + int version; /* version 2 or 3 */ + int limit; /* limits users returned, 0 means no limit */ +{ + struct utmpx *utent; + struct utmpidle **q = utmpidlearr.uia_arr; + int minidle; + int cnt = 0; + time_t now; + extern char *s_malodup(); + + time(&now); /* only one call to time() for this rpc call */ + setutxent(); /* reset the utmpx file */ + while ((utent = getutxent()) != NULL && (limit == 0 || cnt < limit)) { + if (utent->ut_line[0] == '\0' || utent->ut_user[0] == '\0') + continue; + /* + * List only user processes. + * XXX modified to exclude cmdtool style window entries. + */ + if ((all == REAL_USERS) && ((utent->ut_type != USER_PROCESS) || + nonuserx(*utent))) + continue; + + if (version == RUSERSVERS_IDLE) { + /* + * need to free this; done after svc_sendreply. + */ + *q = (struct utmpidle *) + malloc(sizeof (struct utmpidle)); + (*q)->ui_idle = findidle(utent->ut_line, + sizeof (utent->ut_line), now); + if (strncmp(utent->ut_line, "console", + strlen("console")) == 0) { + (*q)->ui_idle = min((*q)->ui_idle, + console_idle(now)); + } + usys5to_ru(utent, &((*q)->ui_utmp)); +#ifdef DEBUG + printf("%-*s %-*s %s; idle %d", + sizeof (utent->ut_line), + utent->ut_line, + sizeof (utent->ut_name), + utent->ut_name, + ctime(&utent->ut_xtime), + (*q)->ui_idle); +#endif + q++; + } else if (version == RUSERSVERS_3) { +#define uav utmp_array_res.utmp_array_val + + uav[cnt].ut_host = + s_malodup(utent->ut_host, utent->ut_syslen); + uav[cnt].ut_user = s_malodup(utent->ut_user, + sizeof (utent->ut_user)); + uav[cnt].ut_line = s_malodup(utent->ut_line, + sizeof (utent->ut_line)); + uav[cnt].ut_type = utent->ut_type; + uav[cnt].ut_time = utent->ut_xtime; + uav[cnt].ut_idle = findidle(utent->ut_line, + sizeof (utent->ut_line), now); + if (strncmp(utent->ut_line, "console", + strlen("console")) == 0) { + uav[cnt].ut_idle = + min(uav[cnt].ut_idle, + console_idle(now)); + } +#ifdef DEBUG + printf("user: %-10s line: %-10s %s; idle %d (%s)\n", + uav[cnt].ut_line, uav[cnt].ut_user, + ctime((time_t *)&uav[cnt].ut_time), + uav[cnt].ut_idle, uav[cnt].ut_host); +#endif +#undef uav + } + cnt++; + } + return (cnt); +} + +/* + * "string" is a character array with maximum size "size". Return a + * malloc'd string that's a duplicate of the string. + */ +char * +s_malodup(string, size) +char *string; +int size; +{ + char *tmp; + + tmp = (char *)malloc(size+1); + if (tmp == NULL) { + msgout("rpc.rusersd: malloc failed (2)"); + return (NULL); + } + strncpy(tmp, string, size); + tmp[size] = '\0'; + return (tmp); +} + + +int +console_idle(now) + time_t now; +{ + /* + * On the console, the user may be running a window system; if so, + * their activity will show up in the last-access times of + * "/dev/kbd" and "/dev/mouse", so take the minimum of the idle + * times on those two devices and "/dev/console" and treat that as + * the idle time. + */ + return (min((unsigned)findidle("kbd", strlen("kbd"), now), + (unsigned)findidle("mouse", strlen("mouse"), now))); +} + +static void +rusers_service(rqstp, transp) + register struct svc_req *rqstp; + register SVCXPRT *transp; +{ + int i; + int cnt; + char *replyerr = "rpc.rusersd: error replying to request"; + + _rpcsvcrecent = _rpcsvcdirty = 1; + switch (rqstp->rq_proc) { + case 0: + if (svc_sendreply(transp, xdr_void, 0) == FALSE) { + msgout(replyerr); + } + break; + case RUSERSPROC_NUM: + cnt = getutmpx_3(REAL_USERS, 0, 0); + if (!svc_sendreply(transp, xdr_u_long, (caddr_t)&cnt)) + msgout(replyerr); + break; + case RUSERSPROC_NAMES: + case RUSERSPROC_ALLNAMES: + if (rqstp->rq_vers == RUSERSVERS_IDLE) { + utmpidlearr.uia_arr = (struct utmpidle **) + malloc(MAXUSERS*sizeof (struct utmpidle *)); + utmpidlearr.uia_cnt = getutmpx_3(rqstp->rq_proc == + RUSERSPROC_ALLNAMES, + RUSERSVERS_IDLE, MAXUSERS); + if (!svc_sendreply(transp, xdr_utmpidlearr, + (caddr_t)&utmpidlearr)) + msgout(replyerr); + for (i = 0; i < utmpidlearr.uia_cnt; i++) { + free(utmpidlearr.uia_arr[i]); + } + free(utmpidlearr.uia_arr); + } else if (rqstp->rq_vers == RUSERSVERS_3) { + int entries, alloc_array_len; + + /* + * Always free strings from previous results array + */ + for (i = 0; i < used_array_len; i++) { + free_ua_entry(&utmp_array_res.utmp_array_val[i]); + } + entries = (rqstp->rq_proc == RUSERSPROC_ALLNAMES); + cnt = getutmpx_3(entries, 0, 0); /* get cnt */ + if (cnt > utmp_array_res.utmp_array_len) { + free(utmp_array_res.utmp_array_val); + utmp_array_res.utmp_array_len = 0; + utmp_array_res.utmp_array_val = (rusers_utmp *) + malloc(cnt * sizeof (rusers_utmp)); + if (utmp_array_res.utmp_array_val == NULL) { + msgout("rpc.rusersd: malloc failed (1)"); + break; + } + alloc_array_len = cnt; + } else { + alloc_array_len = utmp_array_res.utmp_array_len; + } + cnt = getutmpx_3(entries, RUSERSVERS_3, cnt); + utmp_array_res.utmp_array_len = used_array_len = cnt; + if (!svc_sendreply(transp, xdr_utmp_array, + (caddr_t)&utmp_array_res)) + msgout(replyerr); + utmp_array_res.utmp_array_len = alloc_array_len; + } + break; + default: + svcerr_noproc(transp); + break; + } + _rpcsvcdirty = 0; + +} + +free_ua_entry(uap) +rusers_utmp *uap; +{ + if (uap == NULL) + return; + if (uap->ut_user) + free(uap->ut_user); + if (uap->ut_line) + free(uap->ut_line); + if (uap->ut_host) + free(uap->ut_host); +} + + + +/* find & return number of minutes current tty has been idle */ +findidle(name, ln, now) + char *name; + int ln; + time_t now; +{ + struct stat stbuf; + long lastaction, diff; + char ttyname[32]; + + strcpy(ttyname, "/dev/"); + strncat(ttyname, name, ln); + if (stat(ttyname, &stbuf) < 0) + return (INT_MAX); + lastaction = stbuf.st_atime; + diff = now - lastaction; + diff = DIV60(diff); + if (diff < 0) diff = 0; + return (diff); +} + +static +usys5to_ru(s5, bss) + struct utmpx *s5; + struct ru_utmp *bss; +{ + int i; + +#ifdef DEBUG + printf("sizeof (bss->ut_host) == %d\n", sizeof (bss->ut_host)); +#endif + strncpy(bss->ut_name, s5->ut_name, sizeof (bss->ut_name)); + strncpy(bss->ut_line, s5->ut_line, sizeof (bss->ut_line)); + strncpy(bss->ut_host, s5->ut_host, sizeof (bss->ut_host)); + bss->ut_time = s5->ut_xtime; +} + +static void +msgout(msg) + char *msg; +{ +#ifdef RPC_SVC_FG + if (_rpcpmstart) + syslog(LOG_ERR, msg); + else + (void) fprintf(stderr, "%s\n", msg); +#else + syslog(LOG_ERR, msg); +#endif +} + +static void +closedown(sig) +int sig; +{ + if (_rpcsvcrecent) { + _rpcsvcrecent = 0; + } else { + if (_rpcsvcdirty == 0) { + int i, openfd; + struct t_info tinfo; + + if (t_getinfo(0, &tinfo) || (tinfo.servtype == T_CLTS)) + exit(0); + + for (i = 0, openfd = 0; + i < svc_max_pollfd && openfd < 2; + i++) { + if (svc_pollfd[i].fd >= 0) + openfd++; + } + + if (openfd <= 1) + exit(0); + } + } + (void) signal(SIGALRM, closedown); + (void) alarm(_RPCSVC_CLOSEDOWN); +} + +unsigned +min(a, b) +unsigned a; +unsigned b; +{ + if (a < b) + return (a); + else + return (b); +} diff --git a/usr/src/cmd/rpcsvc/rstat.xml b/usr/src/cmd/rpcsvc/rstat.xml new file mode 100644 index 0000000000..8e33cf17bd --- /dev/null +++ b/usr/src/cmd/rpcsvc/rstat.xml @@ -0,0 +1,112 @@ +<?xml version='1.0'?> +<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'> + +<!-- + Copyright 2005 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License, Version 1.0 only + (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 + + ident "%Z%%M% %I% %E% SMI" + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. + + Service manifest for rpc.rstatd +--> + +<service_bundle type='manifest' name='SUNWrcmdr:rstatd'> + +<service + name='network/rpc/rstat' + type='service' + version='1'> + + <create_default_instance enabled='false' /> + + <restarter> + <service_fmri value='svc:/network/inetd:default' /> + </restarter> + + <dependency name='rpcbind' + grouping='require_all' + restart_on='restart' + type='service'> + <service_fmri value='svc:/network/rpc/bind' /> + </dependency> + + <exec_method + type='method' + name='inetd_start' + exec='/usr/lib/netsvc/rstat/rpc.rstatd' + timeout_seconds='0'> + <method_context> + <method_credential user='root' group='root' /> + </method_context> + </exec_method> + + <exec_method + type='method' + name='inetd_offline' + exec=':kill_process' + timeout_seconds='0'> + </exec_method> + + <exec_method + type='method' + name='inetd_disable' + exec=':kill' + timeout_seconds='0'> + </exec_method> + + <property_group name='inetd' type='framework'> + <stability value='Evolving' /> + <propval name='name' type='astring' value='rstatd' /> + <propval name='endpoint_type' type='astring' value='tli' /> + <propval name='proto' type='astring' value='datagram_v' + override='true' /> + <propval name='isrpc' type='boolean' value='true' /> + <propval name='rpc_low_version' type='integer' value='2' /> + <propval name='rpc_high_version' type='integer' value='4' /> + <propval name='wait' type='boolean' value='true' /> + </property_group> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + kernel statistics server + </loctext> + </common_name> + <documentation> + <manpage title='rpc.rstatd' section='1M' + manpath='/usr/share/man' /> + <manpage title='rstatd' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> + +</service> + +</service_bundle> diff --git a/usr/src/cmd/rpcsvc/rstat_main.c b/usr/src/cmd/rpcsvc/rstat_main.c new file mode 100644 index 0000000000..272986d3e9 --- /dev/null +++ b/usr/src/cmd/rpcsvc/rstat_main.c @@ -0,0 +1,174 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1991-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "rstat.h" +#include "rstat_v2.h" +#include <stdio.h> +#include <stdlib.h> /* getenv, exit */ +#include <signal.h> +#include <sys/types.h> +#include <memory.h> +#include <stropts.h> +#include <netconfig.h> +#include <syslog.h> + +#ifdef __STDC__ +#define SIG_PF void(*)(int) +#endif + +#ifdef DEBUG +#define RPC_SVC_FG +#endif + + +int _rpcpmstart; /* Started by a port monitor ? */ +int _rpcfdtype; /* Whether Stream or Datagram ? */ +int _rpcsvcdirty; /* Still serving ? */ + +static void _msgout(/*char *msg*/); +static void closedown(); + +extern void rstatprog_4(/*struct svc_req *rqstp, SVCXPRT *transp*/); +extern void rstatprog_3(/*struct svc_req *rqstp, SVCXPRT *transp*/); +extern void rstatprog_2(/*struct svc_req *rqstp, SVCXPRT *transp*/); + +main() +{ + pid_t pid; + int i; + + /* + * If stdin looks like a TLI endpoint, we assume + * that we were started by a port monitor. If + * t_getstate fails with TBADF, this is not a + * TLI endpoint. + */ + if (t_getstate(0) != -1 || t_errno != TBADF) { + char *netid; + struct netconfig *nconf = NULL; + SVCXPRT *transp; + + _rpcpmstart = 1; + openlog("rstatd", LOG_PID, LOG_DAEMON); + if ((netid = getenv("NLSPROVIDER")) == NULL) { +#ifdef DEBUG + _msgout("cannot get transport name"); +#endif + } else if ((nconf = getnetconfigent(netid)) == NULL) { +#ifdef DEBUG + _msgout("cannot get transport info"); +#endif + } + if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) { + _msgout("cannot create server handle"); + exit(1); + } + if (nconf) + freenetconfigent(nconf); + if (!svc_reg(transp, RSTATPROG, RSTATVERS_VAR, rstatprog_4, + 0)) { + _msgout("unable to register (RSTATPROG, RSTATVERS_VAR)."); + exit(1); + } + if (!svc_reg(transp, RSTATPROG, RSTATVERS_TIME, rstatprog_3, + 0)) { + _msgout("unable to register (RSTATPROG, RSTATVERS_TIME)."); + exit(1); + } + if (!svc_reg(transp, RSTATPROG, RSTATVERS_SWTCH, rstatprog_2, + 0)) { + _msgout("unable to register (RSTATPROG, RSTATVERS_SWTCH)."); + exit(1); + } + svc_run(); + exit(1); + /* NOTREACHED */ + } + else { +#ifndef RPC_SVC_FG + pid = fork(); + if (pid < 0) { + perror("cannot fork"); + exit(1); + } + if (pid) + exit(0); + closefrom(0); + i = open("/dev/console", 2); + (void) dup2(i, 1); + (void) dup2(i, 2); + setsid(); + openlog("rstatd", LOG_PID, LOG_DAEMON); +#endif + } + if (!svc_create(rstatprog_4, RSTATPROG, RSTATVERS_VAR, "datagram_v")) { + _msgout("unable to create (RSTATPROG, RSTATVERS_VAR) for datagram_v."); + exit(1); + } + if (!svc_create(rstatprog_3, RSTATPROG, RSTATVERS_TIME, + "datagram_v")) { + _msgout("unable to create (RSTATPROG, RSTATVERS_TIME) for datagram_v."); + exit(1); + } + if (!svc_create(rstatprog_4, RSTATPROG, RSTATVERS_VAR, "circuit_v")) { + _msgout("unable to create (RSTATPROG, RSTATVERS_VAR) for circuit_v."); + exit(1); + } + if (!svc_create(rstatprog_3, RSTATPROG, RSTATVERS_TIME, "circuit_v")) { + _msgout("unable to create (RSTATPROG, RSTATVERS_TIME) for circuit_v."); + exit(1); + } + + /* + * V2 supported on datagram transports *only* + */ + if (!svc_create(rstatprog_2, RSTATPROG, RSTATVERS_SWTCH, + "datagram_v")) { + _msgout("unable to create (RSTATPROG, RSTATVERS_SWTCH) for datagram_v."); + exit(1); + } + + svc_run(); + _msgout("svc_run returned"); + exit(1); + /* NOTREACHED */ +} + +static +void _msgout(msg) + char *msg; +{ +#ifdef RPC_SVC_FG + if (_rpcpmstart) + syslog(LOG_ERR, msg); + else + (void) fprintf(stderr, "%s\n", msg); +#else + syslog(LOG_ERR, msg); +#endif +} diff --git a/usr/src/cmd/rpcsvc/rstat_proc.c b/usr/src/cmd/rpcsvc/rstat_proc.c new file mode 100644 index 0000000000..a871985bd8 --- /dev/null +++ b/usr/src/cmd/rpcsvc/rstat_proc.c @@ -0,0 +1,1095 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * rstat service: built with rstat.x + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <signal.h> +#include <utmpx.h> +#include <nlist.h> +#include <fcntl.h> +#include <syslog.h> +#include <kstat.h> + +#include <rpc/rpc.h> + +#include <sys/socket.h> +#include <sys/cpuvar.h> +#include <sys/sysinfo.h> +#include <sys/systm.h> +#include <errno.h> +#include <sys/stropts.h> +#include <sys/tihdr.h> +#include <sys/sysmacros.h> + +#include <net/if.h> +#include <inet/mib2.h> + +#include "rstat.h" +#include "rstat_v2.h" + +typedef struct { + kstat_t sys; + kstat_t vm; +} _cpu_stats_t; + +/* + * system and cpu stats + */ +static kstat_ctl_t *kc; /* libkstat cookie */ +static int ncpus; +static _cpu_stats_t *cpu_stats_list = NULL; +static kstat_t *system_misc_ksp; +static kstat_named_t *boot_time_knp; +static kstat_named_t *avenrun_1min_knp, *avenrun_5min_knp, *avenrun_15min_knp; +static int hz; +static struct timeval btm; /* boottime */ + +/* + * network interface stats + */ + +typedef struct mib_item_s { + struct mib_item_s *next_item; + long group; + long mib_id; + long length; + char *valp; +} mib_item_t; + +mib_item_t *netstat_item; + +/* + * disk stats + */ + +struct diskinfo { + struct diskinfo *next; + kstat_t *ks; + kstat_io_t kios; +}; + +#define NULLDISK (struct diskinfo *)0 +static struct diskinfo zerodisk = { NULL, NULL }; +static struct diskinfo *firstdisk = NULLDISK; +static struct diskinfo *lastdisk = NULLDISK; +static struct diskinfo *snip = NULLDISK; +static int ndisks; + +/* + * net stats + */ + +struct netinfo { + struct netinfo *next; + kstat_t *ks; + kstat_named_t *ipackets; + kstat_named_t *opackets; + kstat_named_t *ierrors; + kstat_named_t *oerrors; + kstat_named_t *collisions; +}; + +#define NULLNET (struct netinfo *)0 +static struct netinfo zeronet = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; +static struct netinfo *firstnet = NULLNET; +static struct netinfo *lastnet = NULLNET; +static struct netinfo *netsnip = NULLNET; +static int nnets; + +/* + * Define EXIT_WHEN_IDLE if you are able to have this program invoked + * automatically on demand (as from inetd). When defined, the service + * will terminated after being idle for 120 seconds. + */ + +#define EXIT_WHEN_IDLE 1 + +int sincelastreq = 0; /* number of alarms since last request */ +#ifdef EXIT_WHEN_IDLE +#define CLOSEDOWN 120 /* how long to wait before exiting */ +#endif /* def EXIT_WHEN_IDLE */ + +statstime stats_s3; +statsvar stats_s4; +/* V2 support for backwards compatibility to pre-5.0 systems */ +statsswtch stats_s2; + +static stat_is_init = 0; + +static void fail(int, char *, ...); +static void safe_zalloc(void **, int, int); +static kid_t safe_kstat_read(kstat_ctl_t *, kstat_t *, void *); +static kstat_t *safe_kstat_lookup(kstat_ctl_t *, char *, int, char *); +static void *safe_kstat_data_lookup(kstat_t *, char *); +static void system_stat_init(void); +static int system_stat_load(void); +static void init_disks(void); +static int diskinfo_load(void); +static void init_net(void); +static int netinfo_load(void); + +static void updatestat(int); + +static mib_item_t *mibget(int sd); +static int mibopen(void); +static char *octetstr(char *buf, Octet_t *op, int code); + +static void kstat_copy(kstat_t *, kstat_t *, int); + +static char *cmdname = "rpc.rstatd"; + +#define CPU_STAT(ksp, name) (((kstat_named_t *)safe_kstat_data_lookup( \ + (ksp), (name)))->value.ui64) +static _cpu_stats_t cpu_stats_all = { 0 }; + +static void +stat_init(void) +{ + struct utmpx *utmpx, utmpx_id; + + stat_is_init = 1; + + if ((kc = kstat_open()) == NULL) + fail(1, "kstat_open(): can't open /dev/kstat"); + + /* + * Preallocate minimal set of drive entries. + */ + + if (stats_s4.dk_xfer.dk_xfer_val == NULL) { + stats_s4.dk_xfer.dk_xfer_len = RSTAT_DK_NDRIVE; + stats_s4.dk_xfer.dk_xfer_val = + (int *)calloc(RSTAT_DK_NDRIVE, sizeof (int)); + } + + system_stat_init(); + init_disks(); + init_net(); + + /* + * To get the boot time, use utmpx, which is per-zone, but fall back + * to the system-wide kstat if utmpx is hosed for any reason. + */ + utmpx_id.ut_type = BOOT_TIME; + if ((utmpx = getutxid(&utmpx_id)) != NULL) + btm = utmpx->ut_tv; + else { + btm.tv_sec = boot_time_knp->value.ul; + btm.tv_usec = 0; /* don't bother with usecs for boot time */ + } + endutxent(); + stats_s4.boottime.tv_sec = + stats_s2.boottime.tv_sec = + stats_s3.boottime.tv_sec = btm.tv_sec; + stats_s4.boottime.tv_usec = + stats_s2.boottime.tv_usec = + stats_s3.boottime.tv_usec = btm.tv_usec; + + updatestat(0); + alarm(1); + signal(SIGALRM, updatestat); + sleep(2); /* allow for one wake-up */ +} + +statsvar * +rstatproc_stats_4_svc(argp, svcrq) +void *argp; +struct svc_req *svcrq; +{ + if (! stat_is_init) + stat_init(); +#ifdef EXIT_WHEN_IDLE + sincelastreq = 0; +#endif + return (&stats_s4); +} + +statstime * +rstatproc_stats_3_svc(argp, svcrq) +void *argp; +struct svc_req *svcrq; +{ + if (! stat_is_init) + stat_init(); +#ifdef EXIT_WHEN_IDLE + sincelastreq = 0; +#endif + return (&stats_s3); +} + +statsswtch * +rstatproc_stats_2_svc(argp, svcrq) +void *argp; +struct svc_req *svcrq; +{ + if (! stat_is_init) + stat_init(); +#ifdef EXIT_WHEN_IDLE + sincelastreq = 0; +#endif + return (&stats_s2); +} + + +uint_t * +rstatproc_havedisk_4_svc(argp, svcrq) +void *argp; +struct svc_req *svcrq; +{ + return (rstatproc_havedisk_3_svc(argp, svcrq)); +} + +uint_t * +rstatproc_havedisk_3_svc(argp, svcrq) +void *argp; +struct svc_req *svcrq; +{ + static uint_t have; + + if (! stat_is_init) + stat_init(); +#ifdef EXIT_WHEN_IDLE + sincelastreq = 0; +#endif + have = (ndisks != 0); + return (&have); +} + +uint_t * +rstatproc_havedisk_2_svc(argp, svcrq) +void *argp; +struct svc_req *svcrq; +{ + return (rstatproc_havedisk_3_svc(argp, svcrq)); +} + +void +updatestat(int ignored) +{ +extern int _rpcpmstart; /* Started by a port monitor ? */ +extern int _rpcsvcdirty; /* Still serving ? */ + +#ifdef DEBUG + fprintf(stderr, "entering updatestat\n"); +#endif +#ifdef EXIT_WHEN_IDLE + if (_rpcpmstart && sincelastreq >= CLOSEDOWN && !_rpcsvcdirty) { +#ifdef DEBUG + fprintf(stderr, "about to closedown\n"); +#endif + exit(0); + } + sincelastreq++; +#endif /* def EXIT_WHEN_IDLE */ + + (void) alarm(0); +#ifdef DEBUG + fprintf(stderr, "boottime: %d %d\n", stats_s3.boottime.tv_sec, + stats_s3.boottime.tv_usec); +#endif + while (system_stat_load() || diskinfo_load() || netinfo_load()) { + (void) kstat_chain_update(kc); + system_stat_init(); + init_disks(); + init_net(); + } + stats_s4.cp_time.cp_time_len = CPU_STATES; + if (stats_s4.cp_time.cp_time_val == NULL) + stats_s4.cp_time.cp_time_val = + malloc(stats_s4.cp_time.cp_time_len * sizeof (int)); + stats_s2.cp_time[RSTAT_CPU_USER] = + stats_s3.cp_time[RSTAT_CPU_USER] = + stats_s4.cp_time.cp_time_val[RSTAT_CPU_USER] = + CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_user"); + stats_s2.cp_time[RSTAT_CPU_NICE] = + stats_s3.cp_time[RSTAT_CPU_NICE] = + stats_s4.cp_time.cp_time_val[RSTAT_CPU_NICE] = + CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_wait"); + stats_s2.cp_time[RSTAT_CPU_SYS] = + stats_s3.cp_time[RSTAT_CPU_SYS] = + stats_s4.cp_time.cp_time_val[RSTAT_CPU_SYS] = + CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_kernel"); + stats_s2.cp_time[RSTAT_CPU_IDLE] = + stats_s3.cp_time[RSTAT_CPU_IDLE] = + stats_s4.cp_time.cp_time_val[RSTAT_CPU_IDLE] = + CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_idle"); + +#ifdef DEBUG + fprintf(stderr, "cpu: %d %d %d %d\n", + CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_user"), + CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_wait"), + CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_kernel"), + CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_idle")); + fprintf(stderr, "cp_time: %d %d %d %d\n", + stats_s3.cp_time[RSTAT_CPU_USER], + stats_s3.cp_time[RSTAT_CPU_NICE], + stats_s3.cp_time[RSTAT_CPU_SYS], + stats_s3.cp_time[RSTAT_CPU_IDLE]); +#endif + + /* current time */ + gettimeofday((struct timeval *)&stats_s3.curtime, NULL); + stats_s4.curtime = stats_s3.curtime; + + stats_s2.v_pgpgin = + stats_s3.v_pgpgin = + stats_s4.v_pgpgin = CPU_STAT(&cpu_stats_all.vm, "pgpgin"); + stats_s2.v_pgpgout = + stats_s3.v_pgpgout = + stats_s4.v_pgpgout = CPU_STAT(&cpu_stats_all.vm, "pgpgout"); + stats_s2.v_pswpin = + stats_s3.v_pswpin = + stats_s4.v_pswpin = CPU_STAT(&cpu_stats_all.vm, "pgswapin"); + stats_s2.v_pswpout = + stats_s3.v_pswpout = + stats_s4.v_pswpout = CPU_STAT(&cpu_stats_all.vm, "pgswapout"); + stats_s3.v_intr = CPU_STAT(&cpu_stats_all.sys, "intr"); + stats_s3.v_intr -= hz*(stats_s3.curtime.tv_sec - btm.tv_sec) + + hz*(stats_s3.curtime.tv_usec - btm.tv_usec)/1000000; + stats_s2.v_intr = + stats_s4.v_intr = stats_s3.v_intr; + /* swtch not in V1 */ + stats_s2.v_swtch = + stats_s3.v_swtch = + stats_s4.v_swtch = CPU_STAT(&cpu_stats_all.sys, "pswitch"); + +#ifdef DEBUG + fprintf(stderr, + "pgin: %d pgout: %d swpin: %d swpout: %d intr: %d swtch: %d\n", + stats_s3.v_pgpgin, + stats_s3.v_pgpgout, + stats_s3.v_pswpin, + stats_s3.v_pswpout, + stats_s3.v_intr, + stats_s3.v_swtch); +#endif + /* + * V2 and V3 of rstat are limited to RSTAT_DK_NDRIVE drives + */ + memcpy(stats_s3.dk_xfer, stats_s4.dk_xfer.dk_xfer_val, + RSTAT_DK_NDRIVE * sizeof (int)); + memcpy(stats_s2.dk_xfer, stats_s4.dk_xfer.dk_xfer_val, + RSTAT_DK_NDRIVE * sizeof (int)); +#ifdef DEBUG + fprintf(stderr, "dk_xfer: %d %d %d %d\n", + stats_s4.dk_xfer.dk_xfer_val[0], + stats_s4.dk_xfer.dk_xfer_val[1], + stats_s4.dk_xfer.dk_xfer_val[2], + stats_s4.dk_xfer.dk_xfer_val[3]); +#endif + + stats_s2.if_ipackets = + stats_s3.if_ipackets = stats_s4.if_ipackets; + /* no s2 opackets */ + stats_s3.if_opackets = stats_s4.if_opackets; + stats_s2.if_ierrors = + stats_s3.if_ierrors = stats_s4.if_ierrors; + stats_s2.if_oerrors = + stats_s3.if_oerrors = stats_s4.if_oerrors; + stats_s2.if_collisions = + stats_s3.if_collisions = stats_s4.if_collisions; + + stats_s2.avenrun[0] = + stats_s3.avenrun[0] = + stats_s4.avenrun[0] = avenrun_1min_knp->value.ul; + stats_s2.avenrun[1] = + stats_s3.avenrun[1] = + stats_s4.avenrun[1] = avenrun_5min_knp->value.ul; + stats_s2.avenrun[2] = + stats_s3.avenrun[2] = + stats_s4.avenrun[2] = avenrun_15min_knp->value.ul; +#ifdef DEBUG + fprintf(stderr, "avenrun: %d %d %d\n", stats_s3.avenrun[0], + stats_s3.avenrun[1], stats_s3.avenrun[2]); +#endif + signal(SIGALRM, updatestat); + alarm(1); +} + +/* --------------------------------- MIBGET -------------------------------- */ + +static mib_item_t * +mibget(int sd) +{ + int flags; + int j, getcode; + struct strbuf ctlbuf, databuf; + char buf[512]; + struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf; + struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf; + struct T_error_ack *tea = (struct T_error_ack *)buf; + struct opthdr *req; + mib_item_t *first_item = NULL; + mib_item_t *last_item = NULL; + mib_item_t *temp; + + tor->PRIM_type = T_SVR4_OPTMGMT_REQ; + tor->OPT_offset = sizeof (struct T_optmgmt_req); + tor->OPT_length = sizeof (struct opthdr); + tor->MGMT_flags = T_CURRENT; + req = (struct opthdr *)&tor[1]; + req->level = MIB2_IP; /* any MIB2_xxx value ok here */ + req->name = 0; + req->len = 0; + + ctlbuf.buf = buf; + ctlbuf.len = tor->OPT_length + tor->OPT_offset; + flags = 0; + if (putmsg(sd, &ctlbuf, NULL, flags) == -1) { + perror("mibget: putmsg(ctl) failed"); + goto error_exit; + } + /* + * each reply consists of a ctl part for one fixed structure + * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK, + * containing an opthdr structure. level/name identify the entry, + * len is the size of the data part of the message. + */ + req = (struct opthdr *)&toa[1]; + ctlbuf.maxlen = sizeof (buf); + /*CSTYLED*/ + for (j = 1; ; j++) { + flags = 0; + getcode = getmsg(sd, &ctlbuf, NULL, &flags); + if (getcode == -1) { +#ifdef DEBUG_MIB + perror("mibget getmsg(ctl) failed"); + fprintf(stderr, "# level name len\n"); + i = 0; + for (last_item = first_item; last_item; + last_item = last_item->next_item) + fprintf(stderr, "%d %4d %5d %d\n", ++i, + last_item->group, + last_item->mib_id, + last_item->length); +#endif /* DEBUG_MIB */ + goto error_exit; + } + if (getcode == 0 && + (ctlbuf.len >= sizeof (struct T_optmgmt_ack)) && + (toa->PRIM_type == T_OPTMGMT_ACK) && + (toa->MGMT_flags == T_SUCCESS) && + req->len == 0) { +#ifdef DEBUG_MIB + fprintf(stderr, + "mibget getmsg() %d returned EOD (level %d, name %d)\n", + j, req->level, req->name); +#endif /* DEBUG_MIB */ + return (first_item); /* this is EOD msg */ + } + + if (ctlbuf.len >= sizeof (struct T_error_ack) && + (tea->PRIM_type == T_ERROR_ACK)) { +#ifdef DEBUG_MIB + fprintf(stderr, + "mibget %d gives T_ERROR_ACK: TLI_error = 0x%x, UNIX_error = 0x%x\n", + j, getcode, tea->TLI_error, tea->UNIX_error); +#endif /* DEBUG_MIB */ + errno = (tea->TLI_error == TSYSERR) + ? tea->UNIX_error : EPROTO; + goto error_exit; + } + + if (getcode != MOREDATA || + (ctlbuf.len < sizeof (struct T_optmgmt_ack)) || + (toa->PRIM_type != T_OPTMGMT_ACK) || + (toa->MGMT_flags != T_SUCCESS)) { +#ifdef DEBUG_MIB + fprintf(stderr, + "mibget getmsg(ctl) %d returned %d, ctlbuf.len = %d, PRIM_type = %d\n", + j, getcode, ctlbuf.len, toa->PRIM_type); + if (toa->PRIM_type == T_OPTMGMT_ACK) + fprintf(stderr, + "T_OPTMGMT_ACK: MGMT_flags = 0x%x, req->len = %d\n", + toa->MGMT_flags, req->len); +#endif /* DEBUG_MIB */ + errno = ENOMSG; + goto error_exit; + } + + temp = malloc(sizeof (mib_item_t)); + if (!temp) { + perror("mibget malloc failed"); + goto error_exit; + } + if (last_item) + last_item->next_item = temp; + else + first_item = temp; + last_item = temp; + last_item->next_item = NULL; + last_item->group = req->level; + last_item->mib_id = req->name; + last_item->length = req->len; + last_item->valp = malloc(req->len); +#ifdef DEBUG_MIB + fprintf(stderr, + "msg %d: group = %4d mib_id = %5d length = %d\n", + j, last_item->group, last_item->mib_id, + last_item->length); +#endif /* DEBUG_MIB */ + databuf.maxlen = last_item->length; + databuf.buf = last_item->valp; + databuf.len = 0; + flags = 0; + getcode = getmsg(sd, NULL, &databuf, &flags); + if (getcode == -1) { + perror("mibget getmsg(data) failed"); + goto error_exit; + } else if (getcode != 0) { + fprintf(stderr, +"mibget getmsg(data) returned %d, databuf.maxlen = %d, databuf.len = %d\n", + getcode, databuf.maxlen, databuf.len); + goto error_exit; + } + } + +error_exit: + while (first_item) { + last_item = first_item; + first_item = first_item->next_item; + if (last_item->valp) { + free(last_item->valp); + } + free(last_item); + } + return (first_item); +} + +static int +mibopen(void) +{ + int sd; + + /* gives us ip w/ arp on top */ + sd = open("/dev/arp", O_RDWR); + if (sd == -1) { + perror("arp open"); + close(sd); + return (-1); + } + if (ioctl(sd, I_PUSH, "tcp") == -1) { + perror("tcp I_PUSH"); + close(sd); + return (-1); + } + if (ioctl(sd, I_PUSH, "udp") == -1) { + perror("udp I_PUSH"); + close(sd); + return (-1); + } + return (sd); +} + +static char * +octetstr(char *buf, Octet_t *op, int code) +{ + int i; + char *cp; + + cp = buf; + if (op) + for (i = 0; i < op->o_length; i++) + switch (code) { + case 'd': + sprintf(cp, "%d.", 0xff & op->o_bytes[i]); + cp = strchr(cp, '\0'); + break; + case 'a': + *cp++ = op->o_bytes[i]; + break; + case 'h': + default: + sprintf(cp, "%02x:", 0xff & op->o_bytes[i]); + cp += 3; + break; + } + if (code != 'a' && cp != buf) + cp--; + *cp = '\0'; + return (buf); +} + +static void +fail(int do_perror, char *message, ...) +{ + va_list args; + + va_start(args, message); + fprintf(stderr, "%s: ", cmdname); + vfprintf(stderr, message, args); + va_end(args); + if (do_perror) + fprintf(stderr, ": %s", strerror(errno)); + fprintf(stderr, "\n"); + exit(2); +} + +static void +safe_zalloc(void **ptr, int size, int free_first) +{ + if (free_first && *ptr != NULL) + free(*ptr); + if ((*ptr = malloc(size)) == NULL) + fail(1, "malloc failed"); + memset(*ptr, 0, size); +} + +kid_t +safe_kstat_read(kstat_ctl_t *kctl, kstat_t *ksp, void *data) +{ + kid_t kstat_chain_id = kstat_read(kctl, ksp, data); + + if (kstat_chain_id == -1) + fail(1, "kstat_read(%x, '%s') failed", kctl, ksp->ks_name); + return (kstat_chain_id); +} + +kstat_t * +safe_kstat_lookup(kstat_ctl_t *kctl, char *ks_module, int ks_instance, + char *ks_name) +{ + kstat_t *ksp = kstat_lookup(kctl, ks_module, ks_instance, ks_name); + + if (ksp == NULL) + fail(0, "kstat_lookup('%s', %d, '%s') failed", + ks_module == NULL ? "" : ks_module, + ks_instance, + ks_name == NULL ? "" : ks_name); + return (ksp); +} + +void * +safe_kstat_data_lookup(kstat_t *ksp, char *name) +{ + void *fp = kstat_data_lookup(ksp, name); + + if (fp == NULL) { + fail(0, "kstat_data_lookup('%s', '%s') failed", + ksp->ks_name, name); + } + return (fp); +} + +/* + * Get various KIDs for subsequent system_stat_load operations. + */ + +static void +system_stat_init(void) +{ + kstat_t *ksp; + int i, nvmks; + + /* + * Global statistics + */ + + system_misc_ksp = safe_kstat_lookup(kc, "unix", 0, "system_misc"); + + safe_kstat_read(kc, system_misc_ksp, NULL); + boot_time_knp = safe_kstat_data_lookup(system_misc_ksp, "boot_time"); + avenrun_1min_knp = safe_kstat_data_lookup(system_misc_ksp, + "avenrun_1min"); + avenrun_5min_knp = safe_kstat_data_lookup(system_misc_ksp, + "avenrun_5min"); + avenrun_15min_knp = safe_kstat_data_lookup(system_misc_ksp, + "avenrun_15min"); + + /* + * Per-CPU statistics + */ + + ncpus = 0; + for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) + if (strcmp(ksp->ks_module, "cpu") == 0 && + strcmp(ksp->ks_name, "sys") == 0) + ncpus++; + + safe_zalloc((void **)&cpu_stats_list, ncpus * sizeof (*cpu_stats_list), + 1); + + ncpus = 0; + for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) + if (strcmp(ksp->ks_module, "cpu") == 0 && + strcmp(ksp->ks_name, "sys") == 0 && + kstat_read(kc, ksp, NULL) != -1) { + kstat_copy(ksp, &cpu_stats_list[ncpus].sys, + 1); + if ((ksp = kstat_lookup(kc, "cpu", ksp->ks_instance, + "vm")) != NULL && kstat_read(kc, ksp, NULL) != -1) + kstat_copy(ksp, &cpu_stats_list[ncpus].vm, 1); + else + fail(0, "couldn't find per-CPU VM statistics"); + ncpus++; + } + + if (ncpus == 0) + fail(0, "couldn't find per-CPU statistics"); +} + +/* + * load statistics, summing across CPUs where needed + */ + +static int +system_stat_load(void) +{ + int i, j; + _cpu_stats_t cs; + ulong_t *np, *tp; + + /* + * Global statistics + */ + + safe_kstat_read(kc, system_misc_ksp, NULL); + + /* + * Per-CPU statistics. + */ + + for (i = 0; i < ncpus; i++) { + if (kstat_read(kc, &cpu_stats_list[i].sys, NULL) == -1 || + kstat_read(kc, &cpu_stats_list[i].vm, NULL) == -1) + return (1); + if (i == 0) { + kstat_copy(&cpu_stats_list[0].sys, &cpu_stats_all.sys, + 1); + kstat_copy(&cpu_stats_list[0].vm, &cpu_stats_all.vm, 1); + } else { + kstat_named_t *nkp; + kstat_named_t *tkp; + + /* + * Other CPUs' statistics are accumulated in + * cpu_stats_all, initialized at the first iteration of + * the loop. + */ + nkp = (kstat_named_t *)cpu_stats_all.sys.ks_data; + tkp = (kstat_named_t *)cpu_stats_list[i].sys.ks_data; + for (j = 0; j < cpu_stats_list[i].sys.ks_ndata; j++) + (nkp++)->value.ui64 += (tkp++)->value.ui64; + nkp = (kstat_named_t *)cpu_stats_all.vm.ks_data; + tkp = (kstat_named_t *)cpu_stats_list[i].vm.ks_data; + for (j = 0; j < cpu_stats_list[i].vm.ks_ndata; j++) + (nkp++)->value.ui64 += (tkp++)->value.ui64; + } + } + return (0); +} + +static int +kscmp(kstat_t *ks1, kstat_t *ks2) +{ + int cmp; + + cmp = strcmp(ks1->ks_module, ks2->ks_module); + if (cmp != 0) + return (cmp); + cmp = ks1->ks_instance - ks2->ks_instance; + if (cmp != 0) + return (cmp); + return (strcmp(ks1->ks_name, ks2->ks_name)); +} + +static void +init_disks(void) +{ + struct diskinfo *disk, *prevdisk, *comp; + kstat_t *ksp; + + ndisks = 0; + disk = &zerodisk; + + /* + * Patch the snip in the diskinfo list (see below) + */ + if (snip) + lastdisk->next = snip; + + for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { + + if (ksp->ks_type != KSTAT_TYPE_IO || + strcmp(ksp->ks_class, "disk") != 0) + continue; + prevdisk = disk; + if (disk->next) + disk = disk->next; + else { + safe_zalloc((void **)&disk->next, + sizeof (struct diskinfo), 0); + disk = disk->next; + disk->next = NULLDISK; + } + disk->ks = ksp; + memset((void *)&disk->kios, 0, sizeof (kstat_io_t)); + disk->kios.wlastupdate = disk->ks->ks_crtime; + disk->kios.rlastupdate = disk->ks->ks_crtime; + + /* + * Insertion sort on (ks_module, ks_instance, ks_name) + */ + comp = &zerodisk; + while (kscmp(disk->ks, comp->next->ks) > 0) + comp = comp->next; + if (prevdisk != comp) { + prevdisk->next = disk->next; + disk->next = comp->next; + comp->next = disk; + disk = prevdisk; + } + ndisks++; + } + /* + * Put a snip in the linked list of diskinfos. The idea: + * If there was a state change such that now there are fewer + * disks, we snip the list and retain the tail, rather than + * freeing it. At the next state change, we clip the tail back on. + * This prevents a lot of malloc/free activity, and it's simpler. + */ + lastdisk = disk; + snip = disk->next; + disk->next = NULLDISK; + + firstdisk = zerodisk.next; + + if (ndisks > stats_s4.dk_xfer.dk_xfer_len) { + stats_s4.dk_xfer.dk_xfer_len = ndisks; + safe_zalloc((void **)&stats_s4.dk_xfer.dk_xfer_val, + ndisks * sizeof (int), 1); + } +} + +static int +diskinfo_load(void) +{ + struct diskinfo *disk; + int i; + + for (disk = firstdisk, i = 0; disk; disk = disk->next, i++) { + if (kstat_read(kc, disk->ks, (void *)&disk->kios) == -1) + return (1); + stats_s4.dk_xfer.dk_xfer_val[i] = disk->kios.reads + + disk->kios.writes; + } + return (0); +} + +static void +init_net(void) +{ + static int sd; + mib_item_t *item; + mib2_ipAddrEntry_t *ap; + char namebuf[KSTAT_STRLEN]; + struct netinfo *net, *prevnet, *comp; + kstat_t *ksp; + + if (sd) { + close(sd); + } + while (netstat_item) { + item = netstat_item; + netstat_item = netstat_item->next_item; + if (item->valp) { + free(item->valp); + } + free(item); + } + sd = mibopen(); + if (sd == -1) { +#ifdef DEBUG + fprintf(stderr, "mibopen() failed\n"); +#endif + sd = 0; + } else { + if ((netstat_item = mibget(sd)) == NULL) { +#ifdef DEBUG + fprintf(stderr, "mibget() failed\n"); +#endif + close(sd); + sd = 0; + } + } +#ifdef DEBUG + fprintf(stderr, "mibget returned item: %x\n", netstat_item); +#endif + + nnets = 0; + net = &zeronet; + + if (netsnip) + lastnet->next = netsnip; + + for (item = netstat_item; item; item = item->next_item) { +#ifdef DEBUG_MIB + fprintf(stderr, "\n--- Item %x ---\n", item); + fprintf(stderr, + "Group = %d, mib_id = %d, length = %d, valp = 0x%x\n", + item->group, item->mib_id, item->length, + item->valp); +#endif + if (item->group != MIB2_IP || item->mib_id != MIB2_IP_20) + continue; + ap = (mib2_ipAddrEntry_t *)item->valp; + for (; (char *)ap < item->valp + item->length; ap++) { + + octetstr(namebuf, &ap->ipAdEntIfIndex, 'a'); +#ifdef DEBUG + fprintf(stderr, "%s ", namebuf); +#endif + if (strlen(namebuf) == 0) + continue; + /* + * We found a device of interest. + * Now, let's see if there's a kstat for it. + */ + if ((ksp = kstat_lookup(kc, NULL, -1, namebuf)) == NULL) + continue; + if (ksp->ks_type != KSTAT_TYPE_NAMED) + continue; + if (kstat_read(kc, ksp, NULL) == -1) + continue; + prevnet = net; + if (net->next) + net = net->next; + else { + safe_zalloc((void **)&net->next, + sizeof (struct netinfo), 0); + net = net->next; + net->next = NULLNET; + } + net->ks = ksp; + net->ipackets = kstat_data_lookup(net->ks, + "ipackets"); + net->opackets = kstat_data_lookup(net->ks, + "opackets"); + net->ierrors = kstat_data_lookup(net->ks, + "ierrors"); + net->oerrors = kstat_data_lookup(net->ks, + "oerrors"); + net->collisions = kstat_data_lookup(net->ks, + "collisions"); + /* + * Insertion sort on the name + */ + comp = &zeronet; + while (strcmp(net->ks->ks_name, + comp->next->ks->ks_name) > 0) + comp = comp->next; + if (prevnet != comp) { + prevnet->next = net->next; + net->next = comp->next; + comp->next = net; + net = prevnet; + } + nnets++; + } +#ifdef DEBUG + fprintf(stderr, "\n"); +#endif + } + /* + * Put a snip in the linked list of netinfos. The idea: + * If there was a state change such that now there are fewer + * nets, we snip the list and retain the tail, rather than + * freeing it. At the next state change, we clip the tail back on. + * This prevents a lot of malloc/free activity, and it's simpler. + */ + lastnet = net; + netsnip = net->next; + net->next = NULLNET; + + firstnet = zeronet.next; +} + +static int +netinfo_load(void) +{ + struct netinfo *net; + + if (netstat_item == NULL) { +#ifdef DEBUG + fprintf(stderr, "No net stats\n"); +#endif + return (0); + } + + stats_s4.if_ipackets = + stats_s4.if_opackets = + stats_s4.if_ierrors = + stats_s4.if_oerrors = + stats_s4.if_collisions = 0; + + for (net = firstnet; net; net = net->next) { + if (kstat_read(kc, net->ks, NULL) == -1) + return (1); + if (net->ipackets) + stats_s4.if_ipackets += net->ipackets->value.ul; + if (net->opackets) + stats_s4.if_opackets += net->opackets->value.ul; + if (net->ierrors) + stats_s4.if_ierrors += net->ierrors->value.ul; + if (net->oerrors) + stats_s4.if_oerrors += net->oerrors->value.ul; + if (net->collisions) + stats_s4.if_collisions += net->collisions->value.ul; + } +#ifdef DEBUG + fprintf(stderr, + "ipackets: %d opackets: %d ierrors: %d oerrors: %d colls: %d\n", + stats_s4.if_ipackets, + stats_s4.if_opackets, + stats_s4.if_ierrors, + stats_s4.if_oerrors, + stats_s4.if_collisions); +#endif + return (0); +} + +static void +kstat_copy(kstat_t *src, kstat_t *dst, int fr) +{ + if (fr) + free(dst->ks_data); + *dst = *src; + if (src->ks_data != NULL) { + safe_zalloc(&dst->ks_data, src->ks_data_size, 0); + (void) memcpy(dst->ks_data, src->ks_data, src->ks_data_size); + } else { + dst->ks_data = NULL; + dst->ks_data_size = 0; + } +} diff --git a/usr/src/cmd/rpcsvc/rstat_v2.x b/usr/src/cmd/rpcsvc/rstat_v2.x new file mode 100644 index 0000000000..ae94297700 --- /dev/null +++ b/usr/src/cmd/rpcsvc/rstat_v2.x @@ -0,0 +1,86 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +%/* +% * Version 2 rstat; for backwards compatibility only. +% */ + +%/* +% * Copyright (c) 1985, 1990, 1991 by Sun Microsystems, Inc. +% */ + +%/* from rstat_v2.x */ + +#ifdef RPC_HDR +% +%#pragma ident "%Z%%M% %I% %E% SMI" +% +#endif + +const RSTAT_V2_CPUSTATES = 4; +const RSTAT_V2_DK_NDRIVE = 4; + +/* + * the cpu stat values + */ + +const RSTAT_V2_CPU_USER = 0; +const RSTAT_V2_CPU_NICE = 1; +const RSTAT_V2_CPU_SYS = 2; +const RSTAT_V2_CPU_IDLE = 3; + +/* + * GMT since 0:00, January 1, 1970 + */ +struct rstat_v2_timeval { + int tv_sec; /* seconds */ + int tv_usec; /* and microseconds */ +}; + +struct statsswtch { /* RSTATVERS_SWTCH */ + int cp_time[RSTAT_V2_CPUSTATES]; + int dk_xfer[RSTAT_V2_DK_NDRIVE]; + int v_pgpgin; /* these are cumulative sum */ + int v_pgpgout; + int v_pswpin; + int v_pswpout; + int v_intr; + int if_ipackets; + int if_ierrors; + int if_oerrors; + int if_collisions; + int v_swtch; + int avenrun[3]; + rstat_v2_timeval boottime; +}; + +program RSTATPROG { + /* + * Does not have current time + */ + version RSTATVERS_SWTCH { + statsswtch + RSTATPROC_STATS(void) = 1; + + unsigned int + RSTATPROC_HAVEDISK(void) = 2; + } = 2; +} = 100001; diff --git a/usr/src/cmd/rpcsvc/rup.c b/usr/src/cmd/rpcsvc/rup.c new file mode 100644 index 0000000000..56934c9a5a --- /dev/null +++ b/usr/src/cmd/rpcsvc/rup.c @@ -0,0 +1,537 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <netdb.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <rpc/rpc.h> +#include <netdir.h> +#include <rpcsvc/rstat.h> +#include <rpc/pmap_clnt.h> + + +#define MACHINELEN 15 /* length of machine name printed out */ +#define MACHINELENMAX 128 /* maximum machine name length */ +#define AVENSIZE (3 * sizeof (long)) +#define SLOTS 256 + +int machinecmp(); +int loadcmp(); +int uptimecmp(); +int collectnames(); +int singlehost(); /* returns 1 if rup of given host fails */ +void printsinglehosts(); +void printnames(); + + +struct entry { + struct netconfig *nconf; + struct netbuf *addr; + char *machine; + struct timeval boottime; + time_t curtime; + long avenrun[3]; +}; + +int total_entries; +int curentry; +struct entry *entry; +int vers; /* which version did the broadcasting */ +int lflag; /* load: sort by load average */ +int tflag; /* time: sort by uptime average */ +int hflag; /* host: sort by machine name */ +int dflag; /* debug: list only first n machines */ +int debug; + +main(argc, argv) + char **argv; +{ + statsvar sv; + statstime st; + int single, nfailed; + enum clnt_stat bstat; + + /* + * set number of slots to be 256 to begin with, + * this is large enough for most subnets but not all + */ + + curentry = 0; + total_entries = SLOTS; + entry = malloc(sizeof (struct entry) * total_entries); + single = nfailed = 0; + while (argc > 1) { + if (argv[1][0] != '-') { + single++; + nfailed += singlehost(argv[1]); + } else { + switch (argv[1][1]) { + + case 'l': + lflag++; + break; + case 't': + tflag++; + break; + case 'h': + hflag++; + break; + case 'd': + dflag++; + if (argc < 3) + usage(); + debug = atoi(argv[2]); + argc--; + argv++; + break; + default: + usage(); + } + } + argv++; + argc--; + } + if (single > 0) { + if (hflag || tflag || lflag) + printsinglehosts(); + if (nfailed == single) { + free(entry); + exit(1); /* all hosts we tried failed */ + } else { + free(entry); + exit(0); + } + + } + if (hflag || tflag || lflag) { + printf("collecting responses... "); + fflush(stdout); + } + + sv.cp_time.cp_time_val = (int *)NULL; + sv.dk_xfer.dk_xfer_val = (int *)NULL; + + /* + * Null out pointers in the statsvar struct + * so that we don't follow a random pointer + * somewhere when we get our results back. + * Set lengths to zero so we don't allocate + * some random amount of space we don't need + * (in the case where the reply was program + * not registered). + */ + sv.cp_time.cp_time_len = 0; + sv.cp_time.cp_time_val = (int *)NULL; + sv.dk_xfer.dk_xfer_len = 0; + sv.dk_xfer.dk_xfer_val = (int *)NULL; + + vers = RSTATVERS_VAR; + bstat = rpc_broadcast(RSTATPROG, RSTATVERS_VAR, RSTATPROC_STATS, + xdr_void, NULL, xdr_statsvar, (caddr_t)&sv, + (resultproc_t)collectnames, (char *)0); +#ifdef TESTING + if (bstat != RPC_SUCCESS) + printf("rpc_broadcast for rstat version %d returned %s\n", + vers, clnt_sperrno(bstat)); + fprintf(stderr, "starting second round of broadcasting\n"); +#endif + vers = RSTATVERS_TIME; + bstat = rpc_broadcast(RSTATPROG, RSTATVERS_TIME, RSTATPROC_STATS, + xdr_void, NULL, xdr_statstime, (caddr_t)&st, + (resultproc_t)collectnames, (char *)0); +#ifdef TESTING + if (bstat != RPC_SUCCESS) + printf("rpc_broadcast for rstat version %d returned %s\n", + vers, clnt_sperrno(bstat)); +#endif + if (hflag || tflag || lflag) + printnames(); + + + + free(entry); + exit(0); + /* NOTREACHED */ +} + +int +singlehost(host) + char *host; +{ + static int debugcnt; + enum clnt_stat err; + statstime st; + statsvar sw_var; + bool_t is_var_vers = FALSE; + + + if (curentry >= total_entries) { + struct entry *tmp; + + total_entries += SLOTS; + tmp = realloc((struct entry *)entry, sizeof (struct entry) + * total_entries); + if (tmp == NULL) { + return (1); + } + entry = tmp; + } + + sw_var.cp_time.cp_time_val = (int *)NULL; + sw_var.dk_xfer.dk_xfer_val = (int *)NULL; + err = (enum clnt_stat)callrpc(host, RSTATPROG, RSTATVERS_VAR, + RSTATPROC_STATS, xdr_void, 0, xdr_statsvar, &sw_var); + if (err == RPC_SUCCESS) { + is_var_vers = TRUE; + } else if (err == RPC_PROGVERSMISMATCH) { + err = (enum clnt_stat)callrpc(host, RSTATPROG, RSTATVERS_TIME, + RSTATPROC_STATS, xdr_void, 0, xdr_statstime, &st); + if (err != RPC_SUCCESS) + goto error; + } else + goto error; + + debugcnt++; + if (!hflag && !lflag && !tflag) { + printf("%*.*s ", MACHINELEN, MACHINELEN, host); + if (is_var_vers == TRUE) + putline(sw_var.curtime.tv_sec, sw_var.boottime, + sw_var.avenrun); + else + putline(st.curtime.tv_sec, st.boottime, st.avenrun); + return (0); /* success */ + } else { + entry[curentry].machine = host; + if (is_var_vers == FALSE) { /* RSTATVERS_TIME */ + entry[curentry].boottime.tv_sec = st.boottime.tv_sec; + entry[curentry].boottime.tv_usec = + st.boottime.tv_usec; + entry[curentry].curtime = st.curtime.tv_sec; + memcpy(entry[curentry].avenrun, st.avenrun, AVENSIZE); + } else { /* RSTATVERS_VAR */ + entry[curentry].boottime.tv_sec = + sw_var.boottime.tv_sec; + entry[curentry].boottime.tv_usec = + sw_var.boottime.tv_usec; + entry[curentry].curtime = sw_var.curtime.tv_sec; + memcpy(entry[curentry].avenrun, sw_var.avenrun, + AVENSIZE); + } + } + curentry++; + if (dflag && debugcnt >= debug) + return (1); + return (0); + +error: + fprintf(stderr, "%*.*s: ", MACHINELEN, MACHINELEN, host); + clnt_perrno(err); + /* + * clnt_perrno now prints a newline + */ + /* fprintf(stderr, "\n"); */ + return (1); /* a failure */ +} + +putline(now, boottime, avenrun) + time_t now; + struct timeval boottime; + long avenrun[]; +{ + int uptime, days, hrs, mins, i; + + uptime = now - boottime.tv_sec; + uptime += 30; + if (uptime < 0) /* unsynchronized clocks */ + uptime = 0; + days = uptime / (60*60*24); + uptime %= (60*60*24); + hrs = uptime / (60*60); + uptime %= (60*60); + mins = uptime / 60; + + printf(" up"); + if (days > 0) + printf(" %2d day%s", days, days > 1 ? "s," : ", "); + else + printf(" "); + if (hrs > 0) + printf(" %2d:%02d, ", hrs, mins); + else + printf(" %2d min%s", mins, mins > 1 ? "s," : ", "); + + /* + * Print 1, 5, and 15 minute load averages. + * (Found by looking in kernel for avenrun). + */ + printf(" load average:"); + for (i = 0; i < (AVENSIZE / sizeof (avenrun[0])); i++) { + if (i > 0) + printf(","); + printf(" %.2f", (double)avenrun[i]/FSCALE); + } + printf("\n"); +} + +collectnames(resultsp, taddr, nconf) + char *resultsp; + struct t_bind *taddr; + struct netconfig *nconf; +{ + static int debugcnt; + register struct entry *entryp, *lim; + statstime *st; + statsvar *sv; + struct nd_hostservlist *hs; + extern struct netbuf *netbufdup(); + extern struct netconfig *netconfigdup(); + extern int netbufeq(); + + /* + * need to realloc more space if we have more than 256 machines + * that responded to the broadcast + */ + + if (curentry >= total_entries) { + struct entry *tmp; + + total_entries += SLOTS; + tmp = realloc((struct entry *)entry, sizeof (struct entry) + * total_entries); + if (tmp == NULL) { + return (1); + } + entry = tmp; + } + /* + * weed out duplicates + */ + lim = entry + curentry; + for (entryp = entry; entryp < lim; entryp++) + if (netbufeq(&taddr->addr, entryp->addr)) + return (0); + + if (vers == RSTATVERS_TIME) { + st = (statstime *)resultsp; + } else if (vers == RSTATVERS_VAR) { + sv = (statsvar *)resultsp; + } else { + return (0); /* we don't handle this version */ + } + debugcnt++; + entry[curentry].nconf = netconfigdup(nconf); + entry[curentry].addr = netbufdup(&taddr->addr); + + /* + * if raw, print this entry out immediately + * otherwise store for later sorting + */ + if (!hflag && !lflag && !tflag) { + if (netdir_getbyaddr(nconf, &hs, &taddr->addr) == ND_OK) + printf("%*.*s ", MACHINELEN, MACHINELEN, + hs->h_hostservs->h_host); + else { + char *uaddr = taddr2uaddr(nconf, &taddr->addr); + + if (uaddr) { + printf(" %*.*s", MACHINELEN, MACHINELEN, + uaddr); + (void) free(uaddr); + } else + printf(" %*.*s", MACHINELEN, MACHINELEN, + "unknown"); + } + if (vers == RSTATVERS_TIME) { + putline(st->curtime.tv_sec, st->boottime, st->avenrun); + } else if (vers == RSTATVERS_VAR) { + putline(sv->curtime.tv_sec, sv->boottime, sv->avenrun); + } + } else { + if (vers == RSTATVERS_TIME) { + entry[curentry].boottime.tv_sec = st->boottime.tv_sec; + entry[curentry].boottime.tv_usec = + st->boottime.tv_usec; + entry[curentry].curtime = st->curtime.tv_sec; + memcpy(entry[curentry].avenrun, st->avenrun, AVENSIZE); + } else if (vers == RSTATVERS_VAR) { + entry[curentry].boottime.tv_sec = sv->boottime.tv_sec; + entry[curentry].boottime.tv_usec = + sv->boottime.tv_usec; + entry[curentry].curtime = sv->curtime.tv_sec; + memcpy(entry[curentry].avenrun, sv->avenrun, AVENSIZE); + } + } + curentry++; + if (dflag && debugcnt >= debug) + return (1); + return (0); +} + +void +printsinglehosts() +{ + register int i; + register struct entry *ep; + + + if (hflag) + qsort(entry, curentry, sizeof (struct entry), machinecmp); + else if (lflag) + qsort(entry, curentry, sizeof (struct entry), loadcmp); + else + qsort(entry, curentry, sizeof (struct entry), uptimecmp); + for (i = 0; i < curentry; i++) { + ep = &entry[i]; + printf("%*.*s ", MACHINELEN, MACHINELEN, ep->machine); + putline(ep->curtime, ep->boottime, ep->avenrun); + + } +} + +void +printnames() +{ + char buf[MACHINELENMAX+1]; + struct nd_hostservlist *hs; + register int i; + register struct entry *ep; + + + for (i = 0; i < curentry; i++) { + ep = &entry[i]; + if (netdir_getbyaddr(ep->nconf, &hs, ep->addr) == ND_OK) + sprintf(buf, "%s", hs->h_hostservs->h_host); + else { + char *uaddr = taddr2uaddr(ep->nconf, ep->addr); + + if (uaddr) { + sprintf(buf, "%s", uaddr); + (void) free(uaddr); + } else + sprintf(buf, "%s", "unknown"); + } + if (ep->machine = (char *)malloc(MACHINELENMAX + 1)) + strcpy(ep->machine, buf); + } + printf("\n"); + printsinglehosts(); +} + +machinecmp(a, b) + struct entry *a, *b; +{ + return (strcmp(a->machine, b->machine)); +} + +uptimecmp(a, b) + struct entry *a, *b; +{ + if (a->boottime.tv_sec != b->boottime.tv_sec) + return (a->boottime.tv_sec - b->boottime.tv_sec); + else + return (a->boottime.tv_usec - b->boottime.tv_usec); +} + +loadcmp(a, b) + struct entry *a, *b; +{ + register int i; + + for (i = 0; i < AVENSIZE / sizeof (a->avenrun[0]); i++) + if (a->avenrun[i] != b->avenrun[i]) + return (a->avenrun[i] - b->avenrun[i]); + + return (0); +} + +struct netbuf * +netbufdup(ap) + register struct netbuf *ap; +{ + register struct netbuf *np; + + np = (struct netbuf *) malloc(sizeof (struct netbuf) + ap->len); + if (np) { + np->maxlen = np->len = ap->len; + np->buf = ((char *)np) + sizeof (struct netbuf); + (void) memcpy(np->buf, ap->buf, ap->len); + } + return (np); +} + +struct netconfig * +netconfigdup(onp) + register struct netconfig *onp; +{ + register int nlookupdirs; + register struct netconfig *nnp; + extern char *strdup(); + + nnp = (struct netconfig *)malloc(sizeof (struct netconfig)); + if (nnp) { + nnp->nc_netid = strdup(onp->nc_netid); + nnp->nc_semantics = onp->nc_semantics; + nnp->nc_flag = onp->nc_flag; + nnp->nc_protofmly = strdup(onp->nc_protofmly); + nnp->nc_proto = strdup(onp->nc_proto); + nnp->nc_device = strdup(onp->nc_device); + nnp->nc_nlookups = onp->nc_nlookups; + if (onp->nc_nlookups == 0) + nnp->nc_lookups = (char **)0; + else { + register int i; + + nnp->nc_lookups = (char **)malloc(onp->nc_nlookups * + sizeof (char *)); + if (nnp->nc_lookups) + for (i = 0; i < onp->nc_nlookups; i++) + nnp->nc_lookups[i] = + strdup(onp->nc_lookups[i]); + } + } + + return (nnp); +} + +netbufeq(ap, bp) + register struct netbuf *ap, *bp; +{ + return (ap->len == bp->len && !memcmp(ap->buf, bp->buf, ap->len)); +} + +usage() +{ + fprintf(stderr, "Usage: rup [-h] [-l] [-t] [host ...]\n"); + free(entry); + exit(1); +} diff --git a/usr/src/cmd/rpcsvc/rusers.c b/usr/src/cmd/rpcsvc/rusers.c new file mode 100644 index 0000000000..9ab0f7ae0f --- /dev/null +++ b/usr/src/cmd/rpcsvc/rusers.c @@ -0,0 +1,652 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ +/* + * University Copyright- Copyright (c) 1982, 1986, 1988 + * The Regents of the University of California + * All Rights Reserved + * + * University Acknowledgment- Portions of this document are derived from + * software developed by the University of California, Berkeley, and its + * contributors. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <netconfig.h> +#include <netdir.h> +#include <rpc/rpc.h> +#include <rpcsvc/rusers.h> +#include <string.h> +#include <limits.h> + +#define NMAX 12 /* These are used as field width specifiers */ +#define LMAX 8 /* when printing. */ +#define HMAX 16 /* "Logged in" host name. */ + +#define MACHINELEN 16 /* length of machine name printed out */ +#define NUMENTRIES 256 +#define min(a, b) ((a) < (b) ? (a) : (b)) + +struct entry { + int cnt; + int idle; /* set to INT_MAX if not present */ + char *machine; + utmp_array users; +}; + +static int curentry; +static int total_entries; +static struct entry *entry; +static int hflag; /* host: sort by machine name */ +static int iflag; /* idle: sort by idle time */ +static int uflag; /* users: sort by number of users */ +static int lflag; /* print out long form */ +static int aflag; /* all: list all machines */ +static int dflag; /* debug: list only first n machines */ +static int sorted; +static int debug; +static int debugcnt; +static char *nettype; + +static int hcompare(const struct entry *, const struct entry *); +static int icompare(const struct entry *, const struct entry *); +static int ucompare(const struct entry *, const struct entry *); +static int print_info(struct utmpidlearr *, const char *); +static int print_info_3(utmp_array *, const char *); +static int collectnames(void *, struct netbuf *, struct netconfig *); +static int collectnames_3(void *, struct netbuf *, struct netconfig *); +static void singlehost(char *); +static void printnames(void); +static void putline_2(char *, struct utmpidle *); +static void putline_3(char *, rusers_utmp *); +static void prttime(uint_t, char *); +static void usage(void); + +/* + * rusers [-ahilu] [host...] + */ +int +main(int argc, char *argv[]) +{ + int c; + uint_t errflag = 0; + uint_t single = 0; + struct utmpidlearr utmpidlearr; + utmp_array utmp_array_res; + + curentry = 0; + total_entries = NUMENTRIES; + entry = malloc(sizeof (struct entry) * total_entries); + + while ((c = getopt(argc, argv, ":ad:hilun:")) != -1) { + switch (c) { + case 'a': + aflag++; + break; + case 'd': + dflag++; + debug = atoi(optarg); + (void) printf("Will collect %d responses.\n", debug); + break; + case 'h': + hflag++; + sorted++; + if (iflag || uflag) + errflag++; + break; + case 'i': + iflag++; + sorted++; + if (hflag || uflag) + errflag++; + break; + case 'u': + uflag++; + sorted++; + if (hflag || iflag) + errflag++; + break; + case 'l': + lflag++; + break; + case ':': /* required operand missing */ + errflag++; + break; + case 'n': + nettype = optarg; + break; + default: + case '?': /* Unrecognized option */ + errflag++; + break; + } + } + if (errflag) + usage(); + + for (; optind < argc; optind++) { + single++; + singlehost(argv[optind]); + } + if (single) { + if (sorted) + printnames(); + free(entry); + exit(0); + } + + if (sorted) { + (void) printf("Collecting responses...\n"); + (void) fflush(stdout); + } + utmp_array_res.utmp_array_val = NULL; + utmp_array_res.utmp_array_len = 0; + (void) printf("Sending broadcast for rusersd protocol version 3...\n"); + (void) rpc_broadcast(RUSERSPROG, RUSERSVERS_3, + RUSERSPROC_NAMES, (xdrproc_t)xdr_void, NULL, + (xdrproc_t)xdr_utmp_array, (char *)&utmp_array_res, + (resultproc_t)collectnames_3, nettype); + utmpidlearr.uia_arr = NULL; + (void) printf("Sending broadcast for rusersd protocol version 2...\n"); + (void) rpc_broadcast(RUSERSPROG, RUSERSVERS_IDLE, + RUSERSPROC_NAMES, (xdrproc_t)xdr_void, NULL, + (xdrproc_t)xdr_utmpidlearr, (char *)&utmpidlearr, + (resultproc_t)collectnames, nettype); + + if (sorted) + printnames(); + + free(entry); + return (0); +} + +static void +singlehost(char *name) +{ + enum clnt_stat err; + struct utmpidlearr utmpidlearr; + utmp_array utmp_array_res; + + if (curentry >= total_entries) { + struct entry *tmp; + + total_entries += NUMENTRIES; + if ((tmp = realloc(entry, sizeof (struct entry) + * total_entries)) == NULL) + return; + entry = tmp; + } + utmp_array_res.utmp_array_val = NULL; + utmp_array_res.utmp_array_len = 0; + err = rpc_call(name, RUSERSPROG, RUSERSVERS_3, + RUSERSPROC_NAMES, (xdrproc_t)xdr_void, 0, + (xdrproc_t)xdr_utmp_array, (char *)&utmp_array_res, + nettype); + if (err == RPC_SUCCESS) { + (void) print_info_3(&utmp_array_res, name); + return; + } + if (err == RPC_PROGVERSMISMATCH) { + utmpidlearr.uia_arr = NULL; + err = rpc_call(name, RUSERSPROG, RUSERSVERS_IDLE, + RUSERSPROC_NAMES, (xdrproc_t)xdr_void, 0, + (xdrproc_t)xdr_utmpidlearr, + (char *)&utmpidlearr, nettype); + } + if (err != RPC_SUCCESS) { + (void) fprintf(stderr, "%s: ", name); + clnt_perrno(err); + return; + } + (void) print_info(&utmpidlearr, name); +} + +/* + * Collect responses from RUSERSVERS_IDLE broadcast, convert to + * RUSERSVERS_3 format, and store in entry database. + */ +static int +collectnames(void *resultsp, struct netbuf *raddrp, struct netconfig *nconf) +{ + struct utmpidlearr utmpidlearr; + struct entry *entryp, *lim; + struct nd_hostservlist *hs; + char host[MACHINELEN + 1]; + + utmpidlearr = *(struct utmpidlearr *)resultsp; + if (utmpidlearr.uia_cnt < 1 && !aflag) + return (0); + + if (netdir_getbyaddr(nconf, &hs, raddrp)) { +#ifdef DEBUG + netdir_perror("netdir_getbyaddr"); +#endif + /* netdir routine couldn't resolve addr;just print out uaddr */ + (void) sprintf(host, "%.*s", MACHINELEN, + taddr2uaddr(nconf, raddrp)); + } else { + (void) sprintf(host, "%.*s", MACHINELEN, + hs->h_hostservs->h_host); + netdir_free((char *)hs, ND_HOSTSERVLIST); + } + /* + * need to realloc more space if we have more than 256 machines + * that respond to broadcast + */ + if (curentry >= total_entries) { + struct entry *tmp; + + total_entries += NUMENTRIES; + if ((tmp = realloc(entry, sizeof (struct entry) + * total_entries)) == NULL) + return (1); + entry = tmp; + } + + + /* + * weed out duplicates + */ + lim = entry + curentry; + for (entryp = entry; entryp < lim; entryp++) { + if (strcmp(entryp->machine, host) == 0) + return (0); + } + return (print_info((struct utmpidlearr *)resultsp, host)); +} + +static int +print_info(struct utmpidlearr *utmpidlearrp, const char *name) +{ + utmp_array *iconvert; + int i, cnt, minidle; + char host[MACHINELEN + 1]; + char username[NMAX + 1]; + + cnt = utmpidlearrp->uia_cnt; + (void) sprintf(host, "%.*s", MACHINELEN, name); + + /* + * if raw, print this entry out immediately + * otherwise store for later sorting + */ + if (!sorted) { + if (lflag && (cnt > 0)) + for (i = 0; i < cnt; i++) + putline_2(host, utmpidlearrp->uia_arr[i]); + else { + (void) printf("%-*.*s", MACHINELEN, MACHINELEN, host); + for (i = 0; i < cnt; i++) { + (void) strlcpy(username, + utmpidlearrp->uia_arr[i]->ui_utmp.ut_name, + NMAX + 1); + (void) printf(" %.*s", NMAX, username); + } + (void) printf("\n"); + } + /* store just the name */ + entry[curentry].machine = malloc(MACHINELEN + 1); + if (entry[curentry].machine == NULL) { + (void) fprintf(stderr, "Ran out of memory - exiting\n"); + exit(1); + } + (void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1); + entry[curentry++].cnt = 0; + if (dflag && (++debugcnt >= debug)) + return (1); + return (0); + } + entry[curentry].machine = malloc(MACHINELEN + 1); + if (entry[curentry].machine == NULL) { + (void) fprintf(stderr, "Ran out of memory - exiting\n"); + exit(1); + } + (void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1); + entry[curentry].cnt = cnt; + iconvert = &entry[curentry].users; + iconvert->utmp_array_len = cnt; + iconvert->utmp_array_val = malloc(cnt * sizeof (rusers_utmp)); + minidle = INT_MAX; + for (i = 0; i < cnt; i++) { + iconvert->utmp_array_val[i].ut_user = + strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_name); + iconvert->utmp_array_val[i].ut_line = + strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_line); + iconvert->utmp_array_val[i].ut_host = + strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_host); + iconvert->utmp_array_val[i].ut_time = + utmpidlearrp->uia_arr[i]->ui_utmp.ut_time; + iconvert->utmp_array_val[i].ut_idle = + utmpidlearrp->uia_arr[i]->ui_idle; + minidle = min(minidle, utmpidlearrp->uia_arr[i]->ui_idle); + } + entry[curentry].idle = minidle; + curentry++; + if (dflag && (++debugcnt >= debug)) + return (1); + return (0); +} + + +/* + * Collect responses from RUSERSVERS_3 broadcast. + */ +static int +collectnames_3(void *resultsp, struct netbuf *raddrp, struct netconfig *nconf) +{ + utmp_array *uap; + struct entry *entryp, *lim; + struct nd_hostservlist *hs; + char host[MACHINELEN + 1]; + + uap = (utmp_array *)resultsp; + if (uap->utmp_array_len < 1 && !aflag) + return (0); + + if (netdir_getbyaddr(nconf, &hs, raddrp)) { +#ifdef DEBUG + netdir_perror("netdir_getbyaddr"); +#endif + /* netdir routine couldn't resolve addr;just print out uaddr */ + (void) sprintf(host, "%.*s", MACHINELEN, + taddr2uaddr(nconf, raddrp)); + } else { + (void) sprintf(host, "%.*s", MACHINELEN, + hs->h_hostservs->h_host); + netdir_free((char *)hs, ND_HOSTSERVLIST); + } + + /* + * need to realloc more space if we have more than 256 machines + * that respond to broadcast + */ + if (curentry >= total_entries) { + struct entry *tmp; + + total_entries += NUMENTRIES; + if ((tmp = realloc(entry, sizeof (struct entry) + * total_entries)) == NULL) + return (1); + entry = tmp; + } + + + /* + * weed out duplicates + */ + lim = entry + curentry; + for (entryp = entry; entryp < lim; entryp++) { + if (strcmp(entryp->machine, host) == 0) + return (0); + } + return (print_info_3(uap, host)); +} + +static int +print_info_3(utmp_array *uap, const char *name) +{ + int i, cnt, minidle; + char host[MACHINELEN + 1]; + + cnt = uap->utmp_array_len; + + (void) sprintf(host, "%.*s", MACHINELEN, name); + + /* + * if raw, print this entry out immediately + * otherwise store for later sorting + */ + if (!sorted) { + if (lflag && (cnt > 0)) + for (i = 0; i < cnt; i++) + putline_3(host, &uap->utmp_array_val[i]); + else { + (void) printf("%-*.*s", MACHINELEN, MACHINELEN, host); + for (i = 0; i < cnt; i++) + (void) printf(" %.*s", NMAX, + uap->utmp_array_val[i].ut_user); + (void) printf("\n"); + } + /* store just the name */ + entry[curentry].machine = malloc(MACHINELEN + 1); + if (entry[curentry].machine == NULL) { + (void) fprintf(stderr, "Ran out of memory - exiting\n"); + exit(1); + } + (void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1); + entry[curentry++].cnt = 0; + if (dflag && (++debugcnt >= debug)) + return (1); + return (0); + } + + entry[curentry].machine = malloc(MACHINELEN + 1); + if (entry[curentry].machine == NULL) { + (void) fprintf(stderr, "Ran out of memory - exiting\n"); + exit(1); + } + (void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1); + entry[curentry].cnt = cnt; + entry[curentry].users.utmp_array_len = cnt; + entry[curentry].users.utmp_array_val = malloc(cnt * + sizeof (rusers_utmp)); + minidle = INT_MAX; + for (i = 0; i < cnt; i++) { + entry[curentry].users.utmp_array_val[i].ut_user = + strdup(uap->utmp_array_val[i].ut_user); + entry[curentry].users.utmp_array_val[i].ut_line = + strdup(uap->utmp_array_val[i].ut_line); + entry[curentry].users.utmp_array_val[i].ut_host = + strdup(uap->utmp_array_val[i].ut_host); + entry[curentry].users.utmp_array_val[i].ut_time = + uap->utmp_array_val[i].ut_time; + entry[curentry].users.utmp_array_val[i].ut_idle = + uap->utmp_array_val[i].ut_idle; + minidle = min(minidle, uap->utmp_array_val[i].ut_idle); + } + entry[curentry].idle = minidle; + curentry++; + if (dflag && (++debugcnt >= debug)) + return (1); + return (0); +} + +static void +printnames(void) +{ + int i, j; + int (*compare)(const void *, const void *); + + /* the name of the machine should already be in the structure */ + if (iflag) + compare = (int (*)(const void *, const void *))icompare; + else if (hflag) + compare = (int (*)(const void *, const void *))hcompare; + else + compare = (int (*)(const void *, const void *))ucompare; + qsort(entry, curentry, sizeof (struct entry), compare); + for (i = 0; i < curentry; i++) { + if (!lflag || (entry[i].cnt < 1)) { + (void) printf("%-*.*s", MACHINELEN, + MACHINELEN, entry[i].machine); + for (j = 0; j < entry[i].cnt; j++) + (void) printf(" %.*s", NMAX, + entry[i].users.utmp_array_val[j].ut_user); + (void) printf("\n"); + } else { + for (j = 0; j < entry[i].cnt; j++) + putline_3(entry[i].machine, + &entry[i].users.utmp_array_val[j]); + } + } +} + +static int +hcompare(const struct entry *a, const struct entry *b) +{ + return (strcmp(a->machine, b->machine)); +} + +static int +ucompare(const struct entry *a, const struct entry *b) +{ + return (b->cnt - a->cnt); +} + +static int +icompare(const struct entry *a, const struct entry *b) +{ + return (a->idle - b->idle); +} + +static void +putline_2(char *host, struct utmpidle *uip) +{ + char *cbuf; + struct ru_utmp *up; + char buf[100]; + + up = &uip->ui_utmp; +#define NAMEMAX ((sizeof (up->ut_name) < NMAX) ? NMAX : sizeof (up->ut_name)) +#define NAMEMIN ((sizeof (up->ut_name) > NMAX) ? NMAX : sizeof (up->ut_name)) + /* Try and align this up nicely */ +#define LINEMAX sizeof (up->ut_line) +#define HOSTMAX sizeof (up->ut_host) + /* + * We copy the strings into a buffer because they aren't strictly + * speaking strings but byte arrays (and they may not have a + * terminating NULL. + */ + + (void) strncpy(buf, up->ut_name, NAMEMAX); + buf[NAMEMIN] = '\0'; + (void) printf("%-*.*s ", NAMEMAX, NAMEMAX, buf); + + (void) strcpy(buf, host); + (void) strcat(buf, ":"); + (void) strncat(buf, up->ut_line, LINEMAX); + buf[MACHINELEN+LINEMAX] = '\0'; + (void) printf("%-*.*s", MACHINELEN+LINEMAX, MACHINELEN+LINEMAX, buf); + + cbuf = (char *)ctime(&up->ut_time); + (void) printf(" %.12s ", cbuf+4); + if (uip->ui_idle == INT_MAX) + (void) printf(" ??"); + else + prttime(uip->ui_idle, ""); + if (up->ut_host[0]) { + (void) strncpy(buf, up->ut_host, HOSTMAX); + buf[HOSTMAX] = '\0'; + (void) printf(" (%.*s)", HOSTMAX, buf); + } + (void) putchar('\n'); +} + +static void +putline_3(char *host, rusers_utmp *rup) +{ + char *cbuf; + char buf[100]; + + (void) printf("%-*.*s ", NMAX, NMAX, rup->ut_user); + (void) strcpy(buf, host); + (void) strcat(buf, ":"); + (void) strncat(buf, rup->ut_line, LMAX); + (void) printf("%-*.*s", MACHINELEN+LMAX, MACHINELEN+LMAX, buf); + + cbuf = (char *)ctime((time_t *)&rup->ut_time); + (void) printf(" %.12s ", cbuf+4); + if (rup->ut_idle == INT_MAX) + (void) printf(" ??"); + else + prttime(rup->ut_idle, ""); + if (rup->ut_host[0]) + (void) printf(" (%.*s)", HMAX, rup->ut_host); + (void) putchar('\n'); +} + +/* + * prttime prints a time in hours and minutes. + * The character string tail is printed at the end, obvious + * strings to pass are "", " ", or "am". + */ +static void +prttime(uint_t tim, char *tail) +{ + int didhrs = 0; + + if (tim >= 60) { + (void) printf("%3d:", tim/60); + didhrs++; + } else { + (void) printf(" "); + } + tim %= 60; + if (tim > 0 || didhrs) { + (void) printf(didhrs && tim < 10 ? "%02d" : "%2d", tim); + } else { + (void) printf(" "); + } + (void) printf("%s", tail); +} + +#ifdef DEBUG +/* + * for debugging + */ +int +printit(int i) +{ + int j, v; + + (void) printf("%12.12s: ", entry[i].machine); + if (entry[i].cnt) { + putline_3(entry[i].machine, &entry[i].users.utmp_array_val[0]); + for (j = 1; j < entry[i].cnt; j++) { + (void) printf("\t"); + putline_3(entry[i].machine, + &entry[i].users.utmp_array_val[j]); + } + } else + (void) printf("\n"); +} +#endif + +static void +usage(void) +{ + (void) fprintf(stderr, "Usage: rusers [-ahilu] [host ...]\n"); + free(entry); + exit(1); +} diff --git a/usr/src/cmd/rpcsvc/rusers.xml b/usr/src/cmd/rpcsvc/rusers.xml new file mode 100644 index 0000000000..c960c83e64 --- /dev/null +++ b/usr/src/cmd/rpcsvc/rusers.xml @@ -0,0 +1,116 @@ +<?xml version='1.0'?> +<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'> + +<!-- + Copyright 2005 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License, Version 1.0 only + (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 + + ident "%Z%%M% %I% %E% SMI" + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. + + Service manifest for rpc.rusersd +--> + +<service_bundle type='manifest' name='SUNWrcmdr:rusersd'> + +<service + name='network/rpc/rusers' + type='service' + version='1'> + + <create_default_instance enabled='false' /> + + <restarter> + <service_fmri value='svc:/network/inetd:default' /> + </restarter> + + <dependency name='rpcbind' + grouping='require_all' + restart_on='restart' + type='service'> + <service_fmri value='svc:/network/rpc/bind' /> + </dependency> + + <exec_method + type='method' + name='inetd_start' + exec='/usr/lib/netsvc/rusers/rpc.rusersd' + timeout_seconds='0'> + <method_context> + <method_credential user='root' group='root' /> + </method_context> + </exec_method> + + <exec_method + type='method' + name='inetd_offline' + exec=':kill_process' + timeout_seconds='0'> + </exec_method> + + <exec_method + type='method' + name='inetd_disable' + exec=':kill' + timeout_seconds='0'> + </exec_method> + + <property_group name='inetd' type='framework'> + <stability value='Evolving' /> + <propval name='name' type='astring' value='rusersd' /> + <propval name='endpoint_type' type='astring' value='tli' /> + <propval name='wait' type='boolean' value='true' /> + <propval name='isrpc' type='boolean' value='true' /> + <propval name='rpc_low_version' type='integer' value='2' /> + <propval name='rpc_high_version' type='integer' value='3' /> + <property name='proto' type='astring' override='true'> + <astring_list> + <value_node value='datagram_v'/> + <value_node value='circuit_v' /> + </astring_list> + </property> + </property_group> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + network user name service + </loctext> + </common_name> + <documentation> + <manpage title='rpc.rusersd' section='1M' + manpath='/usr/share/man' /> + <manpage title='rusersd' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> + +</service> + +</service_bundle> diff --git a/usr/src/cmd/rpcsvc/rwall.c b/usr/src/cmd/rpcsvc/rwall.c new file mode 100644 index 0000000000..6c4a42ef70 --- /dev/null +++ b/usr/src/cmd/rpcsvc/rwall.c @@ -0,0 +1,265 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ +/* + * University Copyright- Copyright (c) 1982, 1986, 1988 + * The Regents of the University of California + * All Rights Reserved + * + * University Acknowledgment- Portions of this document are derived from + * software developed by the University of California, Berkeley, and its + * contributors. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * rwall.c + * The client rwall program + */ + +#include <stdio.h> +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <thread.h> +#include <string.h> +#include <rpc/rpc.h> +#include <signal.h> +#include <pwd.h> +#include <rpcsvc/rwall.h> +#include <netconfig.h> +#include <netdb.h> +#include <sys/time.h> +#include <sys/resource.h> + +static void init_who(void); +static void doall(void); +static void doit(char *); +static void *do_one(void *); +static void usage(void); + +#define PATIENCE 10 +#define MAX_THREADS 1024 + +static mutex_t tty = DEFAULTMUTEX; +static char who[9] = "???"; +static char *path; +static mutex_t thr_mtx = DEFAULTMUTEX; +static int thread_count = 8; /* fudge factor for system threads/fds */ +static int qflag = 0; /* quiet: we don't care about errors */ + +main(argc, argv) + int argc; + char **argv; +{ + int msize; + char buf[BUFSIZ+1]; + register i; + char hostname[256]; + int hflag; + struct rlimit rl; + + if (argc < 2) + usage(); + + if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { + rl.rlim_cur = (rl.rlim_max < MAX_THREADS ? + rl.rlim_max : MAX_THREADS); + (void) setrlimit(RLIMIT_NOFILE, &rl); + } + + (void) gethostname(hostname, sizeof (hostname)); + + init_who(); + + msize = snprintf(buf, sizeof (buf), "From %s@%s: ", who, hostname); + while ((i = getchar()) != EOF) { + if (msize >= (sizeof (buf) - 1)) { + (void) fprintf(stderr, "Message too long\n"); + exit(1); + } + buf[msize++] = i; + } + buf[msize] = '\0'; + path = buf; + hflag = 1; + while (argc > 1) { + if (argv[1][0] == '-') { + switch (argv[1][1]) { + case 'h': + hflag = 1; + break; + case 'n': + hflag = 0; + break; + case 'q': + qflag = 1; + break; + default: + usage(); + break; + } + argc--; + argv++; + continue; + } + if (hflag) { + doit(argv[1]); + } else { + char *machine, *user, *domain; + + (void) setnetgrent(argv[1]); + while (getnetgrent(&machine, &user, &domain)) { + if (machine) + doit(machine); + else + doall(); + } + (void) endnetgrent(); + } + argc--; + argv++; + } + thr_exit(NULL); + return (0); +} + +static void +init_who(void) +{ + char *wp; + struct passwd *pwd; + + wp = getlogin(); + + if (wp != NULL) + (void) strncpy(who, wp, sizeof (who)); + else { + pwd = getpwuid(getuid()); + if (pwd) + (void) strncpy(who, pwd->pw_name, sizeof (who)); + } + +} + +/* + * Saw a wild card, so do everything + */ +static void +doall(void) +{ + (void) mutex_lock(&tty); + (void) fprintf(stderr, "writing to everyone not supported\n"); + (void) mutex_unlock(&tty); +} + +/* + * Fire off a detached thread for each host in the list, if the thread + * create fails simply run synchronously. + */ +static void +doit(char *hostname) +{ + thread_t tid; + char *thread_hostname; + + (void) mutex_lock(&thr_mtx); + while (thread_count >= MAX_THREADS) { + (void) mutex_unlock(&thr_mtx); + (void) sleep(PATIENCE/2); + (void) mutex_lock(&thr_mtx); + } + + thread_count++; + (void) mutex_unlock(&thr_mtx); + + thread_hostname = strdup(hostname); + if (thread_hostname == (char *)NULL) { + (void) mutex_lock(&tty); + (void) fprintf(stderr, "Ran out of memory\n"); + (void) mutex_unlock(&tty); + exit(1); + } + + if (thr_create(NULL, 0, do_one, thread_hostname, + THR_DETACHED, &tid) != 0) { + (void) do_one(thread_hostname); + } +} + +static void * +do_one(void *arg) +{ + char *hostname = arg; + CLIENT *clnt; + struct timeval tp; + void *vp = NULL; + +#ifdef DEBUG + (void) mutex_lock(&tty); + (void) fprintf(stderr, "sending message to %s\n%s\n", hostname, path); + (void) mutex_unlock(&tty); + return (0); +#endif + tp.tv_sec = PATIENCE; + tp.tv_usec = 0; + clnt = clnt_create_timed( + hostname, WALLPROG, WALLVERS, "datagram_v", &tp); + if (clnt == NULL) { + if (!qflag) { + (void) mutex_lock(&tty); + (void) fprintf(stderr, "rwall: Can't send to %s\n", + hostname); + clnt_pcreateerror(hostname); + (void) mutex_unlock(&tty); + } + goto errout; + } + + if (wallproc_wall_1(&path, vp, clnt) != RPC_SUCCESS) { + if (!qflag) { + (void) mutex_lock(&tty); + clnt_perror(clnt, hostname); + (void) mutex_unlock(&tty); + } + } + clnt_destroy(clnt); +errout: + (void) mutex_lock(&thr_mtx); + thread_count--; + (void) mutex_unlock(&thr_mtx); + free(hostname); + return (0); +} + +static void +usage(void) +{ + (void) fprintf(stderr, + "Usage: rwall [-q] host .... [-n netgroup ....] [-h host ...]\n"); + exit(1); +} diff --git a/usr/src/cmd/rpcsvc/rwall_subr.c b/usr/src/cmd/rpcsvc/rwall_subr.c new file mode 100644 index 0000000000..cae42646d2 --- /dev/null +++ b/usr/src/cmd/rpcsvc/rwall_subr.c @@ -0,0 +1,119 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ +/* + * University Copyright- Copyright (c) 1982, 1986, 1988 + * The Regents of the University of California + * All Rights Reserved + * + * University Acknowledgment- Portions of this document are derived from + * software developed by the University of California, Berkeley, and its + * contributors. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * rwall_subr.c + * The server procedure for rwalld + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <rpc/rpc.h> +#include <thread.h> +#include <rpcsvc/rwall.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <syslog.h> +#include <string.h> + +#define WALL_PROG "/usr/sbin/wall" + +static mutex_t wall_mutex = DEFAULTMUTEX; +static char *oldmsg; + +/* ARGSUSED */ +bool_t +wallproc_wall_1_svc(wrapstring *argp, void *res, struct svc_req *rqstp) +{ + char *msg; + FILE *fp; + int rval; + struct stat wall; + + msg = *argp; + + /* + * Do not wall the same message twice in case of a retransmission + * in the rare case that two walls arrive close enough with + * a retransmission we might get a duplicate, but that is OK. + */ + (void) mutex_lock(&wall_mutex); + if ((oldmsg != 0) && (strcmp(msg, oldmsg) == 0)) { + (void) mutex_unlock(&wall_mutex); + return (TRUE); + } + + if (oldmsg) + free(oldmsg); + oldmsg = strdup(msg); + + rval = stat(WALL_PROG, &wall); + + /* + * Make sure the wall programs exists, is executeable, and runs + */ + if (rval == -1 || (wall.st_mode & S_IXUSR) == 0 || + (fp = popen(WALL_PROG, "w")) == NULL) { + syslog(LOG_NOTICE, + "rwall message received but could not execute %s", + WALL_PROG); + syslog(LOG_NOTICE, "%s", msg); +#ifdef DEBUG + (void) fprintf(stderr, + "rwall message received but could not execute %s", + WALL_PROG); + (void) fprintf(stderr, "%s", msg); +#endif + (void) mutex_unlock(&wall_mutex); + return (TRUE); + } + + (void) fprintf(fp, "%s", msg); + (void) pclose(fp); + (void) mutex_unlock(&wall_mutex); + + return (TRUE); +} + +/* ARGSUSED */ +int +wallprog_1_freeresult(SVCXPRT *transp, xdrproc_t proc, caddr_t arg) +{ + return (TRUE); +} diff --git a/usr/src/cmd/rpcsvc/spray.c b/usr/src/cmd/rpcsvc/spray.c new file mode 100644 index 0000000000..62c0fa4790 --- /dev/null +++ b/usr/src/cmd/rpcsvc/spray.c @@ -0,0 +1,180 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 1985 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ +/* + * University Copyright- Copyright (c) 1982, 1986, 1988 + * The Regents of the University of California + * All Rights Reserved + * + * University Acknowledgment- Portions of this document are derived from + * software developed by the University of California, Berkeley, and its + * contributors. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <rpc/rpc.h> +#include <rpcsvc/spray.h> +#include <sys/poll.h> + +enum clnt_stat sprayproc_spray_1nd(/*argp, clnt*/); + +#define DEFBYTES 100000 /* default numbers of bytes to send */ +#define MAXPACKETLEN 1514 +#define SPRAYOVERHEAD 86 /* size of rpc packet when size=0 */ + +main(argc, argv) + int argc; + char **argv; +{ + int c; + extern char *optarg; + extern int optind; + register CLIENT *clnt; + unsigned int i; + int delay = 0; + unsigned int psec, bsec; + int buf[SPRAYMAX/4]; + char msgbuf[256]; + unsigned int lnth, cnt; + sprayarr arr; + spraycumul cumul; + spraycumul *co; + char *host = NULL; + char *type; + + if (argc < 2) + usage(); + + cnt = 0; + lnth = SPRAYOVERHEAD; + type = "netpath"; + while (optind < argc) { + if (argv[optind][0] == '-') { + if ((c = getopt(argc, argv, "d:c:t:l:")) == EOF) { + break; + } + switch (c) { + case 'd': + delay = atoi(optarg); + break; + case 'c': + cnt = (unsigned int) atoi(optarg); + break; + case 't': + type = optarg; + break; + case 'l': + lnth = (unsigned int) atoi(optarg); + break; + default: + usage(); + } + } else { + host = argv[optind++]; + } + } + if (host == NULL) + usage(); + clnt = clnt_create(host, SPRAYPROG, SPRAYVERS, type); + if (clnt == (CLIENT *)NULL) { + sprintf(msgbuf, "spray: cannot clnt_create %s:%s", host, type); + clnt_pcreateerror(msgbuf); + exit(1); + } + if (cnt == 0) + cnt = DEFBYTES/lnth; + if (lnth < SPRAYOVERHEAD) + lnth = SPRAYOVERHEAD; + else if (lnth >= SPRAYMAX) + lnth = SPRAYMAX; + if (lnth <= MAXPACKETLEN && lnth % 4 != 2) + lnth = ((lnth + 5) / 4) * 4 - 2; + arr.sprayarr_len = lnth - SPRAYOVERHEAD; + arr.sprayarr_val = (char *) buf; + printf("sending %u packets of length %u to %s ...", cnt, lnth, host); + fflush(stdout); + if (sprayproc_clear_1(NULL, clnt) == NULL) { + clnt_perror(clnt, "SPRAYPROC_CLEAR "); + exit(1); + } + for (i = 0; i < cnt; i++) { + sprayproc_spray_1nd(&arr, clnt); + if (delay > 0) + slp(delay); + } + if ((co = sprayproc_get_1(NULL, clnt)) == NULL) { + clnt_perror(clnt, "SPRAYPROC_GET "); + exit(1); + } + cumul = *co; + if (cumul.counter < cnt) + printf("\n\t%d packets (%.3f%%) dropped by %s\n", + cnt - cumul.counter, + 100.0 * (cnt - cumul.counter)/cnt, host); + else + printf("\n\tno packets dropped by %s\n", host); + psec = (1000000.0 * cumul.counter) + / (1000000.0 * cumul.clock.sec + cumul.clock.usec); + bsec = (lnth * 1000000.0 * cumul.counter)/ + (1000000.0 * cumul.clock.sec + cumul.clock.usec); + printf("\t%u packets/sec, %u bytes/sec\n", psec, bsec); + exit(0); + /* NOTREACHED */ +} + +/* + * A special call, where the TIMEOUT is 0. So, every call times-out. + */ +static struct timeval TIMEOUT = { 0, 0 }; + +enum clnt_stat +sprayproc_spray_1nd(argp, clnt) + sprayarr *argp; + CLIENT *clnt; +{ + return (clnt_call(clnt, SPRAYPROC_SPRAY, xdr_sprayarr, (caddr_t)argp, + xdr_void, NULL, TIMEOUT)); +} + +/* A cheap milliseconds sleep call */ +slp(usecs) +{ + static struct pollfd pfds[1] = { + 0, POLLIN, 0 + }; + pfds[0].fd = fileno(stdout); + poll(pfds, 1, usecs/1000); +} + +usage() +{ + printf("spray host [-t nettype] [-l lnth] [-c cnt] [-d delay]\n"); + exit(1); +} diff --git a/usr/src/cmd/rpcsvc/spray.xml b/usr/src/cmd/rpcsvc/spray.xml new file mode 100644 index 0000000000..f11bc45425 --- /dev/null +++ b/usr/src/cmd/rpcsvc/spray.xml @@ -0,0 +1,112 @@ +<?xml version='1.0'?> +<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'> + +<!-- + Copyright 2005 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License, Version 1.0 only + (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 + + ident "%Z%%M% %I% %E% SMI" + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. + + Service manifest for rpc.sprayd +--> + +<service_bundle type='manifest' name='SUNWrcmdr:sprayd'> + +<service + name='network/rpc/spray' + type='service' + version='1'> + + <create_default_instance enabled='false' /> + + <restarter> + <service_fmri value='svc:/network/inetd:default' /> + </restarter> + + <dependency name='rpcbind' + grouping='require_all' + restart_on='restart' + type='service'> + <service_fmri value='svc:/network/rpc/bind' /> + </dependency> + + <exec_method + type='method' + name='inetd_start' + exec='/usr/lib/netsvc/spray/rpc.sprayd' + timeout_seconds='0'> + <method_context> + <method_credential user='root' group='root' /> + </method_context> + </exec_method> + + <exec_method + type='method' + name='inetd_offline' + exec=':kill_process' + timeout_seconds='0'> + </exec_method> + + <exec_method + type='method' + name='inetd_disable' + exec=':kill' + timeout_seconds='0'> + </exec_method> + + <property_group name='inetd' type='framework'> + <stability value='Evolving' /> + <propval name='name' type='astring' value='sprayd' /> + <propval name='endpoint_type' type='astring' value='tli' /> + <propval name='proto' type='astring' value='datagram_v' + override='true' /> + <propval name='isrpc' type='boolean' value='true' /> + <propval name='rpc_low_version' type='integer' value='1' /> + <propval name='rpc_high_version' type='integer' value='1' /> + <propval name='wait' type='boolean' value='true' /> + </property_group> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + RPC spray + </loctext> + </common_name> + <documentation> + <manpage title='rpc.sprayd' section='1M' + manpath='/usr/share/man' /> + <manpage title='sprayd' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> + +</service> + +</service_bundle> diff --git a/usr/src/cmd/rpcsvc/spray_subr.c b/usr/src/cmd/rpcsvc/spray_subr.c new file mode 100644 index 0000000000..dbaf4cb311 --- /dev/null +++ b/usr/src/cmd/rpcsvc/spray_subr.c @@ -0,0 +1,80 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 1985 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ +/* + * University Copyright- Copyright (c) 1982, 1986, 1988 + * The Regents of the University of California + * All Rights Reserved + * + * University Acknowledgment- Portions of this document are derived from + * software developed by the University of California, Berkeley, and its + * contributors. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <rpc/rpc.h> +#include <rpcsvc/spray.h> + +static spraycumul cumul; +static spraytimeval start_time; + +void * +sprayproc_spray_1(argp, clnt) + sprayarr *argp; + CLIENT *clnt; +{ + cumul.counter++; + return ((void *)0); +} + +spraycumul * +sprayproc_get_1(argp, clnt) + void *argp; + CLIENT *clnt; +{ + gettimeofday((struct timeval *)&cumul.clock, 0); + if (cumul.clock.usec < start_time.usec) { + cumul.clock.usec += 1000000; + cumul.clock.sec -= 1; + } + cumul.clock.sec -= start_time.sec; + cumul.clock.usec -= start_time.usec; + return (&cumul); +} + +void * +sprayproc_clear_1(argp, clnt) + void *argp; + CLIENT *clnt; +{ + static char res; + + cumul.counter = 0; + gettimeofday((struct timeval *)&start_time, 0); + (void) memset((char *)&res, 0, sizeof(res)); + return ((void *)&res); +} diff --git a/usr/src/cmd/rpcsvc/wall.xml b/usr/src/cmd/rpcsvc/wall.xml new file mode 100644 index 0000000000..b500e35062 --- /dev/null +++ b/usr/src/cmd/rpcsvc/wall.xml @@ -0,0 +1,112 @@ +<?xml version='1.0'?> +<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'> + +<!-- + Copyright 2005 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License, Version 1.0 only + (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 + + ident "%Z%%M% %I% %E% SMI" + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. + + Service manifest for rpc.rwalld +--> + +<service_bundle type='manifest' name='SUNWrcmdr:walld'> + +<service + name='network/rpc/wall' + type='service' + version='1'> + + <create_default_instance enabled='false' /> + + <restarter> + <service_fmri value='svc:/network/inetd:default' /> + </restarter> + + <dependency name='rpcbind' + grouping='require_all' + restart_on='restart' + type='service'> + <service_fmri value='svc:/network/rpc/bind' /> + </dependency> + + <exec_method + type='method' + name='inetd_start' + exec='/usr/lib/netsvc/rwall/rpc.rwalld' + timeout_seconds='0'> + <method_context> + <method_credential user='noaccess' group='noaccess' /> + </method_context> + </exec_method> + + <exec_method + type='method' + name='inetd_offline' + exec=':kill_process' + timeout_seconds='0'> + </exec_method> + + <exec_method + type='method' + name='inetd_disable' + exec=':kill' + timeout_seconds='0'> + </exec_method> + + <property_group name='inetd' type='framework'> + <stability value='Evolving' /> + <propval name='name' type='astring' value='walld' /> + <propval name='endpoint_type' type='astring' value='tli' /> + <propval name='proto' type='astring' value='datagram_v' + override='true' /> + <propval name='isrpc' type='boolean' value='true' /> + <propval name='rpc_low_version' type='integer' value='1' /> + <propval name='rpc_high_version' type='integer' value='1' /> + <propval name='wait' type='boolean' value='true' /> + </property_group> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + network rwall server + </loctext> + </common_name> + <documentation> + <manpage title='rpc.rwalld' section='1M' + manpath='/usr/share/man' /> + <manpage title='rwalld' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> + +</service> + +</service_bundle> |
