1/* 2 LDB nsswitch module 3 4 Copyright (C) Simo Sorce 2006 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 3 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Library General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18*/ 19 20#include "ldb-nss.h" 21 22struct _ldb_nss_context *_ldb_nss_ctx = NULL; 23 24NSS_STATUS _ldb_nss_init(void) 25{ 26 int ret; 27 28 pid_t mypid = getpid(); 29 30 if (_ldb_nss_ctx != NULL) { 31 if (_ldb_nss_ctx->pid == mypid) { 32 /* already initialized */ 33 return NSS_STATUS_SUCCESS; 34 } else { 35 /* we are in a forked child now, reinitialize */ 36 talloc_free(_ldb_nss_ctx); 37 _ldb_nss_ctx = NULL; 38 } 39 } 40 41 _ldb_nss_ctx = talloc_named(NULL, 0, "_ldb_nss_ctx(%u)", mypid); 42 if (_ldb_nss_ctx == NULL) { 43 return NSS_STATUS_UNAVAIL; 44 } 45 46 _ldb_nss_ctx->pid = mypid; 47 48 _ldb_nss_ctx->ldb = ldb_init(_ldb_nss_ctx, NULL); 49 if (_ldb_nss_ctx->ldb == NULL) { 50 goto failed; 51 } 52 53 ret = ldb_connect(_ldb_nss_ctx->ldb, _LDB_NSS_URL, LDB_FLG_RDONLY, NULL); 54 if (ret != LDB_SUCCESS) { 55 goto failed; 56 } 57 58 _ldb_nss_ctx->base = ldb_dn_new(_ldb_nss_ctx, _ldb_nss_ctx->ldb, _LDB_NSS_BASEDN); 59 if ( ! ldb_dn_validate(_ldb_nss_ctx->base)) { 60 goto failed; 61 } 62 63 _ldb_nss_ctx->pw_cur = 0; 64 _ldb_nss_ctx->pw_res = NULL; 65 _ldb_nss_ctx->gr_cur = 0; 66 _ldb_nss_ctx->gr_res = NULL; 67 68 return NSS_STATUS_SUCCESS; 69 70failed: 71 /* talloc_free(_ldb_nss_ctx); */ 72 _ldb_nss_ctx = NULL; 73 return NSS_STATUS_UNAVAIL; 74} 75 76NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result, 77 char *buffer, 78 int buflen, 79 int *errnop, 80 struct ldb_message *msg) 81{ 82 int len; 83 int bufpos; 84 const char *tmp; 85 86 bufpos = 0; 87 88 /* get username */ 89 tmp = ldb_msg_find_attr_as_string(msg, "uid", NULL); 90 if (tmp == NULL) { 91 /* this is a fatal error */ 92 *errnop = errno = ENOENT; 93 return NSS_STATUS_UNAVAIL; 94 } 95 len = strlen(tmp)+1; 96 if (bufpos + len > buflen) { 97 /* buffer too small */ 98 *errnop = errno = EAGAIN; 99 return NSS_STATUS_TRYAGAIN; 100 } 101 memcpy(&buffer[bufpos], tmp, len); 102 result->pw_name = &buffer[bufpos]; 103 bufpos += len; 104 105 /* get userPassword */ 106 tmp = ldb_msg_find_attr_as_string(msg, "userPassword", NULL); 107 if (tmp == NULL) { 108 tmp = "LDB"; 109 } 110 len = strlen(tmp)+1; 111 if (bufpos + len > buflen) { 112 /* buffer too small */ 113 *errnop = errno = EAGAIN; 114 return NSS_STATUS_TRYAGAIN; 115 } 116 memcpy(&buffer[bufpos], tmp, len); 117 result->pw_passwd = &buffer[bufpos]; 118 bufpos += len; 119 120 /* this backend never serves an uid 0 user */ 121 result->pw_uid = ldb_msg_find_attr_as_int(msg, "uidNumber", 0); 122 if (result->pw_uid == 0) { 123 /* this is a fatal error */ 124 *errnop = errno = ENOENT; 125 return NSS_STATUS_UNAVAIL; 126 } 127 128 result->pw_gid = ldb_msg_find_attr_as_int(msg, "gidNumber", 0); 129 if (result->pw_gid == 0) { 130 /* this is a fatal error */ 131 *errnop = errno = ENOENT; 132 return NSS_STATUS_UNAVAIL; 133 } 134 135 /* get gecos */ 136 tmp = ldb_msg_find_attr_as_string(msg, "gecos", NULL); 137 if (tmp == NULL) { 138 tmp = ""; 139 } 140 len = strlen(tmp)+1; 141 if (bufpos + len > buflen) { 142 /* buffer too small */ 143 *errnop = errno = EAGAIN; 144 return NSS_STATUS_TRYAGAIN; 145 } 146 memcpy(&buffer[bufpos], tmp, len); 147 result->pw_gecos = &buffer[bufpos]; 148 bufpos += len; 149 150 /* get homeDirectory */ 151 tmp = ldb_msg_find_attr_as_string(msg, "homeDirectory", NULL); 152 if (tmp == NULL) { 153 tmp = ""; 154 } 155 len = strlen(tmp)+1; 156 if (bufpos + len > buflen) { 157 /* buffer too small */ 158 *errnop = errno = EAGAIN; 159 return NSS_STATUS_TRYAGAIN; 160 } 161 memcpy(&buffer[bufpos], tmp, len); 162 result->pw_dir = &buffer[bufpos]; 163 bufpos += len; 164 165 /* get shell */ 166 tmp = ldb_msg_find_attr_as_string(msg, "loginShell", NULL); 167 if (tmp == NULL) { 168 tmp = ""; 169 } 170 len = strlen(tmp)+1; 171 if (bufpos + len > buflen) { 172 /* buffer too small */ 173 *errnop = errno = EAGAIN; 174 return NSS_STATUS_TRYAGAIN; 175 } 176 memcpy(&buffer[bufpos], tmp, len); 177 result->pw_shell = &buffer[bufpos]; 178 bufpos += len; 179 180 return NSS_STATUS_SUCCESS; 181} 182 183NSS_STATUS _ldb_nss_fill_group(struct group *result, 184 char *buffer, 185 int buflen, 186 int *errnop, 187 struct ldb_message *group, 188 struct ldb_result *members) 189{ 190 const char *tmp; 191 size_t len; 192 size_t bufpos; 193 size_t lsize; 194 int i; 195 196 bufpos = 0; 197 198 /* get group name */ 199 tmp = ldb_msg_find_attr_as_string(group, "cn", NULL); 200 if (tmp == NULL) { 201 /* this is a fatal error */ 202 *errnop = errno = ENOENT; 203 return NSS_STATUS_UNAVAIL; 204 } 205 len = strlen(tmp)+1; 206 if (bufpos + len > buflen) { 207 /* buffer too small */ 208 *errnop = errno = EAGAIN; 209 return NSS_STATUS_TRYAGAIN; 210 } 211 memcpy(&buffer[bufpos], tmp, len); 212 result->gr_name = &buffer[bufpos]; 213 bufpos += len; 214 215 /* get userPassword */ 216 tmp = ldb_msg_find_attr_as_string(group, "userPassword", NULL); 217 if (tmp == NULL) { 218 tmp = "LDB"; 219 } 220 len = strlen(tmp)+1; 221 if (bufpos + len > buflen) { 222 /* buffer too small */ 223 *errnop = errno = EAGAIN; 224 return NSS_STATUS_TRYAGAIN; 225 } 226 memcpy(&buffer[bufpos], tmp, len); 227 result->gr_passwd = &buffer[bufpos]; 228 bufpos += len; 229 230 result->gr_gid = ldb_msg_find_attr_as_int(group, "gidNumber", 0); 231 if (result->gr_gid == 0) { 232 /* this is a fatal error */ 233 *errnop = errno = ENOENT; 234 return NSS_STATUS_UNAVAIL; 235 } 236 237 /* check if there is enough memory for the list of pointers */ 238 lsize = (members->count + 1) * sizeof(char *); 239 240 /* align buffer on pointer boundary */ 241 bufpos += (sizeof(char*) - ((unsigned long)(buffer) % sizeof(char*))); 242 if ((buflen - bufpos) < lsize) { 243 /* buffer too small */ 244 *errnop = errno = EAGAIN; 245 return NSS_STATUS_TRYAGAIN; 246 } 247 248 result->gr_mem = (char **)&buffer[bufpos]; 249 bufpos += lsize; 250 251 for (i = 0; i < members->count; i++) { 252 tmp = ldb_msg_find_attr_as_string(members->msgs[i], "uid", NULL); 253 if (tmp == NULL) { 254 /* this is a fatal error */ 255 *errnop = errno = ENOENT; 256 return NSS_STATUS_UNAVAIL; 257 } 258 len = strlen(tmp)+1; 259 if (bufpos + len > buflen) { 260 /* buffer too small */ 261 *errnop = errno = EAGAIN; 262 return NSS_STATUS_TRYAGAIN; 263 } 264 memcpy(&buffer[bufpos], tmp, len); 265 result->gr_mem[i] = &buffer[bufpos]; 266 bufpos += len; 267 } 268 269 result->gr_mem[i] = NULL; 270 271 return NSS_STATUS_SUCCESS; 272} 273 274NSS_STATUS _ldb_nss_fill_initgr(gid_t group, 275 long int limit, 276 long int *start, 277 long int *size, 278 gid_t **groups, 279 int *errnop, 280 struct ldb_result *grlist) 281{ 282 NSS_STATUS ret; 283 int i; 284 285 for (i = 0; i < grlist->count; i++) { 286 287 if (limit && (*start > limit)) { 288 /* TODO: warn no all groups were reported */ 289 *errnop = 0; 290 ret = NSS_STATUS_SUCCESS; 291 goto done; 292 } 293 294 if (*start == *size) { 295 /* buffer full, enlarge it */ 296 long int gs; 297 gid_t *gm; 298 299 gs = (*size) + 32; 300 if (limit && (gs > limit)) { 301 gs = limit; 302 } 303 304 gm = (gid_t *)realloc((*groups), gs * sizeof(gid_t)); 305 if ( ! gm) { 306 *errnop = ENOMEM; 307 ret = NSS_STATUS_UNAVAIL; 308 goto done; 309 } 310 311 *groups = gm; 312 *size = gs; 313 } 314 315 (*groups)[*start] = ldb_msg_find_attr_as_int(grlist->msgs[i], "gidNumber", 0); 316 if ((*groups)[*start] == 0 || (*groups)[*start] == group) { 317 /* skip root group or primary group */ 318 continue; 319 } 320 (*start)++; 321 322 } 323 324 *errnop = 0; 325 ret = NSS_STATUS_SUCCESS; 326done: 327 return ret; 328} 329 330#define _LDB_NSS_ALLOC_CHECK(mem) do { if (!mem) { errno = ENOMEM; return NSS_STATUS_UNAVAIL; } } while(0) 331 332NSS_STATUS _ldb_nss_group_request(struct ldb_result **_res, 333 struct ldb_dn *group_dn, 334 const char * const *attrs, 335 const char *mattr) 336{ 337 struct ldb_control **ctrls; 338 struct ldb_control *ctrl; 339 struct ldb_asq_control *asqc; 340 struct ldb_request *req; 341 int ret; 342 struct ldb_result *res = *_res; 343 344 ctrls = talloc_array(res, struct ldb_control *, 2); 345 _LDB_NSS_ALLOC_CHECK(ctrls); 346 347 ctrl = talloc(ctrls, struct ldb_control); 348 _LDB_NSS_ALLOC_CHECK(ctrl); 349 350 asqc = talloc(ctrl, struct ldb_asq_control); 351 _LDB_NSS_ALLOC_CHECK(asqc); 352 353 asqc->source_attribute = talloc_strdup(asqc, mattr); 354 _LDB_NSS_ALLOC_CHECK(asqc->source_attribute); 355 356 asqc->request = 1; 357 asqc->src_attr_len = strlen(asqc->source_attribute); 358 ctrl->oid = LDB_CONTROL_ASQ_OID; 359 ctrl->critical = 1; 360 ctrl->data = asqc; 361 ctrls[0] = ctrl; 362 ctrls[1] = NULL; 363 364 ret = ldb_build_search_req( 365 &req, 366 _ldb_nss_ctx->ldb, 367 res, 368 group_dn, 369 LDB_SCOPE_BASE, 370 "(objectClass=*)", 371 attrs, 372 ctrls, 373 res, 374 ldb_search_default_callback); 375 376 if (ret != LDB_SUCCESS) { 377 errno = ENOENT; 378 return NSS_STATUS_UNAVAIL; 379 } 380 381 ldb_set_timeout(_ldb_nss_ctx->ldb, req, 0); 382 383 ret = ldb_request(_ldb_nss_ctx->ldb, req); 384 385 if (ret == LDB_SUCCESS) { 386 ret = ldb_wait(req->handle, LDB_WAIT_ALL); 387 } else { 388 talloc_free(req); 389 return NSS_STATUS_UNAVAIL; 390 } 391 392 talloc_free(req); 393 return NSS_STATUS_SUCCESS; 394} 395 396