summaryrefslogtreecommitdiff
path: root/usr/src/lib/nsswitch/nisplus/common/getspent.c
blob: dd246e3b7679914826a5e2ab0c519fcc43fccf04 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 *  nisplus/getspent.c: implementations of getspnam(), getspent(), setspent(),
 *  endspent() for NIS+.  We keep the shadow information in a column
 *  ("shadow") of the same table that stores vanilla passwd information.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/types.h>
#include <shadow.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <thread.h>
#include "nisplus_common.h"
#include "nisplus_tables.h"

extern int key_secretkey_is_set_g();

/*
 * bugid 4301477:
 * We lock NIS+/getspnam() so there is only one at a time,
 * So applications which link with libthread can now call
 * getspnam() (or UNIX pam_authenticate() which calls getspnam)
 * in a Secure NIS+ environment (as per CERT Advisory 96.10).
 * This is usually not a problem as login/su/dtlogin are single
 * threaded, note dtlogin is now linked with libthread (bugid 4263325)
 * which is why this bug exists (Note thr_main() check was removed)
 */
extern int _mutex_lock(mutex_t *mp);
extern int _mutex_unlock(mutex_t *mp);

static mutex_t  one_lane = DEFAULTMUTEX;


static nss_status_t
getbynam(be, a)
	nisplus_backend_ptr_t	be;
	void			*a;
{
	nss_XbyY_args_t		*argp	= (nss_XbyY_args_t *)a;
	struct spwd		*sp	= (struct spwd *)argp->buf.result;
	int			buflen	= argp->buf.buflen;
	nss_status_t		status;
	const char		*username;
	uid_t			orig_uid;
	uid_t			entry_uid;
	struct spwd		save_sp;
	char			*save_buf;

	/* part of fix for bugid 4301477 */
	_mutex_lock(&one_lane);

	/*
	 * There is a dirty little private protocol with the nis_object2str()
	 * routine below:  it gives us back a uid in the argp->key.uid
	 * field.  Since "key" is a union, and we're using key.name,
	 * we save/restore it in case anyone cares.
	 *
	 * NSS2: be->flag is used to indicate *NP* case since we
	 * may not have the shadow passwd available at this point
	 * if called by nscd's switch.
	 */
	username = argp->key.name;
	be->flag = 0;

	status = _nss_nisplus_lookup(be, argp, PW_TAG_NAME, username);

	/*
	 * passwd.org_dir may have its access rights set up so that
	 * the passwd field can only be read by the user whom
	 * the entry describes.  If we get an *NP* in the password
	 * field we should try to get it again as the user.  If not,
	 * we return now.
	 */

	/* fix for bugid 4301477 DELETED if (_thr_main() != -1) goto out; */

	if (status != NSS_SUCCESS || argp->returnval == 0 || be->flag == 0)
		goto out;

	/* Get our current euid and that of the entry */
	orig_uid = geteuid();
	entry_uid = argp->key.uid;
	be->flag = 0;

	/*
	 * If the entry uid differs from our own euid, set our euid to
	 * the entry uid and try the lookup again.
	 */

	if ((entry_uid != orig_uid) && (seteuid(entry_uid) != -1)) {
		/*
		 * Do the second lookup only if secretkey is set for
		 * this euid, otherwise it will be pointless.  Also,
		 * make sure we can allocate space to save the old
		 * results.
		 */
		if (key_secretkey_is_set_g(0, 0) &&
		    ((save_buf = (char *)malloc(buflen)) != 0)) {

			/* Save the old results in case the new lookup fails */
			(void) memcpy(save_buf, argp->buf.buffer, buflen);
			save_sp = *sp;

			/* Do the lookup (this time as the user). */
			status = _nss_nisplus_lookup(be, argp, PW_TAG_NAME,
						    username);

			/* If it failed, restore the old results */
			if (status != NSS_SUCCESS) {
				(void) memcpy(argp->buf.buffer, save_buf,
					buflen);
				*sp = save_sp;
				status = NSS_SUCCESS;
			}

			free(save_buf);
		}

		/* Set uid back */
		(void) seteuid(orig_uid);
	}

out:
	/* end of fix for bugid 4301477 unlock NIS+/getspnam() */
	_mutex_unlock(&one_lane);

	argp->key.name = username;
	return (status);
}

/*
 * place the results from the nis_object structure into argp->buf.result
 * Returns NSS_STR_PARSE_{SUCCESS, ERANGE, PARSE}
 */
/*ARGSUSED*/
static int
nis_object2str(nobj, obj, be, argp)
	int			nobj;
	nis_object		*obj;
	nisplus_backend_ptr_t	be;
	nss_XbyY_args_t		*argp;
{
	char			*buffer, *name, *passwd, *shadow;
	int			buflen, namelen, passwdlen, shadowlen;
	char			*endnum, *uidstr;
	int			uidlen;
	struct entry_col	*ecol;

	/*
	 * If we got more than one nis_object, we just ignore it.
	 * 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 (NSS_STR_PARSE_PARSE);
	}
	ecol = obj->EN_data.en_cols.en_cols_val;

	/* name: user name */
	__NISPLUS_GETCOL_OR_RETURN(ecol, PW_NDX_NAME, namelen, name);

	/* passwd */
	__NISPLUS_GETCOL_OR_EMPTY(ecol, PW_NDX_PASSWD, passwdlen, passwd);

	/* uid */
	__NISPLUS_GETCOL_OR_RETURN(ecol, PW_NDX_UID, uidlen, uidstr);
	(void) strtol(uidstr, &endnum, 10);
	if (*endnum != 0 || endnum == uidstr)
		return (NSS_STR_PARSE_PARSE);
	/*
	 * See discussion of private protocol in getbynam() above.
	 *   Note that we also end up doing this if we're called from
	 *   _nss_nisplus_getent(), but that's OK -- when we're doing
	 *   enumerations we don't care what's in the argp->key union.
	 */
	if (strncmp(passwd, "*NP*", passwdlen) == 0)
		be->flag = 1;

	/*
	 * shadow information
	 *
	 * We will be lenient to no shadow field or a shadow field
	 * with less than the desired number of ":" separated longs.
	 * XXX - should we be more strict ?
	 */
	__NISPLUS_GETCOL_OR_EMPTY(ecol, PW_NDX_SHADOW, shadowlen, shadow);

	buflen = namelen + passwdlen + shadowlen + 3;
	if (argp->buf.result != NULL) {
		if ((be->buffer = calloc(1, buflen)) == NULL)
			return (NSS_STR_PARSE_PARSE);
		/* exclude trailing null from length */
		be->buflen = buflen - 1;
		buffer = be->buffer;
	} else {
		if (buflen > argp->buf.buflen)
			return (NSS_STR_PARSE_ERANGE);
		buflen = argp->buf.buflen;
		buffer = argp->buf.buffer;
		(void) memset(buffer, 0, buflen);
	}
	(void) snprintf(buffer, buflen, "%s:%s:%s",
		name, passwd, shadow);
#ifdef DEBUG
	(void) fprintf(stdout, "shadow [%s]\n", buffer);
	(void) fflush(stdout);
#endif  /* DEBUG */
	return (NSS_STR_PARSE_SUCCESS);
}

static nisplus_backend_op_t sp_ops[] = {
	_nss_nisplus_destr,
	_nss_nisplus_endent,
	_nss_nisplus_setent,
	_nss_nisplus_getent,
	getbynam
};

/*ARGSUSED*/
nss_backend_t *
_nss_nisplus_shadow_constr(dummy1, dummy2, dummy3)
	const char	*dummy1, *dummy2, *dummy3;
{
	return (_nss_nisplus_constr(sp_ops,
				    sizeof (sp_ops) / sizeof (sp_ops[0]),
				    PW_TBLNAME, nis_object2str));
}