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 ret = ldb_global_init(); 49 if (ret != 0) { 50 goto failed; 51 } 52 53 _ldb_nss_ctx->ldb = ldb_init(_ldb_nss_ctx); 54 if (_ldb_nss_ctx->ldb == NULL) { 55 goto failed; 56 } 57 58 ret = ldb_connect(_ldb_nss_ctx->ldb, _LDB_NSS_URL, LDB_FLG_RDONLY, NULL); 59 if (ret != LDB_SUCCESS) { 60 goto failed; 61 } 62 63 _ldb_nss_ctx->base = ldb_dn_explode(_ldb_nss_ctx, _LDB_NSS_BASEDN); 64 if (_ldb_nss_ctx->base == NULL) { 65 goto failed; 66 } 67 68 _ldb_nss_ctx->pw_cur = 0; 69 _ldb_nss_ctx->pw_res = NULL; 70 _ldb_nss_ctx->gr_cur = 0; 71 _ldb_nss_ctx->gr_res = NULL; 72 73 return NSS_STATUS_SUCCESS; 74 75failed: 76 /* talloc_free(_ldb_nss_ctx); */ 77 _ldb_nss_ctx = NULL; 78 return NSS_STATUS_UNAVAIL; 79} 80 81NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result, 82 char *buffer, 83 int buflen, 84 int *errnop, 85 struct ldb_message *msg) 86{ 87 int len; 88 int bufpos; 89 const char *tmp; 90 91 bufpos = 0; 92 93 /* get username */ 94 tmp = ldb_msg_find_attr_as_string(msg, "uid", NULL); 95 if (tmp == NULL) { 96 /* this is a fatal error */ 97 *errnop = errno = ENOENT; 98 return NSS_STATUS_UNAVAIL; 99 } 100 len = strlen(tmp)+1; 101 if (bufpos + len > buflen) { 102 /* buffer too small */ 103 *errnop = errno = EAGAIN; 104 return NSS_STATUS_TRYAGAIN; 105 } 106 memcpy(&buffer[bufpos], tmp, len); 107 result->pw_name = &buffer[bufpos]; 108 bufpos += len; 109 110 /* get userPassword */ 111 tmp = ldb_msg_find_attr_as_string(msg, "userPassword", NULL); 112 if (tmp == NULL) { 113 tmp = "LDB"; 114 } 115 len = strlen(tmp)+1; 116 if (bufpos + len > buflen) { 117 /* buffer too small */ 118 *errnop = errno = EAGAIN; 119 return NSS_STATUS_TRYAGAIN; 120 } 121 memcpy(&buffer[bufpos], tmp, len); 122 result->pw_passwd = &buffer[bufpos]; 123 bufpos += len; 124 125 /* this backend never serves an uid 0 user */ 126 result->pw_uid = ldb_msg_find_attr_as_int(msg, "uidNumber", 0); 127 if (result->pw_uid == 0) { 128 /* this is a fatal error */ 129 *errnop = errno = ENOENT; 130 return NSS_STATUS_UNAVAIL; 131 } 132 133 result->pw_gid = ldb_msg_find_attr_as_int(msg, "gidNumber", 0); 134 if (result->pw_gid == 0) { 135 /* this is a fatal error */ 136 *errnop = errno = ENOENT; 137 return NSS_STATUS_UNAVAIL; 138 } 139 140 /* get gecos */ 141 tmp = ldb_msg_find_attr_as_string(msg, "gecos", NULL); 142 if (tmp == NULL) { 143 tmp = ""; 144 } 145 len = strlen(tmp)+1; 146 if (bufpos + len > buflen) { 147 /* buffer too small */ 148 *errnop = errno = EAGAIN; 149 return NSS_STATUS_TRYAGAIN; 150 } 151 memcpy(&buffer[bufpos], tmp, len); 152 result->pw_gecos = &buffer[bufpos]; 153 bufpos += len; 154 155 /* get homeDirectory */ 156 tmp = ldb_msg_find_attr_as_string(msg, "homeDirectory", NULL); 157 if (tmp == NULL) { 158 tmp = ""; 159 } 160 len = strlen(tmp)+1; 161 if (bufpos + len > buflen) { 162 /* buffer too small */ 163 *errnop = errno = EAGAIN; 164 return NSS_STATUS_TRYAGAIN; 165 } 166 memcpy(&buffer[bufpos], tmp, len); 167 result->pw_dir = &buffer[bufpos]; 168 bufpos += len; 169 170 /* get shell */ 171 tmp = ldb_msg_find_attr_as_string(msg, "loginShell", NULL); 172 if (tmp == NULL) { 173 tmp = ""; 174 } 175 len = strlen(tmp)+1; 176 if (bufpos + len > buflen) { 177 /* buffer too small */ 178 *errnop = errno = EAGAIN; 179 return NSS_STATUS_TRYAGAIN; 180 } 181 memcpy(&buffer[bufpos], tmp, len); 182 result->pw_shell = &buffer[bufpos]; 183 bufpos += len; 184 185 return NSS_STATUS_SUCCESS; 186} 187 188NSS_STATUS _ldb_nss_fill_group(struct group *result, 189 char *buffer, 190 int buflen, 191 int *errnop, 192 struct ldb_message *group, 193 struct ldb_result *members) 194{ 195 const char *tmp; 196 size_t len; 197 size_t bufpos; 198 size_t lsize; 199 int i; 200 201 bufpos = 0; 202 203 /* get group name */ 204 tmp = ldb_msg_find_attr_as_string(group, "cn", NULL); 205 if (tmp == NULL) { 206 /* this is a fatal error */ 207 *errnop = errno = ENOENT; 208 return NSS_STATUS_UNAVAIL; 209 } 210 len = strlen(tmp)+1; 211 if (bufpos + len > buflen) { 212 /* buffer too small */ 213 *errnop = errno = EAGAIN; 214 return NSS_STATUS_TRYAGAIN; 215 } 216 memcpy(&buffer[bufpos], tmp, len); 217 result->gr_name = &buffer[bufpos]; 218 bufpos += len; 219 220 /* get userPassword */ 221 tmp = ldb_msg_find_attr_as_string(group, "userPassword", NULL); 222 if (tmp == NULL) { 223 tmp = "LDB"; 224 } 225 len = strlen(tmp)+1; 226 if (bufpos + len > buflen) { 227 /* buffer too small */ 228 *errnop = errno = EAGAIN; 229 return NSS_STATUS_TRYAGAIN; 230 } 231 memcpy(&buffer[bufpos], tmp, len); 232 result->gr_passwd = &buffer[bufpos]; 233 bufpos += len; 234 235 result->gr_gid = ldb_msg_find_attr_as_int(group, "gidNumber", 0); 236 if (result->gr_gid == 0) { 237 /* this is a fatal error */ 238 *errnop = errno = ENOENT; 239 return NSS_STATUS_UNAVAIL; 240 } 241 242 /* check if there is enough memory for the list of pointers */ 243 lsize = (members->count + 1) * sizeof(char *); 244 245 /* align buffer on pointer boundary */ 246 bufpos += (sizeof(char*) - ((unsigned long)(buffer) % sizeof(char*))); 247 if ((buflen - bufpos) < lsize) { 248 /* buffer too small */ 249 *errnop = errno = EAGAIN; 250 return NSS_STATUS_TRYAGAIN; 251 } 252 253 result->gr_mem = (char **)&buffer[bufpos]; 254 bufpos += lsize; 255 256 for (i = 0; i < members->count; i++) { 257 tmp = ldb_msg_find_attr_as_string(members->msgs[i], "uid", NULL); 258 if (tmp == NULL) { 259 /* this is a fatal error */ 260 *errnop = errno = ENOENT; 261 return NSS_STATUS_UNAVAIL; 262 } 263 len = strlen(tmp)+1; 264 if (bufpos + len > buflen) { 265 /* buffer too small */ 266 *errnop = errno = EAGAIN; 267 return NSS_STATUS_TRYAGAIN; 268 } 269 memcpy(&buffer[bufpos], tmp, len); 270 result->gr_mem[i] = &buffer[bufpos]; 271 bufpos += len; 272 } 273 274 result->gr_mem[i] = NULL; 275 276 return NSS_STATUS_SUCCESS; 277} 278 279NSS_STATUS _ldb_nss_fill_initgr(gid_t group, 280 long int limit, 281 long int *start, 282 long int *size, 283 gid_t **groups, 284 int *errnop, 285 struct ldb_result *grlist) 286{ 287 NSS_STATUS ret; 288 int i; 289 290 for (i = 0; i < grlist->count; i++) { 291 292 if (limit && (*start > limit)) { 293 /* TODO: warn no all groups were reported */ 294 *errnop = 0; 295 ret = NSS_STATUS_SUCCESS; 296 goto done; 297 } 298 299 if (*start == *size) { 300 /* buffer full, enlarge it */ 301 long int gs; 302 gid_t *gm; 303 304 gs = (*size) + 32; 305 if (limit && (gs > limit)) { 306 gs = limit; 307 } 308 309 gm = (gid_t *)realloc((*groups), gs * sizeof(gid_t)); 310 if ( ! gm) { 311 *errnop = ENOMEM; 312 ret = NSS_STATUS_UNAVAIL; 313 goto done; 314 } 315 316 *groups = gm; 317 *size = gs; 318 } 319 320 (*groups)[*start] = ldb_msg_find_attr_as_int(grlist->msgs[i], "gidNumber", 0); 321 if ((*groups)[*start] == 0 || (*groups)[*start] == group) { 322 /* skip root group or primary group */ 323 continue; 324 } 325 (*start)++; 326 327 } 328 329 *errnop = 0; 330 ret = NSS_STATUS_SUCCESS; 331done: 332 return ret; 333} 334 335#define _LDB_NSS_ALLOC_CHECK(mem) do { if (!mem) { errno = ENOMEM; return NSS_STATUS_UNAVAIL; } } while(0) 336 337NSS_STATUS _ldb_nss_group_request(struct ldb_result **_res, 338 struct ldb_dn *group_dn, 339 const char * const *attrs, 340 const char *mattr) 341{ 342 struct ldb_control **ctrls; 343 struct ldb_control *ctrl; 344 struct ldb_asq_control *asqc; 345 struct ldb_request *req; 346 int ret; 347 struct ldb_result *res = *_res; 348 349 ctrls = talloc_array(res, struct ldb_control *, 2); 350 _LDB_NSS_ALLOC_CHECK(ctrls); 351 352 ctrl = talloc(ctrls, struct ldb_control); 353 _LDB_NSS_ALLOC_CHECK(ctrl); 354 355 asqc = talloc(ctrl, struct ldb_asq_control); 356 _LDB_NSS_ALLOC_CHECK(asqc); 357 358 asqc->source_attribute = talloc_strdup(asqc, mattr); 359 _LDB_NSS_ALLOC_CHECK(asqc->source_attribute); 360 361 asqc->request = 1; 362 asqc->src_attr_len = strlen(asqc->source_attribute); 363 ctrl->oid = LDB_CONTROL_ASQ_OID; 364 ctrl->critical = 1; 365 ctrl->data = asqc; 366 ctrls[0] = ctrl; 367 ctrls[1] = NULL; 368 369 ret = ldb_build_search_req( 370 &req, 371 _ldb_nss_ctx->ldb, 372 res, 373 group_dn, 374 LDB_SCOPE_BASE, 375 "(objectClass=*)", 376 attrs, 377 ctrls, 378 res, 379 ldb_search_default_callback); 380 381 if (ret != LDB_SUCCESS) { 382 errno = ENOENT; 383 return NSS_STATUS_UNAVAIL; 384 } 385 386 ldb_set_timeout(_ldb_nss_ctx->ldb, req, 0); 387 388 ret = ldb_request(_ldb_nss_ctx->ldb, req); 389 390 if (ret == LDB_SUCCESS) { 391 ret = ldb_wait(req->handle, LDB_WAIT_ALL); 392 } else { 393 talloc_free(req); 394 return NSS_STATUS_UNAVAIL; 395 } 396 397 talloc_free(req); 398 return NSS_STATUS_SUCCESS; 399} 400 401