1/*++
2/* NAME
3/*	dict_nis 3
4/* SUMMARY
5/*	dictionary manager interface to NIS maps
6/* SYNOPSIS
7/*	#include <dict_nis.h>
8/*
9/*	DICT	*dict_nis_open(map, open_flags, dict_flags)
10/*	const char *map;
11/*	int	open_flags;
12/*	int	dict_flags;
13/* DESCRIPTION
14/*	dict_nis_open() makes the specified NIS map accessible via
15/*	the generic dictionary operations described in dict_open(3).
16/* SEE ALSO
17/*	dict(3) generic dictionary manager
18/* DIAGNOSTICS
19/*	Fatal errors: out of memory, attempt to update NIS map.
20/* LICENSE
21/* .ad
22/* .fi
23/*	The Secure Mailer license must be distributed with this software.
24/* AUTHOR(S)
25/*	Wietse Venema
26/*	IBM T.J. Watson Research
27/*	P.O. Box 704
28/*	Yorktown Heights, NY 10598, USA
29/*--*/
30
31/* System library. */
32
33#include "sys_defs.h"
34#include <string.h>
35
36#ifdef STRCASECMP_IN_STRINGS_H
37#include <strings.h>
38#endif
39
40#ifdef HAS_NIS
41
42#include <rpcsvc/ypclnt.h>
43#ifndef YPERR_BUSY
44#define YPERR_BUSY  16
45#endif
46#ifndef YPERR_ACCESS
47#define YPERR_ACCESS  15
48#endif
49
50#endif
51
52/* Utility library. */
53
54#include "msg.h"
55#include "mymalloc.h"
56#include "vstring.h"
57#include "stringops.h"
58#include "dict.h"
59#include "dict_nis.h"
60
61#ifdef HAS_NIS
62
63/* Application-specific. */
64
65typedef struct {
66    DICT    dict;			/* generic members */
67} DICT_NIS;
68
69 /*
70  * Class variables, so that multiple maps can share this info.
71  */
72static char dict_nis_disabled[1];
73static char *dict_nis_domain;
74
75/* dict_nis_init - NIS binding */
76
77static void dict_nis_init(void)
78{
79    const char *myname = "dict_nis_init";
80
81    if (yp_get_default_domain(&dict_nis_domain) != 0
82	|| dict_nis_domain == 0 || *dict_nis_domain == 0
83	|| strcasecmp(dict_nis_domain, "(none)") == 0) {
84	dict_nis_domain = dict_nis_disabled;
85	msg_warn("%s: NIS domain name not set - NIS lookups disabled", myname);
86    }
87    if (msg_verbose)
88	msg_info("%s: NIS domain %s", myname, dict_nis_domain);
89}
90
91/* dict_nis_strerror - map error number to string */
92
93static char *dict_nis_strerror(int err)
94{
95
96    /*
97     * Grr. There should be a standard function for this.
98     */
99    switch (err) {
100	case YPERR_BADARGS:
101	return ("args to function are bad");
102    case YPERR_RPC:
103	return ("RPC failure - domain has been unbound");
104    case YPERR_DOMAIN:
105	return ("can't bind to server on this domain");
106    case YPERR_MAP:
107	return ("no such map in server's domain");
108    case YPERR_KEY:
109	return ("no such key in map");
110    case YPERR_YPERR:
111	return ("internal yp server or client error");
112    case YPERR_RESRC:
113	return ("resource allocation failure");
114    case YPERR_NOMORE:
115	return ("no more records in map database");
116    case YPERR_PMAP:
117	return ("can't communicate with portmapper");
118    case YPERR_YPBIND:
119	return ("can't communicate with ypbind");
120    case YPERR_YPSERV:
121	return ("can't communicate with ypserv");
122    case YPERR_NODOM:
123	return ("local domain name not set");
124    case YPERR_BADDB:
125	return ("yp database is bad");
126    case YPERR_VERS:
127	return ("yp version mismatch");
128    case YPERR_ACCESS:
129	return ("access violation");
130    case YPERR_BUSY:
131	return ("database busy");
132    default:
133	return ("unknown NIS lookup error");
134    }
135}
136
137/* dict_nis_lookup - find table entry */
138
139static const char *dict_nis_lookup(DICT *dict, const char *key)
140{
141    DICT_NIS *dict_nis = (DICT_NIS *) dict;
142    static char *result;
143    int     result_len;
144    int     err;
145    static VSTRING *buf;
146
147    dict->error = 0;
148
149    /*
150     * Sanity check.
151     */
152    if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
153	msg_panic("dict_nis_lookup: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
154
155    if (dict_nis_domain == dict_nis_disabled)
156	return (0);
157
158    /*
159     * Optionally fold the key.
160     */
161    if (dict->flags & DICT_FLAG_FOLD_FIX) {
162	if (dict->fold_buf == 0)
163	    dict->fold_buf = vstring_alloc(10);
164	vstring_strcpy(dict->fold_buf, key);
165	key = lowercase(vstring_str(dict->fold_buf));
166    }
167
168    /*
169     * See if this NIS map was written with one null byte appended to key and
170     * value.
171     */
172    if (dict->flags & DICT_FLAG_TRY1NULL) {
173	err = yp_match(dict_nis_domain, dict_nis->dict.name,
174		       (void *) key, strlen(key) + 1,
175		       &result, &result_len);
176	if (err == 0) {
177	    dict->flags &= ~DICT_FLAG_TRY0NULL;
178	    return (result);
179	}
180    }
181
182    /*
183     * See if this NIS map was written with no null byte appended to key and
184     * value. This should never be the case, but better play safe.
185     */
186    if (dict->flags & DICT_FLAG_TRY0NULL) {
187	err = yp_match(dict_nis_domain, dict_nis->dict.name,
188		       (void *) key, strlen(key),
189		       &result, &result_len);
190	if (err == 0) {
191	    dict->flags &= ~DICT_FLAG_TRY1NULL;
192	    if (buf == 0)
193		buf = vstring_alloc(10);
194	    vstring_strncpy(buf, result, result_len);
195	    return (vstring_str(buf));
196	}
197    }
198
199    /*
200     * When the NIS lookup fails for reasons other than "key not found", keep
201     * logging warnings, and hope that someone will eventually notice the
202     * problem and fix it.
203     */
204    if (err != YPERR_KEY) {
205	msg_warn("lookup %s, NIS domain %s, map %s: %s",
206		 key, dict_nis_domain, dict_nis->dict.name,
207		 dict_nis_strerror(err));
208	dict->error = DICT_ERR_RETRY;
209    }
210    return (0);
211}
212
213/* dict_nis_close - close NIS map */
214
215static void dict_nis_close(DICT *dict)
216{
217    if (dict->fold_buf)
218	vstring_free(dict->fold_buf);
219    dict_free(dict);
220}
221
222/* dict_nis_open - open NIS map */
223
224DICT   *dict_nis_open(const char *map, int open_flags, int dict_flags)
225{
226    DICT_NIS *dict_nis;
227
228    if (open_flags != O_RDONLY)
229	return (dict_surrogate(DICT_TYPE_NIS, map, open_flags, dict_flags,
230			       "%s:%s map requires O_RDONLY access mode",
231			       DICT_TYPE_NIS, map));
232
233    dict_nis = (DICT_NIS *) dict_alloc(DICT_TYPE_NIS, map, sizeof(*dict_nis));
234    dict_nis->dict.lookup = dict_nis_lookup;
235    dict_nis->dict.close = dict_nis_close;
236    dict_nis->dict.flags = dict_flags | DICT_FLAG_FIXED;
237    if ((dict_flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
238	dict_nis->dict.flags |= (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL);
239    if (dict_flags & DICT_FLAG_FOLD_FIX)
240	dict_nis->dict.fold_buf = vstring_alloc(10);
241    if (dict_nis_domain == 0)
242	dict_nis_init();
243    dict_nis->dict.owner.status = DICT_OWNER_TRUSTED;
244    return (DICT_DEBUG (&dict_nis->dict));
245}
246
247#endif
248