1 /* qmail-users.c - qmail users/cdb lookup routines
2  * Copyright (C) 2010  Bruce Guenter <bruce@untroubled.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 #include <bglibs/sysdeps.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <pwd.h>
22 #include <string.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 
26 #include <bglibs/cdb.h>
27 #include <bglibs/cdb.h>
28 #include <bglibs/iobuf.h>
29 #include <bglibs/str.h>
30 
31 #include "qmail.h"
32 
33 static str users_path;
34 static int users_fd = -1;
35 static struct cdb users_cdb;
36 static struct stat users_stat;
37 
qmail_users_reinit(void)38 int qmail_users_reinit(void)
39 {
40   struct stat s;
41   /* If we can see the users CDB file... */
42   if (stat(users_path.s, &s) != -1) {
43     /* If it was either not open or has changed since the last open... */
44     if (users_fd == -1 ||
45 	s.st_ino != users_stat.st_ino ||
46 	s.st_mtime != users_stat.st_mtime ||
47 	s.st_size != users_stat.st_size) {
48       /* If it was previously open, close it */
49       if (users_fd != -1) {
50 	close(users_fd);
51 	cdb_free(&users_cdb);
52       }
53       /* And re-open it */
54       if ((users_fd = open(users_path.s, O_RDONLY)) != -1) {
55 	fstat(users_fd, &users_stat);
56 	cdb_init(&users_cdb, users_fd);
57       }
58     }
59   }
60   else if (users_fd != -1) {
61     close(users_fd);
62     cdb_free(&users_cdb);
63     users_fd = -1;
64   }
65   return 0;
66 }
67 
qmail_users_init(void)68 int qmail_users_init(void)
69 {
70   if (!str_copy2s(&users_path, qmail_root, "/users/cdb"))
71     return -1;
72   return qmail_users_reinit();
73 }
74 
lookup_userscdb(struct qmail_user * u,str * name,char dash)75 static int lookup_userscdb(struct qmail_user* u,
76 			   str* name, char dash)
77 {
78   char* ptr;
79   const char* end;
80   const char* user;
81   const char* home;
82   int i;
83 
84   if (!str_spliceb(name, 0, 0, "!", 1)
85       || (name->len > 1 && !str_catc(name, dash))) {
86     errno = ENOMEM;
87     return -1;
88   }
89 
90   if ((i = cdb_get(&users_cdb, name, name)) <= 0)
91     return i;
92 
93   /* name now contains:
94    * user NUL uid NUL gid NUL home NUL dash NUL ext
95    */
96   errno = EDOM;
97   ptr = name->s;
98   end = name->s + name->len;
99   user = ptr;
100   if ((ptr += strlen(ptr) + 1) >= end) return -1;
101   u->uid = strtoul(ptr, &ptr, 10);
102   if (*ptr++ != 0 || ptr >= end) return -1;
103   u->gid = strtoul(ptr, &ptr, 10);
104   if (*ptr++ != 0 || ptr >= end) return -1;
105   home = ptr;
106   if ((ptr += strlen(ptr) + 1) >= end) return -1;
107   if ((u->dash = *ptr) != 0) ++ptr;
108   if (*ptr++ != 0 || ptr > end) return -1;
109 
110   if (!str_copys(&u->user, user)
111       || !str_copys(&u->homedir, home)
112       || !str_copyb(&u->ext, ptr, end-ptr)) {
113     errno = ENOMEM;
114     return -1;
115   }
116 
117   return 1;
118 }
119 
lookup_passwd(struct qmail_user * u,const str * namestr,char dash)120 static int lookup_passwd(struct qmail_user* u, const str* namestr, char dash)
121 {
122   const struct passwd* pw;
123   const char* name;
124 
125   if (*(name = namestr->s) == 0)
126     name = "alias";
127   if ((pw = getpwnam(name)) == 0)
128     return (errno == ETXTBSY) ? -1 : 0;
129 
130   if (!str_copys(&u->user, pw->pw_name)
131       || !str_copys(&u->homedir, pw->pw_dir)
132       || !str_copys(&u->ext, "")) {
133     errno = ENOMEM;
134     return -1;
135   }
136   u->uid = pw->pw_uid;
137   u->gid = pw->pw_gid;
138   u->dash = dash;
139   return 1;
140 }
141 
qmail_users_lookup(struct qmail_user * u,const char * name,char dash)142 int qmail_users_lookup(struct qmail_user* u, const char* name, char dash)
143 {
144   static str lname;
145   if (!str_copys(&lname, name)){
146     errno = ENOMEM;
147     return -1;
148   }
149   str_lower(&lname);
150   if (users_fd != -1) {
151     switch (lookup_userscdb(u, &lname, dash)) {
152     case -1: return -1;
153     case 0: break;
154     default: return 1;
155     }
156     if (!str_copys(&lname, name)){
157       errno = ENOMEM;
158       return -1;
159     }
160   }
161   return lookup_passwd(u, &lname, dash);
162 }
163 
qmail_users_lookup_split(struct qmail_user * u,const char * name,str * local,str * ext)164 int qmail_users_lookup_split(struct qmail_user* u, const char* name,
165 			     str* local, str* ext)
166 {
167   static str account;
168   int i;
169 
170   /* Check if the name is a base UNIX user. */
171   if (!str_copys(local, name)) return -1;
172   if (!str_copys(ext, "")) return -1;
173   switch (qmail_users_lookup(u, name, 0)) {
174   case -1: return -1;
175   case 0:  break;
176   default: return 1;
177   }
178 
179   /* Now, look for increasingly shorter base-ext pairs */
180   if (!str_copy(&account, local)) return -1;
181   i = account.len;
182   while (i > 0 && (i = str_findprev(&account, '-', i-1)) != -1) {
183     if (!str_copyb(local, account.s, i)) return -1;
184     if (!str_copyb(ext, account.s+i+1, account.len-i-1)) return -1;
185     switch (qmail_users_lookup(u, local->s, '-')) {
186     case -1: return -1;
187     case 0: continue;
188     default: return 1;
189     }
190   }
191 
192   switch (qmail_users_lookup(u, "", '-')) {
193   case -1: return -1;
194   case 0: return 0;
195   }
196   str_copyb(local, "", 0);
197   if (!str_copy(ext, &account)) return -1;
198   return 1;
199 }
200