xref: /openbsd/usr.bin/finger/util.c (revision 91f110e0)
1 /*	$OpenBSD: util.c,v 1.24 2013/11/26 13:18:55 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1989 The Regents of the University of California.
5  * All rights reserved.
6  * Portions Copyright (c) 1983, 1995, 1996 Eric P. Allman (woof!)
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/types.h>
37 #include <sys/uio.h>
38 #include <sys/param.h>
39 #include <sys/stat.h>
40 #include <err.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <ctype.h>
44 #include <string.h>
45 #include <paths.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <unistd.h>
49 #include <vis.h>
50 #include <err.h>
51 #include "finger.h"
52 #include "extern.h"
53 
54 char	*estrdup(char *);
55 WHERE	*walloc(PERSON *pn);
56 void	find_idle_and_ttywrite(WHERE *);
57 void	userinfo(PERSON *, struct passwd *);
58 
59 struct storage {
60 	struct storage *next;
61 	char a[1];
62 };
63 
64 void
65 free_storage(struct storage *st)
66 {
67 	struct storage *nx;
68 
69 	while (st != NULL) {
70 		nx = st->next;
71 		free(st);
72 		st = nx;
73 	}
74 }
75 
76 void
77 find_idle_and_ttywrite(WHERE *w)
78 {
79 	struct stat sb;
80 
81 	(void)snprintf(tbuf, sizeof(tbuf), "%s%s", _PATH_DEV, w->tty);
82 	if (stat(tbuf, &sb) < 0) {
83 		/* Don't bitch about it, just handle it... */
84 		w->idletime = 0;
85 		w->writable = 0;
86 
87 		return;
88 	}
89 	w->idletime = now < sb.st_atime ? 0 : now - sb.st_atime;
90 
91 #define	TALKABLE	0220		/* tty is writable if 220 mode */
92 	w->writable = ((sb.st_mode & TALKABLE) == TALKABLE);
93 }
94 
95 char *
96 estrdup(char *s)
97 {
98 	char *p = strdup(s);
99 	if (!p)
100 		err(1, "strdup");
101 	return (p);
102 }
103 
104 void
105 userinfo(PERSON *pn, struct passwd *pw)
106 {
107 	char *p;
108 	char *bp, name[1024];
109 	struct stat sb;
110 
111 	pn->realname = pn->office = pn->officephone = pn->homephone = NULL;
112 
113 	pn->uid = pw->pw_uid;
114 	pn->name = estrdup(pw->pw_name);
115 	pn->dir = estrdup(pw->pw_dir);
116 	pn->shell = estrdup(pw->pw_shell);
117 
118 	(void)strncpy(bp = tbuf, pw->pw_gecos, sizeof(tbuf));
119 
120 	/* ampersands get replaced by the login name */
121 	if (!(p = strsep(&bp, ",")))
122 		return;
123 	expandusername(p, pw->pw_name, name, sizeof(name));
124 	pn->realname = estrdup(name);
125 	pn->office = ((p = strsep(&bp, ",")) && *p) ?
126 	    estrdup(p) : NULL;
127 	pn->officephone = ((p = strsep(&bp, ",")) && *p) ?
128 	    estrdup(p) : NULL;
129 	pn->homephone = ((p = strsep(&bp, ",")) && *p) ?
130 	    estrdup(p) : NULL;
131 	(void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILSPOOL,
132 	    pw->pw_name);
133 	pn->mailrecv = -1;		/* -1 == not_valid */
134 	if (stat(tbuf, &sb) < 0) {
135 		if (errno != ENOENT) {
136 			warn("%s", tbuf);
137 			return;
138 		}
139 	} else if (sb.st_size != 0) {
140 		pn->mailrecv = sb.st_mtime;
141 		pn->mailread = sb.st_atime;
142 	}
143 }
144 
145 int
146 match(struct passwd *pw, char *user)
147 {
148 	char *p, *t;
149 	char name[1024];
150 
151 	(void)strncpy(p = tbuf, pw->pw_gecos, sizeof(tbuf));
152 
153 	/* ampersands get replaced by the login name */
154 	if (!(p = strtok(p, ",")))
155 		return (0);
156 	expandusername(p, pw->pw_name, name, sizeof(name));
157 	for (t = name; (p = strtok(t, "\t ")) != NULL; t = NULL)
158 		if (!strcasecmp(p, user))
159 			return (1);
160 	return (0);
161 }
162 
163 /* inspired by usr.sbin/sendmail/util.c::buildfname */
164 void
165 expandusername(char *gecos, char *login, char *buf, int buflen)
166 {
167 	char *p, *bp;
168 
169 	/* why do we skip asterisks!?!? */
170 	if (*gecos == '*')
171 		gecos++;
172 	bp = buf;
173 
174 	/* copy gecos, interpolating & to be full name */
175 	for (p = gecos; *p != '\0'; p++) {
176 		if (bp >= &buf[buflen - 1]) {
177 			/* buffer overflow - just use login name */
178 			strlcpy(buf, login, buflen);
179 			buf[buflen - 1] = '\0';
180 			return;
181 		}
182 		if (*p == '&') {
183 			/* interpolate full name */
184 			strlcpy(bp, login, buflen - (bp - buf));
185 			*bp = toupper((unsigned char)*bp);
186 			bp += strlen(bp);
187 		}
188 		else
189 			*bp++ = *p;
190 	}
191 	*bp = '\0';
192 }
193 
194 void
195 enter_lastlog(PERSON *pn)
196 {
197 	WHERE *w;
198 	static int opened, fd;
199 	struct lastlog ll;
200 	char doit = 0;
201 
202 	/* some systems may not maintain lastlog, don't report errors. */
203 	if (!opened) {
204 		fd = open(_PATH_LASTLOG, O_RDONLY);
205 		opened = 1;
206 	}
207 	if (fd == -1 ||
208 	    lseek(fd, (off_t)(pn->uid * sizeof(ll)), SEEK_SET) !=
209 	    (long)(pn->uid * sizeof(ll)) ||
210 	    read(fd, (char *)&ll, sizeof(ll)) != sizeof(ll)) {
211 			/* as if never logged in */
212 			ll.ll_line[0] = ll.ll_host[0] = '\0';
213 			ll.ll_time = 0;
214 		}
215 	if ((w = pn->whead) == NULL)
216 		doit = 1;
217 	else if (ll.ll_time != 0) {
218 		/* if last login is earlier than some current login */
219 		for (; !doit && w != NULL; w = w->next)
220 			if (w->info == LOGGEDIN && w->loginat < ll.ll_time)
221 				doit = 1;
222 		/*
223 		 * and if it's not any of the current logins
224 		 * can't use time comparison because there may be a small
225 		 * discrepency since login calls time() twice
226 		 */
227 		for (w = pn->whead; doit && w != NULL; w = w->next)
228 			if (w->info == LOGGEDIN &&
229 			    strncmp(w->tty, ll.ll_line, UT_LINESIZE) == 0)
230 				doit = 0;
231 	}
232 	if (doit) {
233 		w = walloc(pn);
234 		w->info = LASTLOG;
235 		bcopy(ll.ll_line, w->tty, UT_LINESIZE);
236 		w->tty[UT_LINESIZE] = 0;
237 		bcopy(ll.ll_host, w->host, UT_HOSTSIZE);
238 		w->host[UT_HOSTSIZE] = 0;
239 		w->loginat = ll.ll_time;
240 	}
241 }
242 
243 void
244 enter_where(struct utmp *ut, PERSON *pn)
245 {
246 	WHERE *w = walloc(pn);
247 
248 	w->info = LOGGEDIN;
249 	bcopy(ut->ut_line, w->tty, UT_LINESIZE);
250 	w->tty[UT_LINESIZE] = 0;
251 	bcopy(ut->ut_host, w->host, UT_HOSTSIZE);
252 	w->host[UT_HOSTSIZE] = 0;
253 	w->loginat = (time_t)ut->ut_time;
254 	find_idle_and_ttywrite(w);
255 }
256 
257 PERSON *
258 enter_person(struct passwd *pw)
259 {
260 	PERSON *pn, **pp;
261 
262 	for (pp = htab + hash(pw->pw_name);
263 	    *pp != NULL && strcmp((*pp)->name, pw->pw_name) != 0;
264 	    pp = &(*pp)->hlink)
265 		;
266 	if ((pn = *pp) == NULL) {
267 		pn = palloc();
268 		entries++;
269 		if (phead == NULL)
270 			phead = ptail = pn;
271 		else {
272 			ptail->next = pn;
273 			ptail = pn;
274 		}
275 		pn->next = NULL;
276 		pn->hlink = NULL;
277 		*pp = pn;
278 		userinfo(pn, pw);
279 		pn->whead = NULL;
280 	}
281 	return (pn);
282 }
283 
284 PERSON *
285 find_person(char *name)
286 {
287 	PERSON *pn;
288 
289 	/* name may be only UT_NAMESIZE long and not terminated */
290 	for (pn = htab[hash(name)];
291 	    pn != NULL && strncmp(pn->name, name, UT_NAMESIZE) != 0;
292 	    pn = pn->hlink)
293 		;
294 	return (pn);
295 }
296 
297 int
298 hash(char *name)
299 {
300 	int h, i;
301 
302 	h = 0;
303 	/* name may be only UT_NAMESIZE long and not terminated */
304 	for (i = UT_NAMESIZE; --i >= 0 && *name;)
305 		h = ((h << 2 | h >> (HBITS - 2)) ^ *name++) & HMASK;
306 	return (h);
307 }
308 
309 PERSON *
310 palloc(void)
311 {
312 	PERSON *p;
313 
314 	if ((p = (PERSON *)malloc((u_int) sizeof(PERSON))) == NULL)
315 		err(1, "malloc");
316 	return (p);
317 }
318 
319 WHERE *
320 walloc(PERSON *pn)
321 {
322 	WHERE *w;
323 
324 	if ((w = (WHERE *)malloc((u_int) sizeof(WHERE))) == NULL)
325 		err(1, "malloc");
326 	if (pn->whead == NULL)
327 		pn->whead = pn->wtail = w;
328 	else {
329 		pn->wtail->next = w;
330 		pn->wtail = w;
331 	}
332 	w->next = NULL;
333 	return (w);
334 }
335 
336 char *
337 prphone(char *num)
338 {
339 	char *p;
340 	int len;
341 	static char pbuf[15];
342 
343 	/* don't touch anything if the user has their own formatting */
344 	for (p = num; *p; ++p)
345 		if (!isdigit((unsigned char)*p))
346 			return (num);
347 	len = p - num;
348 	p = pbuf;
349 	switch (len) {
350 	case 11:			/* +0-123-456-7890 */
351 		*p++ = '+';
352 		*p++ = *num++;
353 		*p++ = '-';
354 		/* FALLTHROUGH */
355 	case 10:			/* 012-345-6789 */
356 		*p++ = *num++;
357 		*p++ = *num++;
358 		*p++ = *num++;
359 		*p++ = '-';
360 		/* FALLTHROUGH */
361 	case 7:				/* 012-3456 */
362 		*p++ = *num++;
363 		*p++ = *num++;
364 		*p++ = *num++;
365 		break;
366 	case 5:				/* x0-1234 */
367 	case 4:				/* x1234 */
368 		*p++ = 'x';
369 		*p++ = *num++;
370 		break;
371 	default:
372 		return (num);
373 	}
374 	if (len != 4) {
375 		*p++ = '-';
376 		*p++ = *num++;
377 	}
378 	*p++ = *num++;
379 	*p++ = *num++;
380 	*p++ = *num++;
381 	*p = '\0';
382 	return (pbuf);
383 }
384 
385 /* Like strvis(), but use malloc() to get the space and return a pointer
386  * to the beginning of the converted string, not the end.
387  *
388  * The caller is responsible for free()'ing the returned string.
389  */
390 char *
391 vs(struct storage **exist, char *src)
392 {
393 	char *dst;
394 	struct storage *n;
395 
396 	if ((n = malloc(sizeof(struct storage) + 4 * strlen(src))) == NULL)
397 		err(1, "malloc failed");
398 	n->next = *exist;
399 	*exist = n;
400 
401 	dst = n->a;
402 
403 	strvis(dst, src, VIS_SAFE|VIS_NOSLASH);
404 	return (dst);
405 }
406