Home | History | Annotate | Line # | Download | only in nss-pam-ldapd
      1 /*	$NetBSD: nslcd-prot.h,v 1.2 2021/08/14 16:14:52 christos Exp $	*/
      2 
      3 /*
      4    nslcd-prot.h - helper macros for reading and writing in protocol streams
      5 
      6    Copyright (C) 2006 West Consulting
      7    Copyright (C) 2006-2014 Arthur de Jong
      8 
      9    This library is free software; you can redistribute it and/or
     10    modify it under the terms of the GNU Lesser General Public
     11    License as published by the Free Software Foundation; either
     12    version 2.1 of the License, or (at your option) any later version.
     13 
     14    This library is distributed in the hope that it will be useful,
     15    but WITHOUT ANY WARRANTY; without even the implied warranty of
     16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     17    Lesser General Public License for more details.
     18 
     19    You should have received a copy of the GNU Lesser General Public
     20    License along with this library; if not, write to the Free Software
     21    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
     22    02110-1301 USA
     23 */
     24 
     25 #ifndef COMMON__NSLCD_PROT_H
     26 #define COMMON__NSLCD_PROT_H 1
     27 
     28 #include <arpa/inet.h>
     29 #include <netinet/in.h>
     30 
     31 #include "tio.h"
     32 
     33 /* If you use these macros you should define the following macros to
     34    handle error conditions (these marcos should clean up and return from the
     35    function):
     36      ERROR_OUT_WRITEERROR(fp)
     37      ERROR_OUT_READERROR(fp)
     38      ERROR_OUT_BUFERROR(fp)
     39      ERROR_OUT_NOSUCCESS(fp) */
     40 
     41 
     42 /* Debugging marcos that can be used to enable detailed protocol logging,
     43    pass -DDEBUG_PROT to do overall protocol debugging, and -DDEBUG_PROT_DUMP
     44    to dump the actual bytestream. */
     45 
     46 #ifdef DEBUG_PROT
     47 /* define a debugging macro to output logging */
     48 #include <string.h>
     49 #include <errno.h>
     50 #define DEBUG_PRINT(fmt, arg)                                               \
     51   fprintf(stderr, "%s:%d:%s: " fmt "\n", __FILE__, __LINE__,                \
     52           __PRETTY_FUNCTION__, arg);
     53 #else /* DEBUG_PROT */
     54 /* define an empty debug macro to disable logging */
     55 #define DEBUG_PRINT(fmt, arg)
     56 #endif /* not DEBUG_PROT */
     57 
     58 #ifdef DEBUG_PROT_DUMP
     59 /* define a debugging macro to output detailed logging */
     60 #ifdef HAVE_STDINT_H
     61 #include <stdint.h>
     62 #endif /* HAVE_STDINT_H */
     63 static void debug_dump(const void *ptr, size_t size)
     64 {
     65   int i;
     66   for (i = 0; i < size; i++)
     67     fprintf(stderr, " %02x", ((const uint8_t *)ptr)[i]);
     68   fprintf(stderr, "\n");
     69 }
     70 #define DEBUG_DUMP(ptr, size)                                               \
     71   fprintf(stderr, "%s:%d:%s:", __FILE__, __LINE__, __PRETTY_FUNCTION__);    \
     72   debug_dump(ptr, size);
     73 #else /* DEBUG_PROT_DUMP */
     74 /* define an empty debug macro to disable logging */
     75 #define DEBUG_DUMP(ptr, size)
     76 #endif /* not DEBUG_PROT_DUMP */
     77 
     78 
     79 /* WRITE marcos, used for writing data, on write error they will
     80    call the ERROR_OUT_WRITEERROR macro
     81    these macros may require the availability of the following
     82    variables:
     83    int32_t tmpint32; - temporary variable
     84    */
     85 
     86 #define WRITE(fp, ptr, size)                                                \
     87   DEBUG_PRINT("WRITE       : var="__STRING(ptr)" size=%d", (int)size);      \
     88   DEBUG_DUMP(ptr, size);                                                    \
     89   if (tio_write(fp, ptr, (size_t)size))                                     \
     90   {                                                                         \
     91     char ebuf[128];                                                         \
     92     int saved_errno = errno;                                                \
     93     DEBUG_PRINT("WRITE       : var="__STRING(ptr)" error: %s",              \
     94                 AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)));            \
     95     ERROR_OUT_WRITEERROR(fp);                                               \
     96   }
     97 
     98 #define WRITE_INT32(fp, i)                                                  \
     99   DEBUG_PRINT("WRITE_INT32 : var="__STRING(i)" int32=%08x", (int)i);        \
    100   tmpint32 = htonl((int32_t)(i));                                           \
    101   WRITE(fp, &tmpint32, sizeof(int32_t))
    102 
    103 #define WRITE_STRING(fp, str)                                               \
    104   DEBUG_PRINT("WRITE_STRING: var="__STRING(str)" string=\"%s\"", (str));    \
    105   if ((str) == NULL)                                                        \
    106   {                                                                         \
    107     WRITE_INT32(fp, 0);                                                     \
    108   }                                                                         \
    109   else                                                                      \
    110   {                                                                         \
    111     WRITE_INT32(fp, strlen(str));                                           \
    112     tmpint32 = ntohl(tmpint32);                                             \
    113     if (tmpint32 > 0)                                                       \
    114     {                                                                       \
    115       WRITE(fp, (str), tmpint32);                                           \
    116     }                                                                       \
    117   }
    118 
    119 #define WRITE_STRINGLIST(fp, arr)                                           \
    120   if ((arr) == NULL)                                                        \
    121   {                                                                         \
    122     DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", 0);             \
    123     WRITE_INT32(fp, 0);                                                     \
    124   }                                                                         \
    125   else                                                                      \
    126   {                                                                         \
    127     /* first determine length of array */                                   \
    128     for (tmp3int32 = 0; (arr)[tmp3int32] != NULL; tmp3int32++)              \
    129       /* noting */ ;                                                        \
    130     /* write number of strings */                                           \
    131     DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
    132     WRITE_INT32(fp, tmp3int32);                                             \
    133     /* write strings */                                                     \
    134     for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++)                 \
    135     {                                                                       \
    136       WRITE_STRING(fp, (arr)[tmp2int32]);                                   \
    137     }                                                                       \
    138   }
    139 
    140 #define WRITE_STRINGLIST_EXCEPT(fp, arr, not)                               \
    141   /* first determine length of array */                                     \
    142   tmp3int32 = 0;                                                            \
    143   for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++)                \
    144     if (strcmp((arr)[tmp2int32], (not)) != 0)                               \
    145       tmp3int32++;                                                          \
    146   /* write number of strings (mius one because we intend to skip one) */    \
    147   DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32);  \
    148   WRITE_INT32(fp, tmp3int32);                                               \
    149   /* write strings */                                                       \
    150   for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++)                \
    151   {                                                                         \
    152     if (strcmp((arr)[tmp2int32], (not)) != 0)                               \
    153     {                                                                       \
    154       WRITE_STRING(fp, (arr)[tmp2int32]);                                   \
    155     }                                                                       \
    156   }
    157 
    158 /* READ macros, used for reading data, on read error they will
    159    call the ERROR_OUT_READERROR or ERROR_OUT_BUFERROR macro
    160    these macros may require the availability of the following
    161    variables:
    162    int32_t tmpint32; - temporary variable
    163    */
    164 
    165 #define READ(fp, ptr, size)                                                 \
    166   if (tio_read(fp, ptr, (size_t)size))                                      \
    167   {                                                                         \
    168     char ebuf[128];                                                         \
    169     int saved_errno = errno;                                                \
    170     DEBUG_PRINT("READ       : var="__STRING(ptr)" error: %s",               \
    171                 AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)));            \
    172     ERROR_OUT_READERROR(fp);                                                \
    173   }                                                                         \
    174   DEBUG_PRINT("READ       : var="__STRING(ptr)" size=%d", (int)(size));     \
    175   DEBUG_DUMP(ptr, size);
    176 
    177 #define READ_INT32(fp, i)                                                   \
    178   READ(fp, &tmpint32, sizeof(int32_t));                                     \
    179   (i) = (int32_t)ntohl(tmpint32);                                           \
    180   DEBUG_PRINT("READ_INT32 : var="__STRING(i)" int32==%08x", (int)(i));
    181 
    182 /* read a string in a fixed-size "normal" buffer */
    183 #define READ_STRING(fp, buffer)                                             \
    184   /* read the size of the string */                                         \
    185   READ(fp, &tmpint32, sizeof(int32_t));                                     \
    186   tmpint32 = ntohl(tmpint32);                                               \
    187   DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" strlen=%d", tmpint32);   \
    188   /* check if read would fit */                                             \
    189   if (((size_t)tmpint32) >= sizeof(buffer))                                 \
    190   {                                                                         \
    191     /* will not fit */                                                      \
    192     tmpint32 = (tmpint32 - sizeof(buffer)) + 1;                             \
    193     DEBUG_PRINT("READ       : buffer %d bytes too small", tmpint32);        \
    194     ERROR_OUT_BUFERROR(fp);                                                 \
    195   }                                                                         \
    196   /* read string from the stream */                                         \
    197   if (tmpint32 > 0)                                                         \
    198   {                                                                         \
    199     READ(fp, buffer, (size_t)tmpint32);                                     \
    200   }                                                                         \
    201   /* null-terminate string in buffer */                                     \
    202   buffer[tmpint32] = '\0';                                                  \
    203   DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" string=\"%s\"", buffer);
    204 
    205 
    206 /* READ BUF macros that read data into a pre-allocated buffer.
    207    these macros may require the availability of the following
    208    variables:
    209    int32_t tmpint32; - temporary variable
    210    char *buffer;     - pointer to a buffer for reading strings
    211    size_t buflen;    - the size of the buffer
    212    size_t bufptr;    - the current position in the buffer
    213    */
    214 
    215 /* current position in the buffer */
    216 #define BUF_CUR                                                             \
    217   (buffer + bufptr)
    218 
    219 /* check that the buffer has sz bytes left in it */
    220 #define BUF_CHECK(fp, sz)                                                   \
    221   if ((bufptr + (size_t)(sz)) > buflen)                                     \
    222   {                                                                         \
    223     /* will not fit */                                                      \
    224     tmpint32 = bufptr + (sz) - (buflen);                                    \
    225     DEBUG_PRINT("READ       : buffer %d bytes too small", tmpint32);        \
    226     ERROR_OUT_BUFERROR(fp);                                                 \
    227   }
    228 
    229 /* move the buffer pointer */
    230 #define BUF_SKIP(sz)                                                        \
    231   bufptr += (size_t)(sz);
    232 
    233 /* move BUF_CUR forward so that it is aligned to the specified
    234    type width */
    235 #define BUF_ALIGN(fp, type)                                                 \
    236   /* figure out number of bytes to skip forward */                         \
    237   tmp2int32 = (sizeof(type) - ((BUF_CUR - (char *)NULL) % sizeof(type)))    \
    238               % sizeof(type);                                               \
    239   /* check and skip */                                                      \
    240   BUF_CHECK(fp, tmp2int32);                                                 \
    241   BUF_SKIP(tmp2int32);
    242 
    243 /* allocate a piece of the buffer to store an array in */
    244 #define BUF_ALLOC(fp, ptr, type, num)                                       \
    245   /* align to the specified type width */                                   \
    246   BUF_ALIGN(fp, type);                                                      \
    247   /* check that we have enough room */                                      \
    248   BUF_CHECK(fp, (size_t)(num) * sizeof(type));                              \
    249   /* store the pointer */                                                   \
    250   (ptr) = (type *)BUF_CUR;                                                  \
    251   /* reserve the space */                                                   \
    252   BUF_SKIP((size_t)(num) * sizeof(type));
    253 
    254 /* read a binary blob into the buffer */
    255 #define READ_BUF(fp, ptr, sz)                                               \
    256   /* check that there is enough room and read */                            \
    257   BUF_CHECK(fp, sz);                                                        \
    258   READ(fp, BUF_CUR, (size_t)sz);                                            \
    259   /* store pointer and skip */                                              \
    260   (ptr) = BUF_CUR;                                                          \
    261   BUF_SKIP(sz);
    262 
    263 /* read string in the buffer (using buffer, buflen and bufptr)
    264    and store the actual location of the string in field */
    265 #define READ_BUF_STRING(fp, field)                                          \
    266   /* read the size of the string */                                         \
    267   READ(fp, &tmpint32, sizeof(int32_t));                                     \
    268   tmpint32 = ntohl(tmpint32);                                               \
    269   DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" strlen=%d", tmpint32); \
    270   /* check if read would fit */                                             \
    271   BUF_CHECK(fp, tmpint32 + 1);                                              \
    272   /* read string from the stream */                                         \
    273   if (tmpint32 > 0)                                                         \
    274   {                                                                         \
    275     READ(fp, BUF_CUR, (size_t)tmpint32);                                    \
    276   }                                                                         \
    277   /* null-terminate string in buffer */                                     \
    278   BUF_CUR[tmpint32] = '\0';                                                 \
    279   DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" string=\"%s\"", BUF_CUR); \
    280   /* prepare result */                                                      \
    281   (field) = BUF_CUR;                                                        \
    282   BUF_SKIP(tmpint32 + 1);
    283 
    284 /* read an array from a stream and store it as a null-terminated
    285    array list (size for the array is allocated) */
    286 #define READ_BUF_STRINGLIST(fp, arr)                                        \
    287   /* read the number of entries */                                          \
    288   READ(fp, &tmp3int32, sizeof(int32_t));                                    \
    289   tmp3int32 = ntohl(tmp3int32);                                             \
    290   DEBUG_PRINT("READ_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32);   \
    291   /* allocate room for *char[num + 1] */                                      \
    292   BUF_ALLOC(fp, arr, char *, tmp3int32 + 1);                                \
    293   /* read all entries */                                                    \
    294   for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++)                   \
    295   {                                                                         \
    296     READ_BUF_STRING(fp, (arr)[tmp2int32]);                                  \
    297   }                                                                         \
    298   /* set last entry to NULL */                                              \
    299   (arr)[tmp2int32] = NULL;
    300 
    301 
    302 /* SKIP macros for skipping over certain parts of the protocol stream. */
    303 
    304 /* skip a number of bytes forward */
    305 #define SKIP(fp, sz)                                                        \
    306   DEBUG_PRINT("READ       : skip %d bytes", (int)(sz));                     \
    307   /* read (skip) the specified number of bytes */                           \
    308   if (tio_skip(fp, sz))                                                     \
    309   {                                                                         \
    310     char ebuf[128];                                                         \
    311     int saved_errno = errno;                                                \
    312     DEBUG_PRINT("READ       : skip error: %s",                              \
    313                 AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)));            \
    314     ERROR_OUT_READERROR(fp);                                                \
    315   }
    316 
    317 /* read a string from the stream but don't do anything with the result */
    318 #define SKIP_STRING(fp)                                                     \
    319   /* read the size of the string */                                         \
    320   READ(fp, &tmpint32, sizeof(int32_t));                                     \
    321   tmpint32 = ntohl(tmpint32);                                               \
    322   DEBUG_PRINT("READ_STRING: skip %d bytes", (int)tmpint32);                 \
    323   /* read (skip) the specified number of bytes */                           \
    324   SKIP(fp, tmpint32);
    325 
    326 /* skip a list of strings */
    327 #define SKIP_STRINGLIST(fp)                                                 \
    328   /* read the number of entries */                                          \
    329   READ(fp, &tmp3int32, sizeof(int32_t));                                    \
    330   tmp3int32 = ntohl(tmp3int32);                                             \
    331   DEBUG_PRINT("READ_STRLST: skip %d strings", (int)tmp3int32);              \
    332   /* read all entries */                                                    \
    333   for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++)                   \
    334   {                                                                         \
    335     SKIP_STRING(fp);                                                        \
    336   }
    337 
    338 
    339 /* These are functions and macros for performing common operations in
    340    the nslcd request/response protocol. */
    341 
    342 /* returns a socket to the server or NULL on error (see errno),
    343    socket should be closed with tio_close() */
    344 TFILE *nslcd_client_open(void)
    345   MUST_USE;
    346 
    347 /* generic request code */
    348 #define NSLCD_REQUEST(fp, action, writefn)                                  \
    349   /* open a client socket */                                                \
    350   if ((fp = nslcd_client_open()) == NULL)                                   \
    351   {                                                                         \
    352     ERROR_OUT_OPENERROR;                                                    \
    353   }                                                                         \
    354   /* write a request header with a request code */                          \
    355   WRITE_INT32(fp, (int32_t)NSLCD_VERSION)                                   \
    356   WRITE_INT32(fp, (int32_t)action)                                          \
    357   /* write the request parameters (if any) */                               \
    358   writefn;                                                                  \
    359   /* flush the stream */                                                    \
    360   if (tio_flush(fp) < 0)                                                    \
    361   {                                                                         \
    362     char ebuf[128];                                                         \
    363     int saved_errno = errno;                                                \
    364     DEBUG_PRINT("WRITE_FLUSH : error: %s",                                  \
    365                 AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)));            \
    366     ERROR_OUT_WRITEERROR(fp);                                               \
    367   }                                                                         \
    368   /* read and check response version number */                              \
    369   READ(fp, &tmpint32, sizeof(int32_t));                                     \
    370   tmpint32 = ntohl(tmpint32);                                               \
    371   if (tmpint32 != (int32_t)NSLCD_VERSION)                                   \
    372   {                                                                         \
    373     ERROR_OUT_READERROR(fp);                                                \
    374   }                                                                         \
    375   /* read and check response request number */                              \
    376   READ(fp, &tmpint32, sizeof(int32_t));                                     \
    377   tmpint32 = ntohl(tmpint32);                                               \
    378   if (tmpint32 != (int32_t)(action))                                        \
    379   {                                                                         \
    380     ERROR_OUT_READERROR(fp);                                                \
    381   }
    382 
    383 /* Read the response code (the result code of the query) from
    384    the stream. */
    385 #define READ_RESPONSE_CODE(fp)                                              \
    386   READ(fp, &tmpint32, sizeof(int32_t));                                     \
    387   tmpint32 = ntohl(tmpint32);                                               \
    388   if (tmpint32 != (int32_t)NSLCD_RESULT_BEGIN)                              \
    389   {                                                                         \
    390     ERROR_OUT_NOSUCCESS(fp);                                                \
    391   }
    392 
    393 #endif /* not COMMON__NSLCD_PROT_H */
    394