This commit was manufactured by cvs2svn to create tag
[krb5.git] / src / appl / gssftp / ftp / secure.c
1 /*
2  * Shared routines for client and server for
3  * secure read(), write(), getc(), and putc().
4  * Only one security context, thus only work on one fd at a time!
5  */
6
7 #include <secure.h>     /* stuff which is specific to client or server */
8
9 #ifdef KRB5_KRB4_COMPAT
10 #include <krb.h>
11
12 CRED_DECL
13 extern KTEXT_ST ticket;
14 extern MSG_DAT msg_data;
15 extern Key_schedule schedule;
16 #endif /* KRB5_KRB4_COMPAT */
17 #ifdef GSSAPI
18 #include <gssapi/gssapi.h>
19 #include <gssapi/gssapi_generic.h>
20 extern gss_ctx_id_t gcontext;
21 #endif /* GSSAPI */
22
23 #include <arpa/ftp.h>
24
25 #include <stdio.h>
26 #include <string.h>
27 #ifdef HAVE_STDLIB_H
28 #include <stdlib.h>
29 #endif
30 #include <sys/types.h>
31 #include <netinet/in.h>
32 #include <errno.h>
33
34 #ifdef NEED_SYS_ERRLIST
35 extern char *sys_errlist[];
36 #endif
37
38 #if (SIZEOF_SHORT == 4)
39 typedef unsigned short ftp_uint32;
40 typedef short ftp_int32;
41 #elif (SIZEOF_INT == 4)
42 typedef unsigned int ftp_uint32;
43 typedef int ftp_int32;
44 #elif (SIZEOF_LONG == 4)
45 typedef unsigned long ftp_uint32;
46 typedef long ftp_int32;
47 #endif
48
49
50 extern struct   sockaddr_in hisaddr;
51 extern struct   sockaddr_in myaddr;
52 extern int      dlevel;
53 extern char     *auth_type;
54
55 /* Some libc's (GNU libc, at least) define MAX as a macro. Forget that. */
56 #ifdef MAX
57 #undef MAX
58 #endif
59
60 #define MAX maxbuf
61 extern unsigned int maxbuf;     /* maximum output buffer size */
62 extern unsigned char *ucbuf;    /* cleartext buffer */
63 static unsigned int nout, bufp; /* number of chars in ucbuf,
64                                  * pointer into ucbuf */
65
66 #ifdef KRB5_KRB4_COMPAT
67 #define FUDGE_FACTOR 32         /* Amount of growth
68                                  * from cleartext to ciphertext.
69                                  * krb_mk_priv adds this # bytes.
70                                  * Must be defined for each auth type.
71                                  */
72 #endif /* KRB5_KRB4_COMPAT */
73
74 #ifdef GSSAPI
75 #undef FUDGE_FACTOR
76 #define FUDGE_FACTOR 64 /*It appears to add 52 byts, but I'm not usre it is a constant--hartmans*/
77 #endif /*GSSAPI*/
78
79 #ifndef FUDGE_FACTOR            /* In case no auth types define it. */
80 #define FUDGE_FACTOR 0
81 #endif
82
83 #ifdef KRB5_KRB4_COMPAT
84 /* XXX - The following must be redefined if KERBEROS_V4 is not used
85  * but some other auth type is.  They must have the same properties. */
86 #define looping_write krb_net_write
87 #define looping_read krb_net_read
88 #endif
89
90 /* perhaps use these in general, certainly use them for GSSAPI */
91
92 #ifndef looping_write
93 static int
94 looping_write(fd, buf, len)
95     int fd;
96     register const char *buf;
97     int len;
98 {
99     int cc;
100     register int wrlen = len;
101     do {
102         cc = write(fd, buf, wrlen);
103         if (cc < 0) {
104             if (errno == EINTR)
105                 continue;
106             return(cc);
107         }
108         else {
109             buf += cc;
110             wrlen -= cc;
111         }
112     } while (wrlen > 0);
113     return(len);
114 }
115 #endif
116 #ifndef looping_read
117 static int
118 looping_read(fd, buf, len)
119     int fd;
120     register char *buf;
121     register int len;
122 {
123     int cc, len2 = 0;
124
125     do {
126         cc = read(fd, buf, len);
127         if (cc < 0) {
128             if (errno == EINTR)
129                 continue;
130             return(cc);          /* errno is already set */
131         }               
132         else if (cc == 0) {
133             return(len2);
134         } else {
135             buf += cc;
136             len2 += cc;
137             len -= cc;
138         }
139     } while (len > 0);
140     return(len2);
141 }
142 #endif
143
144
145 #if defined(STDARG) || (defined(__STDC__) && ! defined(VARARGS)) || defined(HAVE_STDARG_H)
146 extern secure_error(char *, ...);
147 #else
148 extern secure_error();
149 #endif
150
151 #define ERR     -2
152
153 static
154 secure_putbyte(fd, c)
155 int fd;
156 unsigned char c;
157 {
158         int ret;
159
160         ucbuf[nout++] = c;
161         if (nout == MAX - FUDGE_FACTOR) {
162           nout = 0;
163           ret = secure_putbuf(fd, ucbuf, MAX - FUDGE_FACTOR);
164           return(ret?ret:c);
165         }
166 return (c);
167 }
168
169 /* returns:
170  *       0  on success
171  *      -1  on error (errno set)
172  *      -2  on security error
173  */
174 secure_flush(fd)
175 int fd;
176 {
177         int ret;
178
179         if (dlevel == PROT_C)
180                 return(0);
181         if (nout)
182                 if (ret = secure_putbuf(fd, ucbuf, nout))
183                         return(ret);
184         return(secure_putbuf(fd, "", nout = 0));
185 }
186
187 /* returns:
188  *      c>=0  on success
189  *      -1    on error
190  *      -2    on security error
191  */
192 secure_putc(c, stream)
193 char c;
194 FILE *stream;
195 {
196         if (dlevel == PROT_C)
197                 return(putc(c,stream));
198         return(secure_putbyte(fileno(stream), (unsigned char) c));
199 }
200
201 /* returns:
202  *      nbyte on success
203  *      -1  on error (errno set)
204  *      -2  on security error
205  */
206 secure_write(fd, buf, nbyte)
207 int fd;
208 unsigned char *buf;
209 unsigned int nbyte;
210 {
211         unsigned int i;
212         int c;
213
214         if (dlevel == PROT_C)
215                 return(write(fd,buf,nbyte));
216         for (i=0; nbyte>0; nbyte--)
217                 if ((c = secure_putbyte(fd, buf[i++])) < 0)
218                         return(c);
219         return(i);
220 }
221
222 /* returns:
223  *       0  on success
224  *      -1  on error (errno set)
225  *      -2  on security error
226  */
227 secure_putbuf(fd, buf, nbyte)
228   int fd;
229 unsigned char *buf;
230 unsigned int nbyte;
231 {
232         static char *outbuf;            /* output ciphertext */
233         static unsigned int bufsize;    /* size of outbuf */
234         ftp_int32 length;
235         ftp_uint32 net_len;
236
237         /* Other auth types go here ... */
238 #ifdef KRB5_KRB4_COMPAT
239         if (bufsize < nbyte + FUDGE_FACTOR) {
240                 if (outbuf?
241                     (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))):
242                     (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) {
243                                         bufsize =nbyte + FUDGE_FACTOR;
244                 } else {
245                         bufsize = 0;
246                         secure_error("%s (in malloc of PROT buffer)",
247                                      sys_errlist[errno]);
248                         return(ERR);
249                 }
250         }
251
252         if (strcmp(auth_type, "KERBEROS_V4") == 0)
253           if ((length = dlevel == PROT_P ?
254             krb_mk_priv(buf, (unsigned char *) outbuf, nbyte, schedule,
255                         SESSION, &myaddr, &hisaddr)
256           : krb_mk_safe(buf, (unsigned char *) outbuf, nbyte, SESSION,
257                         &myaddr, &hisaddr)) == -1) {
258                 secure_error("krb_mk_%s failed for KERBEROS_V4",
259                                 dlevel == PROT_P ? "priv" : "safe");
260                 return(ERR);
261           }
262 #endif /* KRB5_KRB4_COMPAT */
263 #ifdef GSSAPI
264         if (strcmp(auth_type, "GSSAPI") == 0) {
265                 gss_buffer_desc in_buf, out_buf;
266                 OM_uint32 maj_stat, min_stat;
267                 int conf_state;
268                 
269                 in_buf.value = buf;
270                 in_buf.length = nbyte;
271                 maj_stat = gss_seal(&min_stat, gcontext,
272                                     (dlevel == PROT_P), /* confidential */
273                                     GSS_C_QOP_DEFAULT,
274                                     &in_buf, &conf_state,
275                                     &out_buf);
276                 if (maj_stat != GSS_S_COMPLETE) {
277                         /* generally need to deal */
278                         /* ie. should loop, but for now just fail */
279                         secure_gss_error(maj_stat, min_stat,
280                                          dlevel == PROT_P?
281                                          "GSSAPI seal failed":
282                                          "GSSAPI sign failed");
283                         return(ERR);
284                 }
285
286                 if (bufsize < out_buf.length) {
287                         if (outbuf?
288                             (outbuf = realloc(outbuf, (unsigned) out_buf.length)):
289                             (outbuf = malloc((unsigned) out_buf.length))) {
290                                 bufsize = out_buf.length;
291                         } else {
292                                 bufsize = 0;
293                                 secure_error("%s (in malloc of PROT buffer)",
294                                              sys_errlist[errno]);
295                                 return(ERR);
296                         }
297                 }
298
299                 memcpy(outbuf, out_buf.value, length=out_buf.length);
300                 gss_release_buffer(&min_stat, &out_buf);
301         }
302 #endif /* GSSAPI */
303         net_len = htonl((u_long) length);
304         if (looping_write(fd, &net_len, 4) == -1) return(-1);
305         if (looping_write(fd, outbuf, length) != length) return(-1);
306         return(0);
307 }
308
309 static
310 secure_getbyte(fd)
311 int fd;
312 {
313         /* number of chars in ucbuf, pointer into ucbuf */
314         static unsigned int nin, bufp;
315         int kerror;
316         ftp_uint32 length;
317
318         if (nin == 0) {
319                 if ((kerror = looping_read(fd, &length, sizeof(length)))
320                                 != sizeof(length)) {
321                         secure_error("Couldn't read PROT buffer length: %d/%s",
322                                      kerror,
323                                      kerror == -1 ? sys_errlist[errno]
324                                      : "premature EOF");
325                         return(ERR);
326                 }
327                 if ((length = (u_long) ntohl(length)) > MAX) {
328                         secure_error("Length (%d) of PROT buffer > PBSZ=%u", 
329                                      length, MAX);
330                         return(ERR);
331                 }
332                 if ((kerror = looping_read(fd, ucbuf, length)) != length) {
333                         secure_error("Couldn't read %u byte PROT buffer: %s",
334                                         length, kerror == -1 ?
335                                         sys_errlist[errno] : "premature EOF");
336                         return(ERR);
337                 }
338                 /* Other auth types go here ... */
339 #ifdef KRB5_KRB4_COMPAT
340                 if (strcmp(auth_type, "KERBEROS_V4") == 0) {
341                   if (kerror = dlevel == PROT_P ?
342                     krb_rd_priv(ucbuf, length, schedule, SESSION,
343                                 &hisaddr, &myaddr, &msg_data)
344                   : krb_rd_safe(ucbuf, length, SESSION,
345                                 &hisaddr, &myaddr, &msg_data)) {
346                         secure_error("krb_rd_%s failed for KERBEROS_V4 (%s)",
347                                         dlevel == PROT_P ? "priv" : "safe",
348                                         krb_get_err_text(kerror));
349                         return(ERR);
350                   }
351                   memcpy(ucbuf, msg_data.app_data, msg_data.app_length);
352                   nin = bufp = msg_data.app_length;
353                 }
354 #endif /* KRB5_KRB4_COMPAT */
355 #ifdef GSSAPI
356                 if (strcmp(auth_type, "GSSAPI") == 0) {
357                   gss_buffer_desc xmit_buf, msg_buf;
358                   OM_uint32 maj_stat, min_stat;
359                   int conf_state;
360
361                   xmit_buf.value = ucbuf;
362                   xmit_buf.length = length;
363                   conf_state = (dlevel == PROT_P);
364                   /* decrypt/verify the message */
365                   maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
366                                         &msg_buf, &conf_state, NULL);
367                   if (maj_stat != GSS_S_COMPLETE) {
368                     secure_gss_error(maj_stat, min_stat, 
369                                      (dlevel == PROT_P)?
370                                      "failed unsealing ENC message":
371                                      "failed unsealing MIC message");
372                     return ERR;
373                   }
374
375                   memcpy(ucbuf, msg_buf.value, nin = bufp = msg_buf.length);
376                   gss_release_buffer(&min_stat, &msg_buf);
377               }
378 #endif /* GSSAPI */
379                 /* Other auth types go here ... */
380         }
381         if (nin == 0)
382                 return(EOF);
383         else    return(ucbuf[bufp - nin--]);
384 }
385
386 /* returns:
387  *      c>=0 on success
388  *      -1   on EOF
389  *      -2   on security error
390  */
391 secure_getc(stream)
392 FILE *stream;
393 {
394         if (dlevel == PROT_C)
395                 return(getc(stream));
396         return(secure_getbyte(fileno(stream)));
397 }
398
399 /* returns:
400  *      n>0 on success (n == # of bytes read)
401  *      0   on EOF
402  *      -1  on error (errno set), only for PROT_C
403  *      -2  on security error
404  */
405 secure_read(fd, buf, nbyte)
406 int fd;
407 char *buf;
408 int nbyte;
409 {
410         static int c;
411         int i;
412
413         if (dlevel == PROT_C)
414                 return(read(fd,buf,nbyte));
415         if (c == EOF)
416                 return(c = 0);
417         for (i=0; nbyte>0; nbyte--)
418                 switch (c = secure_getbyte(fd)) {
419                         case ERR: return(c);
420                         case EOF: if (!i) c = 0;
421                                   return(i);
422                         default:  buf[i++] = c;
423                 }
424         return(i);
425 }