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