2000-11-27 Tobias Ringstrom <tori@tellus.mine.nu>
[nfs-utils.git] / utils / mountd / auth.c
1 /*
2  * utils/mountd/auth.c
3  *
4  * Authentication procedures for mountd.
5  *
6  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7  */
8
9 #include "config.h"
10
11 #include <sys/stat.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <errno.h>
15 #include "misc.h"
16 #include "nfslib.h"
17 #include "exportfs.h"
18 #include "mountd.h"
19 #include "xmalloc.h"
20
21 enum auth_error
22 {
23   bad_path,
24   unknown_host,
25   no_entry,
26   not_exported,
27   illegal_port,
28   faked_hostent,
29   no_forward_dns,
30   success
31 };
32
33 static void             auth_fixpath(char *path);
34 static nfs_export*      auth_authenticate_internal
35   (char *what, struct sockaddr_in *caller, char *path,
36    struct hostent **hpp, enum auth_error *error);
37 static char     *export_file = NULL;
38
39 void
40 auth_init(char *exports)
41 {
42
43         export_file = exports;
44         auth_reload();
45         xtab_mount_write();
46 }
47
48 int
49 auth_reload()
50 {
51         struct stat             stb;
52         static time_t           last_modified = 0;
53
54         if (stat(_PATH_ETAB, &stb) < 0)
55                 xlog(L_FATAL, "couldn't stat %s", _PATH_ETAB);
56         if (stb.st_mtime == last_modified)
57                 return 0;
58         last_modified = stb.st_mtime;
59
60         export_freeall();
61         // export_read(export_file);
62         xtab_export_read();
63
64         return 1;
65 }
66
67 static nfs_export *
68 auth_authenticate_internal(char *what, struct sockaddr_in *caller,
69                            char *path, struct hostent **hpp,
70                            enum auth_error *error)
71 {
72         struct in_addr          addr = caller->sin_addr;
73         nfs_export              *exp;
74
75         if (path[0] != '/') {
76                 *error = bad_path;
77                 return NULL;
78         }
79         auth_fixpath(path);
80
81         /* First try it w/o doing a hostname lookup... */
82         *hpp = get_hostent((const char *)&addr, sizeof(addr), AF_INET);
83         exp = export_find(*hpp, path);
84
85         if (!exp) {
86             /* Ok, that didn't fly.  Try it with a reverse lookup. */
87             free (*hpp);
88             *hpp = gethostbyaddr((const char *)&addr, sizeof(addr),
89                                  AF_INET);
90             if (!(*hpp)) {
91                 *error = no_entry;
92                 *hpp = get_hostent((const char *)&addr, sizeof(addr), AF_INET);
93                 return NULL;
94             } else {
95                 /* must make sure the hostent is authorative. */
96                 char **sp;
97                 struct hostent *forward = NULL;
98                 char *tmpname;
99
100                 *hpp = hostent_dup (*hpp);
101                 tmpname = xstrdup((*hpp)->h_name);
102                 if (tmpname) {
103                         forward = gethostbyname(tmpname);
104                         free(tmpname);
105                 }
106                 if (forward) {
107                         /* now make sure the "addr" is in the list */
108                         for (sp = forward->h_addr_list ; *sp ; sp++) {
109                                 if (memcmp(*sp, &addr, forward->h_length)==0)
110                                         break;
111                         }
112                 
113                         if (!*sp) {
114                                 /* it was a FAKE */
115                                 *error = faked_hostent;
116                                 return NULL;
117                         }
118                         free (*hpp);
119                         *hpp = hostent_dup (forward);
120                 }
121                 else {
122                         /* never heard of it. misconfigured DNS? */
123                         *error = no_forward_dns;
124                         return NULL;
125                 }
126             }
127
128             if (!(exp = export_find(*hpp, path))) {
129                 *error = no_entry;
130                 return NULL;
131             }
132         }
133
134         if (!exp->m_mayexport) {
135                 *error = not_exported;
136                 return NULL;
137         }
138
139         if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) &&
140             (ntohs(caller->sin_port) <  IPPORT_RESERVED/2 ||
141              ntohs(caller->sin_port) >= IPPORT_RESERVED)) {
142                 *error = illegal_port;
143                 return NULL;
144         }
145
146         *error = success;
147
148         return exp;
149 }
150
151 nfs_export *
152 auth_authenticate(char *what, struct sockaddr_in *caller, char *path)
153 {
154         nfs_export      *exp = NULL;
155         char            epath[MAXPATHLEN+1];
156         char            *p = NULL;
157         struct hostent  *hp = NULL;
158         struct in_addr  addr = caller->sin_addr;
159         enum auth_error error;
160
161         if (path [0] != '/') {
162                 xlog(L_WARNING, "bad path in %s request from %s: \"%s\"",
163                      what, inet_ntoa(addr), path);
164                 return exp;
165         }
166
167         strncpy(epath, path, sizeof (epath) - 1);
168         epath[sizeof (epath) - 1] = '\0';
169
170         /* Try the longest matching exported pathname. */
171         while (1) {
172                 if (hp) {
173                         free (hp);
174                         hp = NULL;
175                 }
176                 exp = auth_authenticate_internal(what, caller, epath,
177                                                  &hp, &error);
178                 if (exp || (error != not_exported && error != no_entry))
179                         break;
180                 /* We have to treat the root, "/", specially. */
181                 if (p == &epath[1]) break;
182                 p = strrchr(epath, '/');
183                 if (p == epath) p++;
184                 *p = '\0';
185         }
186
187         switch (error) {
188         case bad_path:
189                 xlog(L_WARNING, "bad path in %s request from %s: \"%s\"",
190                      what, inet_ntoa(addr), path);
191                 break;
192
193         case unknown_host:
194                 xlog(L_WARNING, "%s request from unknown host %s for %s (%s)",
195                      what, inet_ntoa(addr), path, epath);
196                 break;
197
198         case no_entry:
199                 xlog(L_WARNING, "refused %s request from %s for %s (%s): no export entry",
200                      what, hp->h_name, path, epath);
201                 break;
202
203         case not_exported:
204                 xlog(L_WARNING, "refused %s request from %s for %s (%s): not exported",
205                      what, hp->h_name, path, epath);
206                 break;
207
208         case illegal_port:
209                 xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %d",
210                      what, hp->h_name, path, epath, ntohs(caller->sin_port));
211                 break;
212
213         case faked_hostent:
214                 xlog(L_WARNING, "refused %s request from %s (%s) for %s (%s): DNS forward lookup does't match with reverse",
215                      what, inet_ntoa(addr), hp->h_name, path, epath);
216                 break;
217
218         case no_forward_dns:
219                 xlog(L_WARNING, "refused %s request from %s (%s) for %s (%s): no DNS forward lookup",
220                      what, inet_ntoa(addr), hp->h_name, path, epath);
221                 break;
222
223         case success:
224                 xlog(L_NOTICE, "authenticated %s request from %s:%d for %s (%s)",
225                      what, hp->h_name, ntohs(caller->sin_port), path, epath);
226                 break;
227         default:
228                 xlog(L_NOTICE, "%s request from %s:%d for %s (%s) gave %d",
229                      what, hp->h_name, ntohs(caller->sin_port), path, epath, error);
230         }
231
232         if (hp)
233                 free (hp);
234
235         return exp;
236 }
237
238 static void
239 auth_fixpath(char *path)
240 {
241         char    *sp, *cp;
242
243         for (sp = cp = path; *sp; sp++) {
244                 if (*sp != '/' || sp[1] != '/')
245                         *cp++ = *sp;
246         }
247         while (cp > path+1 && cp[-1] == '/')
248                 cp--;
249         *cp = '\0';
250 }