1/*	$OpenBSD: tokendb.c,v 1.11 2019/06/28 13:32:53 deraadt Exp $	*/
2
3/*-
4 * Copyright (c) 1995 Migration Associates Corp. All Rights Reserved
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *      This product includes software developed by Berkeley Software Design,
17 *      Inc.
18 * 4. The name of Berkeley Software Design, Inc.  may not be used to endorse
19 *    or promote products derived from this software without specific prior
20 *    written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 *	BSDI $From: tokendb.c,v 1.1 1996/08/26 20:13:10 prb Exp $
35 */
36
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <sys/time.h>
40#include <sys/resource.h>
41
42#include <ctype.h>
43#include <db.h>
44#include <errno.h>
45#include <fcntl.h>
46#include <grp.h>
47#include <limits.h>
48#include <stdio.h>
49#include <syslog.h>
50#include <stdlib.h>
51#include <string.h>
52#include <unistd.h>
53
54#include "token.h"
55#include "tokendb.h"
56
57static	DB	*tokendb;
58
59/*
60 * Static function prototypes
61 */
62
63static	int	tokendb_open(void);
64static	void	tokendb_close(void);
65
66/*
67 * Retrieve a user record from the token database file
68 */
69
70int
71tokendb_getrec(char *username, TOKENDB_Rec *tokenrec)
72{
73	DBT	key;
74	DBT	data;
75	int	status = 0;
76
77	key.data = username;
78	key.size = strlen(username) + 1;
79	memset(&data, 0, sizeof(data));
80
81	if (tokendb_open())
82		return(-1);
83
84	status = (tokendb->get)(tokendb, &key, &data, 0);
85	switch (status) {
86	case 1:
87		tokendb_close();
88		return(ENOENT);
89	case -1:
90		tokendb_close();
91		return(-1);
92	}
93	memcpy(tokenrec, data.data, sizeof(TOKENDB_Rec));
94	if ((tokenrec->flags & TOKEN_USEMODES) == 0)
95		tokenrec->mode = tt->modes & ~TOKEN_RIM;
96	tokendb_close();
97	return (0);
98}
99
100/*
101 * Put a user record to the token database file.
102 */
103
104int
105tokendb_putrec(char *username, TOKENDB_Rec *tokenrec)
106{
107	DBT	key;
108	DBT	data;
109	int	status = 0;
110
111	key.data = username;
112	key.size = strlen(username) + 1;
113
114	if (tokenrec->mode)
115		tokenrec->flags |= TOKEN_USEMODES;
116	data.data = tokenrec;
117	data.size = sizeof(TOKENDB_Rec);
118
119	if (!tokendb_open()) {
120		if (flock((tokendb->fd)(tokendb), LOCK_EX)) {
121			tokendb_close();
122			return (-1);
123		}
124		status = (tokendb->put)(tokendb, &key, &data, 0);
125	}
126	tokendb_close();
127	return (status);
128}
129
130/*
131 * Remove a user record from the token database file.
132 */
133
134int
135tokendb_delrec(char *username)
136{
137	DBT	key;
138	int	status = 0;
139
140	key.data = username;
141	key.size = strlen(username) + 1;
142
143	if (!tokendb_open()) {
144		if (flock((tokendb->fd)(tokendb), LOCK_EX)) {
145			tokendb_close();
146			return (-1);
147		}
148		status = (tokendb->del)(tokendb, &key, 0);
149	}
150	tokendb_close();
151	return (status);
152}
153
154/*
155 * Open the token database.  In order to expedite access in
156 * heavily loaded conditions, we employ a N1 lock method.
157 * Updates should be brief, so all locks wait infinitely.
158 * Wait for a read (shared) lock as all updates read first.
159 */
160
161static	int
162tokendb_open(void)
163{
164	int	must_set_perms = 0;
165	int	must_set_mode = 0;
166	struct	group	*grp;
167	struct	stat	statb;
168
169	if ((grp = getgrnam(TOKEN_GROUP)) == NULL) {
170		printf("Missing %s group, authentication disabled\n",
171		    TOKEN_GROUP);
172		fflush(stdout);
173		syslog(LOG_ALERT,
174		    "the %s group is missing, token authentication disabled",
175		    TOKEN_GROUP);
176		return (-1);
177	}
178
179	if (stat(tt->db, &statb) == -1) {
180		if (errno != ENOENT)
181			return (-1);
182		must_set_perms++;
183	} else {
184		if (statb.st_uid != 0 || statb.st_gid != grp->gr_gid) {
185#ifdef PARANOID
186			printf("Authentication disabled\n");
187			fflush(stdout);
188			syslog(LOG_ALERT,
189			    "POTENTIAL COMPROMISE of %s. Owner was %u, "
190			    "Group was %u", tt->db, statb.st_uid, statb.st_gid);
191			return (-1);
192#else
193			must_set_perms++;
194#endif
195		}
196		if ((statb.st_mode & 0777) != 0640) {
197#ifdef PARANOID
198			printf("Authentication disabled\n");
199			fflush(stdout);
200			syslog(LOG_ALERT,
201			    "POTENTIAL COMPROMISE of %s. Mode was %o",
202			    tt->db, statb.st_mode);
203			return (-1);
204#else
205			must_set_mode++;
206#endif
207		}
208	}
209	if (!(tokendb =
210	    dbopen(tt->db, O_CREAT | O_RDWR, 0640, DB_BTREE, 0)) )
211		return (-1);
212
213	if (flock((tokendb->fd)(tokendb), LOCK_SH)) {
214		(tokendb->close)(tokendb);
215		return (-1);
216	}
217	if (must_set_perms && fchown((tokendb->fd)(tokendb), 0, grp->gr_gid))
218		syslog(LOG_INFO,
219		    "Can't set owner/group of %s errno=%m", tt->db);
220	if (must_set_mode && fchmod((tokendb->fd)(tokendb), 0640))
221		syslog(LOG_INFO,
222		    "Can't set mode of %s errno=%m", tt->db);
223
224	return (0);
225}
226
227/*
228 * Close the token database.  We are holding an unknown lock.
229 * Release it, then close the db. Since little can be done
230 * about errors, we ignore them.
231 */
232
233static	void
234tokendb_close(void)
235{
236	if (tokendb) {
237		(void)flock((tokendb->fd)(tokendb), LOCK_UN);
238		(tokendb->close)(tokendb);
239		tokendb = NULL;
240	}
241}
242
243/*
244 * Retrieve the first user record from the database, leaving the
245 * database open for the next retrieval. If the march thru the
246 * the database is aborted before end-of-file, the caller should
247 * call tokendb_close to release the read lock.
248 */
249
250int
251tokendb_firstrec(int reverse_flag, TOKENDB_Rec *tokenrec)
252{
253	DBT	key;
254	DBT	data;
255	int	status = 0;
256
257	memset(&data, 0, sizeof(data));
258
259	if (!tokendb_open()) {
260		status = (tokendb->seq)(tokendb, &key, &data,
261				reverse_flag ? R_LAST : R_FIRST);
262	}
263	if (status) {
264		tokendb_close();
265		return (status);
266	}
267	if (!data.data) {
268		tokendb_close();
269		return (ENOENT);
270	}
271	memcpy(tokenrec, data.data, sizeof(TOKENDB_Rec));
272	if ((tokenrec->flags & TOKEN_USEMODES) == 0)
273		tokenrec->mode = tt->modes & ~TOKEN_RIM;
274	return (0);
275}
276
277/*
278 * Retrieve the next sequential user record from the database. Close
279 * the database only on end-of-file or error.
280 */
281
282
283int
284tokendb_nextrec(int reverse_flag, TOKENDB_Rec *tokenrec)
285{
286	DBT	key;
287	DBT	data;
288	int	status;
289
290	memset(&data, 0, sizeof(data));
291
292	status = (tokendb->seq)(tokendb, &key, &data,
293		reverse_flag ? R_PREV : R_NEXT);
294
295	if (status) {
296		tokendb_close();
297		return (status);
298	}
299	if (!data.data) {
300		tokendb_close();
301		return (ENOENT);
302	}
303	memcpy(tokenrec, data.data, sizeof(TOKENDB_Rec));
304	if ((tokenrec->flags & TOKEN_USEMODES) == 0)
305		tokenrec->mode = tt->modes & ~TOKEN_RIM;
306	return (0);
307}
308
309/*
310 * Retrieve and lock a user record.  Since there are no facilities in
311 * BSD for record locking, we hack a bit lock into the user record.
312 */
313
314int
315tokendb_lockrec(char *username, TOKENDB_Rec *tokenrec, unsigned recflags)
316{
317	DBT	key;
318	DBT	data;
319	int	status;
320
321	key.data = username;
322	key.size = strlen(username) + 1;
323	memset(&data, 0, sizeof(data));
324
325	if (tokendb_open())
326		return(-1);
327
328	if (flock((tokendb->fd)(tokendb), LOCK_EX)) {
329		tokendb_close();
330		return(-1);
331	}
332	switch ((tokendb->get)(tokendb, &key, &data, 0)) {
333	case 1:
334		tokendb_close();
335		return (ENOENT);
336	case -1:
337		tokendb_close();
338		return(-1);
339	}
340	memcpy(tokenrec, data.data, sizeof(TOKENDB_Rec));
341
342	if ((tokenrec->flags & TOKEN_LOCKED)||(tokenrec->flags & recflags)) {
343		tokendb_close();
344		return(1);
345	}
346	data.data = tokenrec;
347	data.size = sizeof(TOKENDB_Rec);
348
349	time(&tokenrec->lock_time);
350	tokenrec->flags |= recflags;
351	status = (tokendb->put)(tokendb, &key, &data, 0);
352	tokendb_close();
353	if (status)
354		return(-1);
355	if ((tokenrec->flags & TOKEN_USEMODES) == 0)
356		tokenrec->mode = tt->modes & ~TOKEN_RIM;
357
358	return(0);
359}
360
361