Merge from vendor branch HEIMDAL:
[dragonfly.git] / crypto / heimdal-0.6.3 / appl / popper / pop_init.c
1 /*
2  * Copyright (c) 1989 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6
7 #include <popper.h>
8 RCSID("$Id: pop_init.c,v 1.58.12.1 2003/10/13 12:01:35 lha Exp $");
9
10
11 #if defined(KRB4) || defined(KRB5)
12
13 static int
14 pop_net_read(POP *p, int fd, void *buf, size_t len)
15 {
16 #ifdef KRB5
17     return krb5_net_read(p->context, &fd, buf, len);
18 #elif defined(KRB4)
19     return krb_net_read(fd, buf, len);
20 #endif
21 }
22 #endif
23
24 static char *addr_log;
25
26 static void
27 pop_write_addr(POP *p, struct sockaddr *addr)
28 {
29     char ts[32];
30     char as[128];
31     time_t t;
32     FILE *f;
33     if(addr_log == NULL)
34         return;
35     t = time(NULL);
36     strftime(ts, sizeof(ts), "%Y%m%d%H%M%S", localtime(&t));
37     if(inet_ntop (addr->sa_family, socket_get_address(addr), 
38                   as, sizeof(as)) == NULL) {
39         pop_log(p, POP_PRIORITY, "failed to print address");
40         return;
41     }
42     
43     f = fopen(addr_log, "a");
44     if(f == NULL) {
45         pop_log(p, POP_PRIORITY, "failed to open address log (%s)", addr_log);
46         return;
47     }
48     fprintf(f, "%s %s\n", as, ts);
49     fclose(f);
50 }
51
52 #ifdef KRB4
53 static int
54 krb4_authenticate (POP *p, int s, u_char *buf, struct sockaddr *addr)
55 {
56     Key_schedule schedule;
57     KTEXT_ST ticket;
58     char instance[INST_SZ];  
59     char version[9];
60     int auth;
61   
62     if (memcmp (buf, KRB_SENDAUTH_VERS, 4) != 0)
63         return -1;
64     if (pop_net_read (p, s, buf + 4,
65                       KRB_SENDAUTH_VLEN - 4) != KRB_SENDAUTH_VLEN - 4)
66         return -1;
67     if (memcmp (buf, KRB_SENDAUTH_VERS, KRB_SENDAUTH_VLEN) != 0)
68         return -1;
69
70     k_getsockinst (0, instance, sizeof(instance));
71     auth = krb_recvauth(KOPT_IGNORE_PROTOCOL,
72                         s,
73                         &ticket,
74                         "pop",
75                         instance,
76                         (struct sockaddr_in *)addr,
77                         (struct sockaddr_in *) NULL,
78                         &p->kdata,
79                         "",
80                         schedule,
81                         version);
82     
83     if (auth != KSUCCESS) {
84         pop_msg(p, POP_FAILURE, "Kerberos authentication failure: %s", 
85                 krb_get_err_text(auth));
86         pop_log(p, POP_PRIORITY, "%s: (%s.%s@%s) %s", p->client, 
87                 p->kdata.pname, p->kdata.pinst, p->kdata.prealm,
88                 krb_get_err_text(auth));
89         return -1;
90     }
91
92 #ifdef DEBUG
93     pop_log(p, POP_DEBUG, "%s.%s@%s (%s): ok", p->kdata.pname, 
94             p->kdata.pinst, p->kdata.prealm, p->ipaddr);
95 #endif /* DEBUG */
96     return 0;
97 }
98 #endif /* KRB4 */
99
100 #ifdef KRB5
101 static int
102 krb5_authenticate (POP *p, int s, u_char *buf, struct sockaddr *addr)
103 {
104     krb5_error_code ret;
105     krb5_auth_context auth_context = NULL;
106     u_int32_t len;
107     krb5_ticket *ticket;
108     char *server;
109
110     if (memcmp (buf, "\x00\x00\x00\x13", 4) != 0)
111         return -1;
112     len = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]);
113         
114     if (krb5_net_read(p->context, &s, buf, len) != len)
115         return -1;
116     if (len != sizeof(KRB5_SENDAUTH_VERSION)
117         || memcmp (buf, KRB5_SENDAUTH_VERSION, len) != 0)
118         return -1;
119
120     ret = krb5_recvauth (p->context,
121                          &auth_context,
122                          &s,
123                          "KPOPV1.0",
124                          NULL, /* let rd_req figure out what server to use */
125                          KRB5_RECVAUTH_IGNORE_VERSION,
126                          NULL,
127                          &ticket);
128     if (ret) {
129         pop_log(p, POP_PRIORITY, "krb5_recvauth: %s",
130                 krb5_get_err_text(p->context, ret));
131         return -1;
132     }
133
134
135     ret = krb5_unparse_name(p->context, ticket->server, &server);
136     if(ret) {
137         pop_log(p, POP_PRIORITY, "krb5_unparse_name: %s", 
138                 krb5_get_err_text(p->context, ret));
139         ret = -1;
140         goto out;
141     }
142     /* does this make sense? */
143     if(strncmp(server, "pop/", 4) != 0) {
144         pop_log(p, POP_PRIORITY,
145                 "Got ticket for service `%s'", server);
146         ret = -1;
147         goto out;
148     } else if(p->debug)
149         pop_log(p, POP_DEBUG, 
150                 "Accepted ticket for service `%s'", server);
151     free(server);
152  out:
153     krb5_auth_con_free (p->context, auth_context);
154     krb5_copy_principal (p->context, ticket->client, &p->principal);
155     krb5_free_ticket (p->context, ticket);
156
157     return ret;
158 }
159 #endif
160
161 static int
162 krb_authenticate(POP *p, struct sockaddr *addr)
163 {
164 #if defined(KRB4) || defined(KRB5)
165     u_char buf[BUFSIZ];
166
167     if (pop_net_read (p, 0, buf, 4) != 4) {
168         pop_msg(p, POP_FAILURE, "Reading four bytes: %s",
169                 strerror(errno));
170         exit (1);
171     }
172 #ifdef KRB4
173     if (krb4_authenticate (p, 0, buf, addr) == 0){
174         pop_write_addr(p, addr);
175         p->version = 4;
176         return POP_SUCCESS;
177     }
178 #endif
179 #ifdef KRB5
180     if (krb5_authenticate (p, 0, buf, addr) == 0){
181         pop_write_addr(p, addr);
182         p->version = 5;
183         return POP_SUCCESS;
184     }
185 #endif
186     exit (1);
187         
188 #endif /* defined(KRB4) || defined(KRB5) */
189
190     return(POP_SUCCESS);
191 }
192
193 static int
194 plain_authenticate (POP *p, struct sockaddr *addr)
195 {
196     return(POP_SUCCESS);
197 }
198
199 static int kerberos_flag;
200 static char *auth_str;
201 static int debug_flag;
202 static int interactive_flag;
203 static char *port_str;
204 static char *trace_file;
205 static int timeout;
206 static int help_flag;
207 static int version_flag;
208
209 static struct getargs args[] = {
210 #if defined(KRB4) || defined(KRB5)
211     { "kerberos", 'k', arg_flag, &kerberos_flag, "use kerberos" },
212 #endif
213     { "auth-mode", 'a', arg_string, &auth_str, "required authentication" },
214     { "debug", 'd', arg_flag, &debug_flag },
215     { "interactive", 'i', arg_flag, &interactive_flag, "create new socket" },
216     { "port", 'p', arg_string, &port_str, "port to listen to", "port" },
217     { "trace-file", 't', arg_string, &trace_file, "trace all command to file", "file" },
218     { "timeout", 'T', arg_integer, &timeout, "timeout", "seconds" },
219     { "address-log", 0, arg_string, &addr_log, "enable address log", "file" },
220     { "help", 'h', arg_flag, &help_flag },
221     { "version", 'v', arg_flag, &version_flag }
222 };
223
224 static int num_args = sizeof(args) / sizeof(args[0]);
225
226 /* 
227  *  init:   Start a Post Office Protocol session
228  */
229
230 static int
231 pop_getportbyname(POP *p, const char *service, 
232                   const char *proto, short def)
233 {
234 #ifdef KRB5
235     return krb5_getportbyname(p->context, service, proto, def);
236 #elif defined(KRB4)
237     return k_getportbyname(service, proto, htons(def));
238 #else
239     return htons(default);
240 #endif
241 }
242
243 int
244 pop_init(POP *p,int argcount,char **argmessage)
245 {
246     struct sockaddr_storage cs_ss;
247     struct sockaddr *cs = (struct sockaddr *)&cs_ss;
248     socklen_t               len;
249     char                *   trace_file_name = "/tmp/popper-trace";
250     int                     portnum = 0;
251     int                     optind = 0;
252     int                     error;
253
254     /*  Initialize the POP parameter block */
255     memset (p, 0, sizeof(POP));
256
257     setprogname(argmessage[0]);
258
259     /*  Save my name in a global variable */
260     p->myname = (char*)getprogname();
261
262     /*  Get the name of our host */
263     gethostname(p->myhost,MaxHostNameLen);
264
265 #ifdef KRB5
266     {
267         krb5_error_code ret;
268
269         ret = krb5_init_context (&p->context);
270         if (ret)
271             errx (1, "krb5_init_context failed: %d", ret);
272
273         krb5_openlog(p->context, p->myname, &p->logf);
274         krb5_set_warn_dest(p->context, p->logf);
275     }
276 #else
277     /*  Open the log file */
278     roken_openlog(p->myname,POP_LOGOPTS,POP_FACILITY);
279 #endif
280
281     p->auth_level = AUTH_NONE;
282
283     if(getarg(args, num_args, argcount, argmessage, &optind)){
284         arg_printusage(args, num_args, NULL, "");
285         exit(1);
286     }
287     if(help_flag){
288         arg_printusage(args, num_args, NULL, "");
289         exit(0);
290     }
291     if(version_flag){
292         print_version(NULL);
293         exit(0);
294     }
295
296     argcount -= optind;
297     argmessage += optind;
298
299     if (argcount != 0) {
300         arg_printusage(args, num_args, NULL, "");
301         exit(1);
302     }
303
304     if(auth_str){
305         if (strcmp (auth_str, "none") == 0)
306             p->auth_level = AUTH_NONE;
307         else if(strcmp(auth_str, "otp") == 0)
308             p->auth_level = AUTH_OTP;
309         else
310             warnx ("bad value for -a: %s", optarg);
311     }
312     /*  Debugging requested */
313     p->debug = debug_flag;
314
315     if(port_str)
316         portnum = htons(atoi(port_str));
317     if(trace_file){
318         p->debug++;
319         if ((p->trace = fopen(trace_file, "a+")) == NULL) {
320             pop_log(p, POP_PRIORITY,
321                     "Unable to open trace file \"%s\", err = %d",
322                     optarg,errno);
323             exit (1);
324         }
325         trace_file_name = trace_file;
326     }
327
328 #if defined(KRB4) || defined(KRB5)
329     p->kerberosp = kerberos_flag;
330 #endif
331
332     if(timeout)
333         pop_timeout = timeout;
334     
335     /* Fake inetd */
336     if (interactive_flag) {
337         if (portnum == 0)
338             portnum = p->kerberosp ?
339                 pop_getportbyname(p, "kpop", "tcp", 1109) :
340             pop_getportbyname(p, "pop", "tcp", 110);
341         mini_inetd (portnum);
342     }
343
344     /*  Get the address and socket of the client to whom I am speaking */
345     len = sizeof(cs_ss);
346     if (getpeername(STDIN_FILENO, cs, &len) < 0) {
347         pop_log(p,POP_PRIORITY,
348             "Unable to obtain socket and address of client, err = %d",errno);
349         exit (1);
350     }
351
352     /*  Save the dotted decimal form of the client's IP address 
353         in the POP parameter block */
354     inet_ntop (cs->sa_family, socket_get_address (cs),
355                p->ipaddr, sizeof(p->ipaddr));
356
357     /*  Save the client's port */
358     p->ipport = ntohs(socket_get_port (cs));
359
360     /*  Get the canonical name of the host to whom I am speaking */
361     error = getnameinfo_verified (cs, len, p->client, sizeof(p->client),
362                                   NULL, 0, 0);
363     if (error) {
364         pop_log (p, POP_PRIORITY,
365                  "getnameinfo: %s", gai_strerror (error));
366         strlcpy (p->client, p->ipaddr, sizeof(p->client));
367     }
368
369     /*  Create input file stream for TCP/IP communication */
370     if ((p->input = fdopen(STDIN_FILENO,"r")) == NULL){
371         pop_log(p,POP_PRIORITY,
372             "Unable to open communication stream for input, err = %d",errno);
373         exit (1);
374     }
375
376     /*  Create output file stream for TCP/IP communication */
377     if ((p->output = fdopen(STDOUT_FILENO,"w")) == NULL){
378         pop_log(p,POP_PRIORITY,
379             "Unable to open communication stream for output, err = %d",errno);
380         exit (1);
381     }
382
383     pop_log(p,POP_PRIORITY,
384         "(v%s) Servicing request from \"%s\" at %s\n",
385             VERSION,p->client,p->ipaddr);
386
387 #ifdef DEBUG
388     if (p->trace)
389         pop_log(p,POP_PRIORITY,
390             "Tracing session and debugging information in file \"%s\"",
391                 trace_file_name);
392     else if (p->debug)
393         pop_log(p,POP_PRIORITY,"Debugging turned on");
394 #endif /* DEBUG */
395
396
397     if(p->kerberosp)
398         return krb_authenticate(p, cs);
399     else
400         return plain_authenticate(p, cs);
401 }