b8d4fcfd0dbd9fbc864367569c7207db86726547
[trust_router.git] / gsscon / gsscon_common.c
1 /*
2  * Copyright (c) 2012, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  * This code was adapted from the MIT Kerberos Consortium's
34  * GSS example code, which was distributed under the following
35  * license:
36  *
37  * Copyright 2004-2006 Massachusetts Institute of Technology.
38  * All Rights Reserved.
39  *
40  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
41  * distribute this software and its documentation for any purpose and
42  * without fee is hereby granted, provided that the above copyright
43  * notice appear in all copies and that both that copyright notice and
44  * this permission notice appear in supporting documentation, and that
45  * the name of M.I.T. not be used in advertising or publicity pertaining
46  * to distribution of the software without specific, written prior
47  * permission.  Furthermore if you modify this software you must label
48  * your software as modified software and not distribute it in such a
49  * fashion that it might be confused with the original M.I.T. software.
50  * M.I.T. makes no representations about the suitability of
51  * this software for any purpose.  It is provided "as is" without express
52  * or implied warranty.
53  */
54
55 #include <gsscon.h>
56
57 /* --------------------------------------------------------------------------- */
58 /* Display the contents of the buffer in hex and ascii                         */
59
60 static void PrintBuffer (const char *inBuffer, 
61                          size_t      inLength)
62 {
63     int i;  
64     
65     for (i = 0; i < inLength; i += 16) {
66         int l;
67         for (l = i; l < (i + 16); l++) {
68             if (l >= inLength) {
69                 printf ("  ");
70             } else {
71                 u_int8_t *byte = (u_int8_t *) inBuffer + l;
72                 printf ("%2.2x", *byte);
73             }
74             if ((l % 4) == 3) { printf (" "); }
75         }
76         printf ("   ");
77         for (l = i; l < (i + 16) && l < inLength; l++) {
78             printf ("%c", ((inBuffer[l] > 0x1f) && 
79                            (inBuffer[l] < 0x7f)) ? inBuffer[l] : '.');            
80         }
81         printf ("\n");
82     }
83     printf ("\n");
84 }
85
86 /* --------------------------------------------------------------------------- */
87 /* Standard network read loop, accounting for EINTR, EOF and incomplete reads  */
88
89 static int ReadBuffer (int     inSocket, 
90                        size_t  inBufferLength, 
91                        char   *ioBuffer)
92 {
93     int err = 0;
94     ssize_t bytesRead = 0;
95     
96     if (!ioBuffer) { err = EINVAL; }
97     
98     if (!err) {
99         char *ptr = ioBuffer;
100         do {
101             ssize_t count = read (inSocket, ptr, inBufferLength - bytesRead);
102             if (count < 0) {
103                 /* Try again on EINTR */
104                 if (errno != EINTR) { err = errno; }
105             } else if (count == 0) {
106                 err = ECONNRESET; /* EOF and we expected data */
107             } else {
108                 ptr += count;
109                 bytesRead += count;
110             }
111         } while (!err && (bytesRead < inBufferLength));
112     } 
113     
114     if (err) { gsscon_print_error (err, "ReadBuffer failed"); }
115
116     return err;
117 }
118
119 /* --------------------------------------------------------------------------- */
120 /* Standard network write loop, accounting for EINTR and incomplete writes     */
121
122 static int WriteBuffer (int         inSocket, 
123                         const char *inBuffer, 
124                         size_t      inBufferLength)
125 {
126     int err = 0;
127     ssize_t bytesWritten = 0;
128     
129     if (!inBuffer) { err = EINVAL; }
130     
131     if (!err) {
132         const char *ptr = inBuffer;
133         do {
134             ssize_t count;
135
136             count = write (inSocket, ptr, inBufferLength - bytesWritten);
137
138             if (count < 0) {
139                 /* Try again on EINTR */
140                 if (errno != EINTR) { err = errno; }
141             } else {
142                 ptr += count;
143                 bytesWritten += count;
144             }
145         } while (!err && (bytesWritten < inBufferLength));
146     } 
147     
148     if (err) { gsscon_print_error (err, "WriteBuffer failed"); }
149
150     return err;
151 }
152
153 /* --------------------------------------------------------------------------- */
154 /* Read a GSS token (length + data) off the network                            */
155
156 int gsscon_read_token (int      inSocket, 
157                char   **outTokenValue, 
158                size_t  *outTokenLength)
159 {
160     int err = 0;
161     char *token = NULL;
162     u_int32_t tokenLength = 0;
163     
164     if (!outTokenValue ) { err = EINVAL; }
165     if (!outTokenLength) { err = EINVAL; }
166     
167     if (!err) {
168         err = ReadBuffer (inSocket, 4, (char *) &tokenLength);
169     }
170     
171     if (!err) {
172         tokenLength = ntohl (tokenLength);
173         token = malloc (tokenLength);
174         if (token==NULL) {
175           err=EIO;
176         } else {
177           memset (token, 0, tokenLength); 
178         
179           err = ReadBuffer (inSocket, tokenLength, token);
180         }
181     }
182     
183     if (!err) {
184         *outTokenLength = tokenLength;
185         *outTokenValue = token;        
186         token = NULL; /* only free on error */
187     } else { 
188         gsscon_print_error (err, "ReadToken failed"); 
189     }
190
191     if (token) { free (token); }
192     
193     return err;
194 }
195
196 /* --------------------------------------------------------------------------- */
197 /* Write a GSS token (length + data) onto the network                          */
198
199 int gsscon_write_token (int         inSocket, 
200                 const char *inTokenValue, 
201                 size_t      inTokenLength)
202 {
203     int err = 0;
204     u_int32_t tokenLength = htonl (inTokenLength);
205
206     if (!inTokenValue) { err = EINVAL; }
207     
208     if (!err) {
209         err = WriteBuffer (inSocket, (char *) &tokenLength, 4);
210     }
211         
212     if (!err) { 
213         err = WriteBuffer (inSocket, inTokenValue, inTokenLength);
214     }
215     
216     if (!err) {
217     //    printf ("Wrote token:\n");
218     //    PrintBuffer (inTokenValue, inTokenLength);
219
220     } else { 
221         gsscon_print_error (err, "gsscon_write_token() failed");
222     }
223    
224     return err;
225 }
226
227 /* --------------------------------------------------------------------------- */
228 /* Read an encrypted GSS token (length + encrypted data) off the network       */
229
230
231 int gsscon_read_encrypted_token (int                  inSocket, 
232                         const gss_ctx_id_t   inContext, 
233                         char               **outTokenValue, 
234                         size_t              *outTokenLength)
235 {
236     int err = 0;
237     char *token = NULL;
238     size_t tokenLength = 0;
239     OM_uint32 majorStatus;
240     OM_uint32 minorStatus = 0;
241     gss_buffer_desc outputBuffer = { 0 , NULL};
242     char *unencryptedToken = NULL;
243     
244     if (!inContext     ) { err = EINVAL; }
245     if (!outTokenValue ) { err = EINVAL; }
246     if (!outTokenLength) { err = EINVAL; }
247     
248     if (!err) {
249         err = gsscon_read_token (inSocket, &token, &tokenLength);
250     }
251     
252     if (!err) {
253         gss_buffer_desc inputBuffer = { tokenLength, token};
254         int encrypted = 0; /* did mechanism encrypt/integrity protect? */
255
256         majorStatus = gss_unwrap (&minorStatus, 
257                                   inContext, 
258                                   &inputBuffer, 
259                                   &outputBuffer, 
260                                   &encrypted, 
261                                   NULL /* qop_state */);
262         if (majorStatus != GSS_S_COMPLETE) { 
263             gsscon_print_gss_errors("gss_unwrap", majorStatus, minorStatus);
264             err = minorStatus ? minorStatus : majorStatus; 
265         } else if (!encrypted) {
266             fprintf (stderr, "WARNING!  Mechanism not using encryption!");
267             err = EINVAL; /* You may not want to fail here. */
268         }
269     }
270     
271     if (!err) {
272         unencryptedToken = malloc (outputBuffer.length);
273         if (unencryptedToken == NULL) { err = ENOMEM; }
274     }
275     
276     if (!err) {
277         memcpy (unencryptedToken, outputBuffer.value, outputBuffer.length);
278         
279         // printf ("Unencrypted token:\n");
280         // PrintBuffer (unencryptedToken, outputBuffer.length);
281         
282         *outTokenLength = outputBuffer.length;
283         *outTokenValue = unencryptedToken;
284         unencryptedToken = NULL; /* only free on error */
285         
286     } else { 
287         gsscon_print_error (err, "ReadToken failed"); 
288     }
289     
290     if (token             ) { free (token); }
291     if (outputBuffer.value) { gss_release_buffer (&minorStatus, &outputBuffer); }
292     if (unencryptedToken  ) { free (unencryptedToken); }
293     
294     return err;
295 }
296
297 /* --------------------------------------------------------------------------- */
298 /* Write an encrypted GSS token (length + encrypted data) onto the network     */
299
300 int gsscon_write_encrypted_token (int                 inSocket, 
301                          const gss_ctx_id_t  inContext, 
302                          const char         *inToken, 
303                          size_t              inTokenLength)
304 {
305     int err = 0;
306     OM_uint32 majorStatus;
307     OM_uint32 minorStatus = 0;
308     gss_buffer_desc outputBuffer = { 0, NULL };
309     
310     if (!inContext) { err = EINVAL; }
311     if (!inToken  ) { err = EINVAL; }
312     
313     if (!err) {
314         gss_buffer_desc inputBuffer = { inTokenLength, (char *) inToken };
315         int encrypt = 1;   /* do encryption and integrity protection */
316         int encrypted = 0; /* did mechanism encrypt/integrity protect? */
317         
318         majorStatus = gss_wrap (&minorStatus, 
319                                 inContext, 
320                                 encrypt, 
321                                 GSS_C_QOP_DEFAULT,
322                                 &inputBuffer, 
323                                 &encrypted, 
324                                 &outputBuffer);
325         if (majorStatus != GSS_S_COMPLETE) { 
326             gsscon_print_gss_errors ("gss_wrap", majorStatus, minorStatus);
327             err = minorStatus ? minorStatus : majorStatus; 
328         } else if (!encrypted) {
329             fprintf (stderr, "WARNING!  Mechanism does not support encryption!");
330             err = EINVAL; /* You may not want to fail here. */
331         }
332     }
333     
334     if (!err) {
335       //  printf ("Unencrypted token:\n");
336       //  PrintBuffer (inToken, inTokenLength);
337         err = gsscon_write_token (inSocket, outputBuffer.value, outputBuffer.length);
338     }
339     
340     if (!err) {
341     } else { 
342         gsscon_print_error (err, "gsscon_write_token failed");
343     }
344     
345     if (outputBuffer.value) { gss_release_buffer (&minorStatus, &outputBuffer); }
346     
347     return err;
348 }
349
350 /* --------------------------------------------------------------------------- */
351 /* Print BSD error                                                             */
352
353 void gsscon_print_error (int         inError, 
354                  const char *inString)
355 {
356     fprintf (stderr, "%s: %s (err = %d)\n", 
357              inString, error_message (inError), inError);
358 }
359
360 /* --------------------------------------------------------------------------- */
361 /* PrintGSSAPI errors                                                         */
362
363 void gsscon_print_gss_errors (const char *inRoutineName, 
364                      OM_uint32   inMajorStatus, 
365                      OM_uint32   inMinorStatus)
366 {
367     OM_uint32 minorStatus;
368     OM_uint32 majorStatus;      
369     gss_buffer_desc errorBuffer;
370
371     OM_uint32 messageContext = 0; /* first message */
372     int count = 1;
373     
374     fprintf (stderr, "Error returned by %s:\n", inRoutineName);
375     
376     do {
377         majorStatus = gss_display_status (&minorStatus, 
378                                           inMajorStatus, 
379                                           GSS_C_GSS_CODE, 
380                                           GSS_C_NULL_OID, 
381                                           &messageContext, 
382                                           &errorBuffer);
383         if (majorStatus == GSS_S_COMPLETE) {
384             fprintf (stderr,"      major error <%d> %s\n", 
385                      count, (char *) errorBuffer.value);
386             gss_release_buffer (&minorStatus, &errorBuffer);
387         }
388         ++count;
389     } while (messageContext != 0);
390     
391     count = 1;
392     messageContext = 0;
393     do {
394         majorStatus = gss_display_status (&minorStatus, 
395                                           inMinorStatus, 
396                                           GSS_C_MECH_CODE, 
397                                           GSS_C_NULL_OID, 
398                                           &messageContext, 
399                                           &errorBuffer);
400         fprintf (stderr,"      minor error <%d> %s\n", 
401                  count, (char *) errorBuffer.value);
402         ++count;
403     } while (messageContext != 0);
404 }
405