/* PIPServer - A deamon for finger protocol v2 * * Copyright (C) 1999 Michael Baumer * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* $Id: statusdb.c,v 1.20 2001/07/04 14:37:31 baumi Exp $ * * A database for the status of users * * Attention: The db file itself does not shrink * We only reuse old space. This has the advantage that readers may still * read while we update some entries * * One could think about a "cleaner" function that frees the no longer used * entries */ #include #include #include #define _XOPEN_SOURCE #include #include #include "finger.h" #include "local_user.h" #include "statusdb.h" #include "in.fingerd.h" #include "flock.h" #include "log.h" #ifndef SEEK_SET #define SEEK_SET 0 /* set the seek pointer */ #define SEEK_CUR 1 /* increment the seek pointer */ #define SEEK_END 2 /* extend the file size */ #endif /* global values * * used for changing an entry */ static int no = -1; db_entry _entry; /* free a list of db_entry */ void db_freelist(db_entry *Entry) { db_entry *prev; while (Entry) { prev = Entry; Entry = Entry->next; if (prev != &_entry) free(prev); } } login_info * read_utmp_db(int sorted) { login_info *current, *tmp; login_info first; finger_userinfo *finfo; int dbfd = 0; db_entry entry; int nread; char dbfile[strlen(config.dbpath) + 32]; sprintf(dbfile, "%s/statusdb", config.dbpath); if ((dbfd = open(dbfile, O_RDONLY))<0) { log(LOG_ERR, "could not open status DB file: %m"); return NULL; } /* request a lock */ if (flock (dbfd, LOCK_SH) < 0) { log(LOG_ERR, "Status DB: Lock set failed: %m"); return NULL; } current = &first; current->next = NULL; /* Iterate over the db entries */ while ((nread = read(dbfd, &entry, sizeof(entry))) == sizeof(entry)) { if (!entry.flag) continue; /* make login info */ current->next = (login_info *)calloc(1, sizeof(login_info)); current=current->next; current->login_time = entry.login_time; current->idle_time = time(0) - entry.idle_time; strncpy(current->login_user, entry.login_user, UT_NAMESIZE); current->login_user[UT_NAMESIZE - 1] = '\0'; strncpy(current->realname, entry.realname, REALNAME_LEN); current->realname[REALNAME_LEN - 1] = '\0'; strncpy(current->hostname, entry.hostname, HOSTNAME_LEN); current->hostname[HOSTNAME_LEN - 1] = '\0'; strncpy(current->status, entry.status, STATUS_LEN); current->status[STATUS_LEN - 1] = '\0'; strncpy(current->fromhost, entry.fromhost, HOSTNAME_LEN); current->status[HOSTNAME_LEN - 1] = '\0'; } /* release lock */ if (flock (dbfd, LOCK_UN) < 0) { log(LOG_ERR, "Status DB: Lock release failed: %m"); } close (dbfd); return(first.next); } /* write_entry * * Updates the users entry in the database * * 1. opens and requests an exclusive lock on the database file. * 2. writes the updates entry back to the disk * 3. closes the file and releases the lock * * Normally a user will connect to the finger deamon and "login". * Afterwards (s)he is able to set the status at any time in the connection */ int write_entry() { login_info *current, *tmp; login_info first; finger_userinfo *finfo; int dbfd = 0; int nread; char dbfile[strlen(config.dbpath) + 32]; sprintf(dbfile, "%s/statusdb", config.dbpath); if ((dbfd = open(dbfile, O_WRONLY))<0) { log(LOG_ERR, "could not open status DB file: %m"); return 0; } /* request a lock */ if (flock (dbfd, LOCK_EX) < 0) { log(LOG_ERR, "Status DB: Lock set failed: %m"); return 0; } /* We have now the file opened for writing, locked exclusively for us */ if (lseek(dbfd, no*sizeof(db_entry), SEEK_SET) < 0) { log(LOG_ERR, "Status DB: Seek failed: %m"); return 0; } /* write the record */ if ((nread = write(dbfd, &_entry, sizeof(db_entry))) != sizeof(db_entry)) { log(LOG_ERR, "Status DB: Write entry failed: %m"); return 0; } close(dbfd); return 1; } int db_login(char *username, char *passwd, char *hostname) { int dbfd; int nread; char dbfile[strlen(config.dbpath) + 32]; char *enc; sprintf(dbfile, "%s/statusdb", config.dbpath); if ((dbfd = open(dbfile, O_RDONLY))<0) { log(LOG_ERR, "could not open status DB file: %m"); return 0; } /* request a lock */ if (flock (dbfd, LOCK_SH) < 0) { log(LOG_ERR, "Status DB: Lock set failed: %m"); return 0; } /* Iterate over the db entries */ no = 0; while ((nread = read(dbfd, &_entry, sizeof(db_entry))) == sizeof(db_entry)) { if (!strncmp(_entry.login_user, username, UT_NAMESIZE)) { break; } no++; } /* release lock */ if (flock (dbfd, LOCK_UN) < 0) { log(LOG_ERR, "Status DB: Lock release failed: %m"); no = -1; close(dbfd); return 0; } close(dbfd); if (!nread) { /* no = -1; return 0; */ /* We simply add the user to the database, if the user exists on the system. This could be considered insecure and inappropriate. (i.e this is a TODO :-) */ return db_add_user(username, passwd, hostname); } enc = crypt(passwd, _entry.passwd); if (strncmp(enc, _entry.passwd, 13)) { no = -1; return -1; } _entry.flag = 1; strcpy(_entry.status, "Online"); _entry.login_time = time(NULL); _entry.idle_time = time(NULL); strncpy(_entry.hostname, hostname, HOSTNAME_LEN); _entry.hostname[HOSTNAME_LEN - 1] = '\0'; /* todo */ strncpy(_entry.fromhost, "", HOSTNAME_LEN); _entry.hostname[HOSTNAME_LEN - 1] = '\0'; return(write_entry()); } /* db_adduser * * Adds a user to the database file and performs a login * * Do not confuse this login with the system login! */ int db_add_user(char *username, char *passwd, char *hostname) { finger_userinfo *finfo; int dbfd; int nread; char dbfile[strlen(config.dbpath) + 32]; char *enc; finfo = pwd_search_login(username, 0); if (!finfo) { no = -1; return -1; } _entry.flag = 1; strcpy(_entry.login_user, finfo->pw_name); strcpy(_entry.realname, finfo->pw_realname); if (hostname) { strncpy(_entry.hostname, hostname, HOSTNAME_LEN); _entry.hostname[HOSTNAME_LEN - 1] = '\0'; } else strncpy(_entry.hostname, "(unknown)", HOSTNAME_LEN); /* todo */ strncpy(_entry.fromhost, "", HOSTNAME_LEN); _entry.fromhost[HOSTNAME_LEN - 1] = '\0'; strcpy(_entry.status, "Online"); strncpy(_entry.passwd, crypt(passwd, "AA"), 13); _entry.login_time = time(NULL); _entry.idle_time = time(NULL); sprintf(dbfile, "%s/statusdb", config.dbpath); if ((dbfd = open(dbfile, (O_WRONLY | O_APPEND)))<0) { log(LOG_ERR, "could not open status DB file: %m"); return 0; } /* request a lock */ if (flock (dbfd, LOCK_EX) < 0) { log(LOG_ERR, "Status DB: Lock set failed: %m"); return 0; } /* Add the entry to the file */ /* compute position for later acesses */ no = lseek(dbfd, 0, SEEK_CUR) / sizeof(db_entry); /* write the record */ if ((nread = write(dbfd, &_entry, sizeof(db_entry))) != sizeof(db_entry)) { log(LOG_ERR, "Status DB: Write entry failed: %m"); return 0; } /* release lock */ if (flock (dbfd, LOCK_UN) < 0) { log(LOG_ERR, "Status DB: Lock release failed: %m"); no = -1; close(dbfd); return 0; } close(dbfd); return 1; } /* db_logout * * If someone logged in, log him out */ int db_logout() { if (no >= 0) { setEntry_status("Offline"); _entry.flag = 0; return(write_entry()); } return 0; } /* db_insertlist * * Insert a list of users * Utility function for the site fingerd * * The last entry of list is not valid */ int db_insertlist(db_entry *list) { db_entry *first = list; db_entry *prev; db_entry *fileentry; int dbfd; int nread; char dbfile[strlen(config.dbpath) + 32]; finger_userinfo *finfo; /* Validity check */ if (!list) { return 0; } sprintf(dbfile, "%s/statusdb", config.dbpath); if ((dbfd = open(dbfile, (O_RDWR)))<0) { log(LOG_ERR, "could not open status DB file: %m"); return 0; } /* request a lock */ if (flock (dbfd, LOCK_EX) < 0) { log(LOG_ERR, "Status DB: Lock set failed: %m"); return 0; } fileentry = &_entry; fileentry->next = NULL; while ((nread = read(dbfd, fileentry, sizeof(db_entry))) == sizeof(db_entry)) { list = first; prev = NULL; while (list->next) { if (!strcmp(fileentry->login_user, list->login_user)) { break; } prev = list; list = list->next; } /* postcondition: (list->next != 0) means "login found in list" */ if (list->next) { /* Don't overwrite if user explicitely logged in */ if (fileentry->flag != 1) { /* NOTE: in the db it is a "idle since" */ fileentry->flag = 2; strcpy(fileentry->status, list->status); strcpy(fileentry->hostname, list->hostname); strcpy(fileentry->fromhost, list->fromhost); fileentry->login_time = list->login_time; fileentry->idle_time = time(NULL) - list->idle_time; } /* delete entry from list */ if (prev) { prev->next = list->next; /* necessary because we modify the original list */ free(list); } else { /* dont free the first entry! // free(first); */ first = first->next; } } else /* not online on the cluster */ if (fileentry->flag != 1) { /* not explicitely logged in */ fileentry->flag = 0; strcpy(fileentry->status, "Offline"); } fileentry->next = (db_entry *)malloc(sizeof(db_entry)); if (!fileentry->next) { db_freelist(_entry.next); return 0; } fileentry->next->next = NULL; fileentry = fileentry->next; } /* rewrite list to disk */ fileentry = &_entry; if (lseek(dbfd, 0, SEEK_SET) < 0) { log(LOG_ERR, "Status DB: Seek failed: %m"); db_freelist(_entry.next); return 0; } /* the last entry is not valid */ while(fileentry->next) { /* write the record */ if ((nread = write(dbfd, fileentry, sizeof(db_entry))) != sizeof(db_entry)) { log(LOG_ERR, "Status DB: Write entry failed: %m"); db_freelist(fileentry); return 0; } /* free this entry */ prev = fileentry; fileentry = fileentry->next; if (prev != &_entry) free(prev); } if ((fileentry) && (prev != &_entry) && (fileentry != &_entry)) free(fileentry); /* create users that are not in the database yet */ list = first; while (list->next) { /* Create the entry */ finfo = pwd_search_login(list->login_user, 0); if (finfo) { _entry.flag = 2; strcpy(_entry.login_user, finfo->pw_name); strcpy(_entry.realname, finfo->pw_realname); strncpy(_entry.hostname, list->hostname, HOSTNAME_LEN); _entry.hostname[HOSTNAME_LEN - 1] = '\0'; strncpy(_entry.fromhost, list->fromhost, HOSTNAME_LEN); _entry.fromhost[HOSTNAME_LEN - 1] = '\0'; strcpy(_entry.status, list->status); strcpy(_entry.passwd, ""); _entry.login_time = time(NULL); _entry.idle_time = time(NULL); /* write the record */ if ((nread = write(dbfd, &_entry, sizeof(db_entry))) != sizeof(db_entry)) { log(LOG_ERR, "Status DB: Write entry failed: %m"); return 0; } } list = list->next; } /* release lock */ if (flock (dbfd, LOCK_UN) < 0) { log(LOG_ERR, "Status DB: Lock release failed: %m"); no = -1; close(dbfd); return 0; } close(dbfd); return 1; }