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