1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
|
/*
common.c - common server code routines
This file is part of the nss-pam-ldapd library.
Copyright (C) 2006 West Consulting
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Arthur de Jong
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <limits.h>
#include <netdb.h>
#include <string.h>
#include <regex.h>
#include <stdlib.h>
#include <signal.h>
#include "nslcd.h"
#include "common.h"
#include "log.h"
#include "attmap.h"
#include "cfg.h"
/* simple wrapper around snptintf() to return non-zero in case
of any failure (but always keep string 0-terminated) */
int mysnprintf(char *buffer, size_t buflen, const char *format, ...)
{
int res;
va_list ap;
/* do snprintf */
va_start(ap, format);
res = vsnprintf(buffer, buflen, format, ap);
va_end(ap);
/* NULL-terminate the string just to be on the safe side */
buffer[buflen - 1] = '\0';
/* check if the string was completely written */
return ((res < 0) || (((size_t)res) >= buflen));
}
/* get a name of a signal with a given signal number */
const char *signame(int signum)
{
switch (signum)
{
case SIGHUP: return "SIGHUP"; /* Hangup detected */
case SIGINT: return "SIGINT"; /* Interrupt from keyboard */
case SIGQUIT: return "SIGQUIT"; /* Quit from keyboard */
case SIGILL: return "SIGILL"; /* Illegal Instruction */
case SIGABRT: return "SIGABRT"; /* Abort signal from abort(3) */
case SIGFPE: return "SIGFPE"; /* Floating point exception */
case SIGKILL: return "SIGKILL"; /* Kill signal */
case SIGSEGV: return "SIGSEGV"; /* Invalid memory reference */
case SIGPIPE: return "SIGPIPE"; /* Broken pipe */
case SIGALRM: return "SIGALRM"; /* Timer signal from alarm(2) */
case SIGTERM: return "SIGTERM"; /* Termination signal */
case SIGUSR1: return "SIGUSR1"; /* User-defined signal 1 */
case SIGUSR2: return "SIGUSR2"; /* User-defined signal 2 */
case SIGCHLD: return "SIGCHLD"; /* Child stopped or terminated */
case SIGCONT: return "SIGCONT"; /* Continue if stopped */
case SIGSTOP: return "SIGSTOP"; /* Stop process */
case SIGTSTP: return "SIGTSTP"; /* Stop typed at tty */
case SIGTTIN: return "SIGTTIN"; /* tty input for background process */
case SIGTTOU: return "SIGTTOU"; /* tty output for background process */
#ifdef SIGBUS
case SIGBUS: return "SIGBUS"; /* Bus error */
#endif
#ifdef SIGPOLL
case SIGPOLL: return "SIGPOLL"; /* Pollable event */
#endif
#ifdef SIGPROF
case SIGPROF: return "SIGPROF"; /* Profiling timer expired */
#endif
#ifdef SIGSYS
case SIGSYS: return "SIGSYS"; /* Bad argument to routine */
#endif
#ifdef SIGTRAP
case SIGTRAP: return "SIGTRAP"; /* Trace/breakpoint trap */
#endif
#ifdef SIGURG
case SIGURG: return "SIGURG"; /* Urgent condition on socket */
#endif
#ifdef SIGVTALRM
case SIGVTALRM: return "SIGVTALRM"; /* Virtual alarm clock */
#endif
#ifdef SIGXCPU
case SIGXCPU: return "SIGXCPU"; /* CPU time limit exceeded */
#endif
#ifdef SIGXFSZ
case SIGXFSZ: return "SIGXFSZ"; /* File size limit exceeded */
#endif
default: return "UNKNOWN";
}
}
/* return the fully qualified domain name of the current host */
const char *getfqdn(void)
{
static char *fqdn = NULL;
char hostname[BUFLEN_HOSTNAME];
int hostnamelen;
int i;
struct hostent *host = NULL;
/* if we already have a fqdn return that */
if (fqdn != NULL)
return fqdn;
/* get system hostname */
if (gethostname(hostname, sizeof(hostname)) < 0)
{
log_log(LOG_ERR, "gethostname() failed: %s", strerror(errno));
return NULL;
}
hostnamelen = strlen(hostname);
/* lookup hostent */
host = gethostbyname(hostname);
if (host == NULL)
{
log_log(LOG_ERR, "gethostbyname(%s): %s", hostname, hstrerror(h_errno));
/* fall back to hostname */
fqdn = strdup(hostname);
return fqdn;
}
/* check h_name for fqdn starting with our hostname */
if ((strncasecmp(hostname, host->h_name, hostnamelen) == 0) &&
(host->h_name[hostnamelen] == '.') &&
(host->h_name[hostnamelen + 1] != '\0'))
{
fqdn = strdup(host->h_name);
return fqdn;
}
/* also check h_aliases */
for (i = 0; host->h_aliases[i] != NULL; i++)
{
if ((strncasecmp(hostname, host->h_aliases[i], hostnamelen) == 0) &&
(host->h_aliases[i][hostnamelen] == '.') &&
(host->h_aliases[i][hostnamelen + 1] != '\0'))
{
fqdn = strdup(host->h_aliases[i]);
return fqdn;
}
}
/* fall back to h_name if it has a dot in it */
if (strchr(host->h_name, '.') != NULL)
{
fqdn = strdup(host->h_name);
return fqdn;
}
/* also check h_aliases */
for (i = 0; host->h_aliases[i] != NULL; i++)
{
if (strchr(host->h_aliases[i], '.') != NULL)
{
fqdn = strdup(host->h_aliases[i]);
return fqdn;
}
}
/* nothing found, fall back to hostname */
fqdn = strdup(hostname);
return fqdn;
}
const char *get_userpassword(MYLDAP_ENTRY *entry, const char *attr,
char *buffer, size_t buflen)
{
const char *tmpvalue;
/* get the value */
tmpvalue = attmap_get_value(entry, attr, buffer, buflen);
if (tmpvalue == NULL)
return NULL;
/* go over the entries and return the remainder of the value if it
starts with {crypt} or crypt$ */
if (strncasecmp(tmpvalue, "{crypt}", 7) == 0)
return tmpvalue + 7;
if (strncasecmp(tmpvalue, "crypt$", 6) == 0)
return tmpvalue + 6;
/* just return the first value completely */
return tmpvalue;
/* TODO: support more password formats e.g. SMD5
(which is $1$ but in a different format)
(any code for this is more than welcome) */
}
/* Checks if the specified name seems to be a valid user or group name. */
int isvalidname(const char *name)
{
return regexec(&nslcd_cfg->validnames, name, 0, NULL, 0) == 0;
}
/* this writes a single address to the stream */
int write_address(TFILE *fp, MYLDAP_ENTRY *entry, const char *attr,
const char *addr)
{
int32_t tmpint32;
struct in_addr ipv4addr;
struct in6_addr ipv6addr;
/* try to parse the address as IPv4 first, fall back to IPv6 */
if (inet_pton(AF_INET, addr, &ipv4addr) > 0)
{
/* write address type */
WRITE_INT32(fp, AF_INET);
/* write the address length */
WRITE_INT32(fp, sizeof(struct in_addr));
/* write the address itself (in network byte order) */
WRITE(fp, &ipv4addr, sizeof(struct in_addr));
}
else if (inet_pton(AF_INET6, addr, &ipv6addr) > 0)
{
/* write address type */
WRITE_INT32(fp, AF_INET6);
/* write the address length */
WRITE_INT32(fp, sizeof(struct in6_addr));
/* write the address itself (in network byte order) */
WRITE(fp, &ipv6addr, sizeof(struct in6_addr));
}
else
{
/* failure, log but write simple invalid address
(otherwise the address list is messed up) */
/* TODO: have error message in correct format */
log_log(LOG_WARNING, "%s: %s: \"%s\" unparsable",
myldap_get_dn(entry), attr, addr);
/* write an illegal address type */
WRITE_INT32(fp, -1);
/* write an emtpy address */
WRITE_INT32(fp, 0);
}
/* we're done */
return 0;
}
int read_address(TFILE *fp, char *addr, int *addrlen, int *af)
{
int32_t tmpint32;
int len;
/* read address family */
READ_INT32(fp, *af);
if ((*af != AF_INET) && (*af != AF_INET6))
{
log_log(LOG_WARNING, "incorrect address family specified: %d", *af);
return -1;
}
/* read address length */
READ_INT32(fp, len);
if ((len > *addrlen) || (len <= 0))
{
log_log(LOG_WARNING, "address length incorrect: %d", len);
return -1;
}
*addrlen = len;
/* read address */
READ(fp, addr, len);
/* we're done */
return 0;
}
/* convert the provided string representation of a sid
(e.g. S-1-5-21-1936905831-823966427-12391542-23578)
to a format that can be used to search the objectSid property with */
char *sid2search(const char *sid)
{
const char *tmpsid = sid;
char *res, *tmp;
int i = 0;
unsigned long int l;
/* check the beginning of the string */
if (strncasecmp(sid, "S-", 2) != 0)
{
log_log(LOG_ERR, "error in SID %s", sid);
exit(EXIT_FAILURE);
}
/* count the number of dashes in the sid */
while (tmpsid != NULL)
{
i++;
tmpsid = strchr(tmpsid + 1, '-');
}
i -= 2; /* number of security ids plus one because we add the uid later */
/* allocate memory */
res = malloc(3 + 3 + 6 * 3 + i * 4 * 3 + 1);
if (res == NULL)
{
log_log(LOG_CRIT, "malloc() failed to allocate memory");
exit(1);
}
/* build the first part */
l = strtoul(sid + 2, &tmp, 10);
sprintf(res, "\\%02x\\%02x", (unsigned int)l & 0xff, (unsigned int)i);
/* build authority part (we only handle 32 of the 48 bits) */
l = strtoul(tmp + 1, &tmp, 10);
sprintf(res + strlen(res), "\\00\\00\\%02x\\%02x\\%02x\\%02x",
(unsigned int)((l >> 24) & 0xff),
(unsigned int)((l >> 16) & 0xff),
(unsigned int)((l >> 8) & 0xff),
(unsigned int)(l & 0xff));
/* go over the rest of the bits */
while (*tmp != '\0')
{
l = strtoul(tmp + 1, &tmp, 10);
sprintf(res + strlen(res), "\\%02x\\%02x\\%02x\\%02x",
(unsigned int)(l & 0xff),
(unsigned int)((l >> 8) & 0xff),
(unsigned int)((l >> 16) & 0xff),
(unsigned int)((l >> 24) & 0xff));
}
return res;
}
/* return the last security identifier of the binary sid */
unsigned long int binsid2id(const char *binsid)
{
int i;
/* find the position of the last security id */
i = 2 + 6 + ((((unsigned int)binsid[1]) & 0xff) - 1) * 4;
return (((unsigned long int)binsid[i]) & 0xff) |
((((unsigned long int)binsid[i + 1]) & 0xff) << 8) |
((((unsigned long int)binsid[i + 2]) & 0xff) << 16) |
((((unsigned long int)binsid[i + 3]) & 0xff) << 24);
}
#ifdef WANT_STRTOUI
/* provide a strtoui() implementation, similar to strtoul() but returning
an range-checked unsigned int instead */
unsigned int strtoui(const char *nptr, char **endptr, int base)
{
unsigned long val;
val = strtoul(nptr, endptr, base);
if (val > UINT_MAX)
{
errno = ERANGE;
return UINT_MAX;
}
/* If errno was set by strtoul, we'll pass it back as-is */
return (unsigned int)val;
}
#endif /* WANT_STRTOUI */
|