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