1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 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 #if 0 36 #ifndef lint 37 static char sccsid[] = "@(#)util.c 8.3 (Berkeley) 4/28/95"; 38 #endif 39 #endif 40 41 #include <sys/cdefs.h> 42 __FBSDID("$FreeBSD$"); 43 44 #include <sys/param.h> 45 #include <sys/socket.h> 46 #include <sys/stat.h> 47 #include <ctype.h> 48 #include <db.h> 49 #include <err.h> 50 #include <errno.h> 51 #include <fcntl.h> 52 #include <paths.h> 53 #include <pwd.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 #include <utmpx.h> 59 #include "finger.h" 60 #include "pathnames.h" 61 62 static void find_idle_and_ttywrite(WHERE *); 63 static void userinfo(PERSON *, struct passwd *); 64 static WHERE *walloc(PERSON *); 65 66 int 67 match(struct passwd *pw, const char *user) 68 { 69 char *p, *t; 70 char name[1024]; 71 72 if (!strcasecmp(pw->pw_name, user)) 73 return(1); 74 75 /* 76 * XXX 77 * Why do we skip asterisks!?!? 78 */ 79 (void)strncpy(p = tbuf, pw->pw_gecos, sizeof(tbuf)); 80 tbuf[sizeof(tbuf) - 1] = '\0'; 81 if (*p == '*') 82 ++p; 83 84 /* Ampersands get replaced by the login name. */ 85 if ((p = strtok(p, ",")) == NULL) 86 return(0); 87 88 for (t = name; t < &name[sizeof(name) - 1] && (*t = *p) != '\0'; ++p) { 89 if (*t == '&') { 90 (void)strncpy(t, pw->pw_name, 91 sizeof(name) - (t - name)); 92 name[sizeof(name) - 1] = '\0'; 93 while (t < &name[sizeof(name) - 1] && *++t) 94 continue; 95 } else { 96 ++t; 97 } 98 } 99 *t = '\0'; 100 for (t = name; (p = strtok(t, "\t ")) != NULL; t = NULL) 101 if (!strcasecmp(p, user)) 102 return(1); 103 return(0); 104 } 105 106 void 107 enter_lastlog(PERSON *pn) 108 { 109 WHERE *w; 110 struct utmpx *ut = NULL; 111 char doit = 0; 112 113 if (setutxdb(UTXDB_LASTLOGIN, NULL) == 0) 114 ut = getutxuser(pn->name); 115 if ((w = pn->whead) == NULL) 116 doit = 1; 117 else if (ut != NULL && ut->ut_type == USER_PROCESS) { 118 /* if last login is earlier than some current login */ 119 for (; !doit && w != NULL; w = w->next) 120 if (w->info == LOGGEDIN && 121 w->loginat < ut->ut_tv.tv_sec) 122 doit = 1; 123 /* 124 * and if it's not any of the current logins 125 * can't use time comparison because there may be a small 126 * discrepancy since login calls time() twice 127 */ 128 for (w = pn->whead; doit && w != NULL; w = w->next) 129 if (w->info == LOGGEDIN && 130 strcmp(w->tty, ut->ut_line) == 0) 131 doit = 0; 132 } 133 if (ut != NULL && doit) { 134 w = walloc(pn); 135 w->info = LASTLOG; 136 strcpy(w->tty, ut->ut_line); 137 strcpy(w->host, ut->ut_host); 138 w->loginat = ut->ut_tv.tv_sec; 139 } 140 endutxent(); 141 } 142 143 void 144 enter_where(struct utmpx *ut, PERSON *pn) 145 { 146 WHERE *w; 147 148 w = walloc(pn); 149 w->info = LOGGEDIN; 150 strcpy(w->tty, ut->ut_line); 151 strcpy(w->host, ut->ut_host); 152 w->loginat = ut->ut_tv.tv_sec; 153 find_idle_and_ttywrite(w); 154 } 155 156 PERSON * 157 enter_person(struct passwd *pw) 158 { 159 DBT data, key; 160 PERSON *pn; 161 162 if (db == NULL && 163 (db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL)) == NULL) 164 err(1, NULL); 165 166 key.data = pw->pw_name; 167 key.size = strlen(pw->pw_name); 168 169 switch ((*db->get)(db, &key, &data, 0)) { 170 case 0: 171 memmove(&pn, data.data, sizeof pn); 172 return (pn); 173 default: 174 case -1: 175 err(1, "db get"); 176 /* NOTREACHED */ 177 case 1: 178 ++entries; 179 pn = palloc(); 180 userinfo(pn, pw); 181 pn->whead = NULL; 182 183 data.size = sizeof(PERSON *); 184 data.data = &pn; 185 if ((*db->put)(db, &key, &data, 0)) 186 err(1, "db put"); 187 return (pn); 188 } 189 } 190 191 PERSON * 192 find_person(char *name) 193 { 194 struct passwd *pw; 195 196 DBT data, key; 197 PERSON *p; 198 199 if (!db) 200 return(NULL); 201 202 if ((pw = getpwnam(name)) && hide(pw)) 203 return(NULL); 204 205 key.data = name; 206 key.size = strlen(name); 207 208 if ((*db->get)(db, &key, &data, 0)) 209 return (NULL); 210 memmove(&p, data.data, sizeof p); 211 return (p); 212 } 213 214 PERSON * 215 palloc(void) 216 { 217 PERSON *p; 218 219 if ((p = malloc(sizeof(PERSON))) == NULL) 220 err(1, NULL); 221 return(p); 222 } 223 224 static WHERE * 225 walloc(PERSON *pn) 226 { 227 WHERE *w; 228 229 if ((w = malloc(sizeof(WHERE))) == NULL) 230 err(1, NULL); 231 if (pn->whead == NULL) 232 pn->whead = pn->wtail = w; 233 else { 234 pn->wtail->next = w; 235 pn->wtail = w; 236 } 237 w->next = NULL; 238 return(w); 239 } 240 241 char * 242 prphone(char *num) 243 { 244 char *p; 245 int len; 246 static char pbuf[20]; 247 248 /* don't touch anything if the user has their own formatting */ 249 for (p = num; *p; ++p) 250 if (!isdigit(*p)) 251 return(num); 252 len = p - num; 253 p = pbuf; 254 switch(len) { 255 case 11: /* +0-123-456-7890 */ 256 *p++ = '+'; 257 *p++ = *num++; 258 *p++ = '-'; 259 /* FALLTHROUGH */ 260 case 10: /* 012-345-6789 */ 261 *p++ = *num++; 262 *p++ = *num++; 263 *p++ = *num++; 264 *p++ = '-'; 265 /* FALLTHROUGH */ 266 case 7: /* 012-3456 */ 267 *p++ = *num++; 268 *p++ = *num++; 269 *p++ = *num++; 270 break; 271 case 5: /* x0-1234 */ 272 case 4: /* x1234 */ 273 *p++ = 'x'; 274 *p++ = *num++; 275 break; 276 default: 277 return(num); 278 } 279 if (len != 4) { 280 *p++ = '-'; 281 *p++ = *num++; 282 } 283 *p++ = *num++; 284 *p++ = *num++; 285 *p++ = *num++; 286 *p = '\0'; 287 return(pbuf); 288 } 289 290 static void 291 find_idle_and_ttywrite(WHERE *w) 292 { 293 struct stat sb; 294 time_t touched; 295 int error; 296 297 (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_DEV, w->tty); 298 299 error = stat(tbuf, &sb); 300 if (error < 0 && errno == ENOENT) { 301 /* 302 * The terminal listed is not actually a terminal (i.e., 303 * ":0"). This is a failure, so we'll skip printing 304 * out the idle time, which is non-ideal but better 305 * than a bogus warning and idle time. 306 */ 307 w->idletime = -1; 308 return; 309 } else if (error < 0) { 310 warn("%s", tbuf); 311 w->idletime = -1; 312 return; 313 } 314 touched = sb.st_atime; 315 if (touched < w->loginat) { 316 /* tty untouched since before login */ 317 touched = w->loginat; 318 } 319 w->idletime = now < touched ? 0 : now - touched; 320 321 #define TALKABLE 0220 /* tty is writable if 220 mode */ 322 w->writable = ((sb.st_mode & TALKABLE) == TALKABLE); 323 } 324 325 static void 326 userinfo(PERSON *pn, struct passwd *pw) 327 { 328 char *p, *t; 329 char *bp, name[1024]; 330 struct stat sb; 331 332 pn->realname = pn->office = pn->officephone = pn->homephone = NULL; 333 334 pn->uid = pw->pw_uid; 335 if ((pn->name = strdup(pw->pw_name)) == NULL) 336 err(1, "strdup failed"); 337 if ((pn->dir = strdup(pw->pw_dir)) == NULL) 338 err(1, "strdup failed"); 339 if ((pn->shell = strdup(pw->pw_shell)) == NULL) 340 err(1, "strdup failed"); 341 342 /* why do we skip asterisks!?!? */ 343 (void)strncpy(bp = tbuf, pw->pw_gecos, sizeof(tbuf)); 344 tbuf[sizeof(tbuf) - 1] = '\0'; 345 if (*bp == '*') 346 ++bp; 347 348 /* ampersands get replaced by the login name */ 349 if (!(p = strsep(&bp, ","))) 350 return; 351 for (t = name; t < &name[sizeof(name) - 1] && (*t = *p) != '\0'; ++p) { 352 if (*t == '&') { 353 (void)strncpy(t, pw->pw_name, 354 sizeof(name) - (t - name)); 355 name[sizeof(name) - 1] = '\0'; 356 if (islower(*t)) 357 *t = toupper(*t); 358 while (t < &name[sizeof(name) - 1] && *++t) 359 continue; 360 } else { 361 ++t; 362 } 363 } 364 *t = '\0'; 365 if ((pn->realname = strdup(name)) == NULL) 366 err(1, "strdup failed"); 367 pn->office = ((p = strsep(&bp, ",")) && *p) ? 368 strdup(p) : NULL; 369 pn->officephone = ((p = strsep(&bp, ",")) && *p) ? 370 strdup(p) : NULL; 371 pn->homephone = ((p = strsep(&bp, ",")) && *p) ? 372 strdup(p) : NULL; 373 (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pw->pw_name); 374 pn->mailrecv = -1; /* -1 == not_valid */ 375 if (stat(tbuf, &sb) < 0) { 376 if (errno != ENOENT) { 377 warn("%s", tbuf); 378 return; 379 } 380 } else if (sb.st_size != 0) { 381 pn->mailrecv = sb.st_mtime; 382 pn->mailread = sb.st_atime; 383 } 384 } 385 386 /* 387 * Is this user hiding from finger? 388 * If ~<user>/.nofinger exists, return 1 (hide), else return 0 (nohide). 389 * Nobody can hide from root. 390 */ 391 392 int 393 hide(struct passwd *pw) 394 { 395 struct stat st; 396 char buf[MAXPATHLEN]; 397 398 if (invoker_root || !pw->pw_dir) 399 return 0; 400 401 snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, _PATH_NOFINGER); 402 403 if (stat(buf, &st) == 0) 404 return 1; 405 406 return 0; 407 } 408