]> git.saurik.com Git - apple/system_cmds.git/blame_incremental - chpass.tproj/directory_service.c
system_cmds-336.23.tar.gz
[apple/system_cmds.git] / chpass.tproj / directory_service.c
... / ...
CommitLineData
1#ifdef DIRECTORY_SERVICE
2
3#include "directory_service.h"
4#include "chpass.h"
5#include <err.h>
6#include <sys/time.h>
7
8#include <sys/errno.h>
9extern int errno;
10
11#define CONFIGNAMELEN 14
12#define GLOBALCONFIGLEN 20
13#define LOOKUPORDERLEN 13
14#define LINESIZE 128
15#define NETINFOROOTLEN 13
16#define NLIST 13
17#define REMOTEINFOLEN 9
18#define USERCONFIGLEN 18
19
20/*---------------------------------------------------------------------------
21 * Global variables
22 *---------------------------------------------------------------------------*/
23char *DSPath = NULL;
24const char MasterPasswd[] = "/etc/master.passwd";
25
26/*---------------------------------------------------------------------------
27 * Local variables
28 *---------------------------------------------------------------------------*/
29static char Agent[] = "Agent";
30static char ConfigName[] = "_config_name: ";
31static char DSFiles[] = "/BSD/local";
32static char FFPatFmt[] = "^%s:";
33static char GlobalConfig[] = "Global Configuration";
34static char LocalNI[] = "/NetInfo/DefaultLocalNode";
35static char LookupOrder[] = "LookupOrder: ";
36static char LookupOrderSep[] = " ";
37static char NetinfoRoot[] = "/NetInfo/root";
38static char NiclPathFmt[] = "/users/%s";
39static char NISPatFmt[] = "/usr/bin/ypcat passwd.byname | /usr/bin/grep -q '^%s:'";
40static char RemoteNI[] = "/NetInfo/";
41static char UserConfig[] = "User Configuration";
42static unsigned char RestrictedFFRoot[] = {
43 0, /*E_LOGIN */
44 0, /*E_PASSWD */
45 0, /*E_UID */
46 0, /*E_GID */
47 0, /*E_CHANGE */
48 0, /*E_EXPIRE */
49 0, /*E_CLASS */
50 0, /*E_HOME */
51 0, /*E_SHELL */
52 0, /*E_NAME */
53 1, /*E_LOCATE */
54 1, /*E_BPHONE */
55 1, /*E_HPHONE */
56};
57static unsigned char RestrictedFFUser[] = {
58 1, /*E_LOGIN */
59 1, /*E_PASSWD */
60 1, /*E_UID */
61 1, /*E_GID */
62 1, /*E_CHANGE */
63 1, /*E_EXPIRE */
64 1, /*E_CLASS */
65 1, /*E_HOME */
66 0, /*E_SHELL */
67 0, /*E_NAME */
68 1, /*E_LOCATE */
69 1, /*E_BPHONE */
70 1, /*E_HPHONE */
71};
72static unsigned char RestrictedLocalNIRoot[] = {
73 0, /*E_LOGIN */
74 1, /*E_PASSWD */
75 0, /*E_UID */
76 0, /*E_GID */
77 0, /*E_CHANGE */
78 0, /*E_EXPIRE */
79 0, /*E_CLASS */
80 0, /*E_HOME */
81 0, /*E_SHELL */
82 0, /*E_NAME */
83 1, /*E_LOCATE */
84 1, /*E_BPHONE */
85 1, /*E_HPHONE */
86};
87static unsigned char RestrictedLocalNIUser[] = {
88 1, /*E_LOGIN */
89 1, /*E_PASSWD */
90 1, /*E_UID */
91 1, /*E_GID */
92 1, /*E_CHANGE */
93 1, /*E_EXPIRE */
94 1, /*E_CLASS */
95 1, /*E_HOME */
96 0, /*E_SHELL */
97 0, /*E_NAME */
98 1, /*E_LOCATE */
99 1, /*E_BPHONE */
100 1, /*E_HPHONE */
101};
102
103#define NWHERE 4
104
105typedef int (*wherefunc)(const char *);
106
107static int compar(const void *, const void *);
108static int runnicl(char *name, char *key, char *val);
109static int whereCache(const char *);
110static int whereDS(const char *);
111static int whereFF(const char *);
112static int whereNI(const char *);
113static int whereNIL(const char *);
114static int whereNIS(const char *);
115
116/*---------------------------------------------------------------------------
117 * WhereList determines what functions to call when the LookupOrder is followed
118 *---------------------------------------------------------------------------*/
119struct where {
120 char *agent;
121 int len;
122 wherefunc func;
123} WhereList[] = {
124 {"Cache", 5, whereCache},
125 {"DS", 2, whereDS},
126 {"FF", 2, whereFF},
127 {"NI", 2, whereNI},
128 {"NIL", 3, whereNIL},
129 {"NIS", 3, whereNIS},
130};
131
132#define PATINDEX 2
133static char *Grep[] = {
134 "/usr/bin/grep",
135 "-q",
136 NULL, /* pattern goes here */
137 (char *)MasterPasswd,
138 NULL
139};
140
141#define NICLPATHINDEX 3
142#define NICLKEYINDEX 4
143#define NICLVALUEINDEX 5
144static char *Nicl[] = {
145 "/usr/bin/nicl",
146 ".",
147 "-create",
148 NULL, /* path goes here */
149 NULL, /* key goes here */
150 NULL, /* value goes here */
151 NULL
152};
153
154#define YPCATINDEX 2
155static char *Ypcat[] = {
156 "/bin/sh",
157 "-c",
158 NULL, /* ypcat cmd goes here */
159 NULL
160};
161
162/*---------------------------------------------------------------------------
163 * compar - called by bsearch() to search WhereList for an agent
164 *---------------------------------------------------------------------------*/
165#define A ((const struct where *)a)
166#define KEY ((const char *)key)
167static int
168compar(const void *key, const void *a)
169{
170 int result = strncmp(KEY, A->agent, A->len);
171 if(result)
172 return result;
173 if(KEY[A->len] == 0)
174 return 0;
175 return strcmp(KEY + A->len, Agent);
176}
177#undef KEY
178#undef A
179
180/*---------------------------------------------------------------------------
181 * runnicl - run the nicl command to update local netinfo fields
182 *---------------------------------------------------------------------------*/
183static int
184runnicl(char *name, char *key, char *val)
185{
186 char path[128];
187 pid_t pid;
188 int estat;
189 int status;
190
191 IF((pid = fork()) >= 0) {
192 if(pid == 0) {
193 sprintf(path, NiclPathFmt, name);
194 Nicl[NICLPATHINDEX] = path;
195 Nicl[NICLKEYINDEX] = key;
196 Nicl[NICLVALUEINDEX] = val;
197 /*---------------------------------------------------------------
198 * Become fully root to call nicl
199 *---------------------------------------------------------------*/
200 setuid(geteuid());
201 execv(Nicl[0], Nicl);
202 _exit(1);
203 }
204 if(waitpid(pid, &estat, 0) < 0) {
205 status = errno;
206 break;
207 }
208 if(!WIFEXITED(estat)) {
209 status = E_NICLFAILED;
210 break;
211 }
212 status = (WEXITSTATUS(estat) == 0 ? 0 : E_NICLFAILED);
213 } CLEANUP {
214 } ELSE {
215 status = errno;
216 } ENDIF
217 return status;
218}
219/*---------------------------------------------------------------------------
220 * PUBLIC setrestricted - sets the restricted flag
221 *---------------------------------------------------------------------------*/
222void
223setrestricted(int where, struct passwd *pw)
224{
225 unsigned char *restricted;
226 int i;
227 ENTRY *ep;
228
229 switch(where)
230 {
231 case WHERE_FILES:
232 restricted = uid ? RestrictedFFUser : RestrictedFFRoot;
233 break;
234 case WHERE_LOCALNI:
235 restricted = uid ? RestrictedLocalNIUser : RestrictedLocalNIRoot;
236 break;
237 default:
238 return;
239 }
240
241 for (ep = list, i = NLIST; i > 0; i--)
242 (ep++)->restricted = *restricted++;
243
244 if (uid && !ok_shell(pw->pw_shell))
245 list[E_SHELL].restricted = 1;
246}
247
248/*---------------------------------------------------------------------------
249 * PUBLIC update_local_ni - update local netinfo
250 *---------------------------------------------------------------------------*/
251void
252update_local_ni(struct passwd *pworig, struct passwd *pw)
253{
254 char buf[64];
255 char *np, *op, *bp;
256
257 if(pworig->pw_uid != pw->pw_uid) {
258 sprintf(buf, "%d", pw->pw_uid);
259 runnicl(pworig->pw_name, "uid", buf);
260 }
261 if(pworig->pw_gid != pw->pw_gid) {
262 sprintf(buf, "%d", pw->pw_gid);
263 runnicl(pworig->pw_name, "gid", buf);
264 }
265 if(pworig->pw_change != pw->pw_change) {
266 sprintf(buf, "%lu", pw->pw_change);
267 runnicl(pworig->pw_name, "change", buf);
268 }
269 if(pworig->pw_expire != pw->pw_expire) {
270 sprintf(buf, "%lu", pw->pw_expire);
271 runnicl(pworig->pw_name, "expire", buf);
272 }
273 if(strcmp(pworig->pw_dir, pw->pw_dir) != 0)
274 runnicl(pworig->pw_name, "home", pw->pw_dir);
275 if(strcmp(pworig->pw_shell, pw->pw_shell) != 0)
276 runnicl(pworig->pw_name, "shell", pw->pw_shell);
277 if(strcmp(pworig->pw_class, pw->pw_class) != 0)
278 runnicl(pworig->pw_name, "class", pw->pw_class);
279
280 bp = pworig->pw_gecos;
281 op = strsep(&bp, ",");
282 if(!op)
283 op = "";
284 bp = pw->pw_gecos;
285 np = strsep(&bp, ",");
286 if(!np)
287 np = "";
288 if(strcmp(op, np) != 0)
289 runnicl(pworig->pw_name, "realname", np);
290
291 if(strcmp(pworig->pw_name, pw->pw_name) != 0)
292 runnicl(pworig->pw_name, "name", pw->pw_name);
293
294 warnx("netinfo domain \"%s\" updated", DSPath);
295}
296
297/*---------------------------------------------------------------------------
298 * whereCache - we skip the cache
299 *---------------------------------------------------------------------------*/
300static int
301whereCache(const char *name)
302{
303 return E_NOTFOUND;
304}
305
306/*---------------------------------------------------------------------------
307 * whereDS - call DirectoryService. This does both netinfo and other directory
308 * services, so we cache the value so we only process once.
309 *---------------------------------------------------------------------------*/
310static int
311whereDS(const char *name)
312{
313 tDirReference dsRef;
314 static tDirStatus status;
315 static int dsCached = 0;
316
317 if(dsCached)
318 return status;
319 dsCached = 1;
320 IF((status = dsOpenDirService(&dsRef)) == eDSNoErr) {
321 tDataBuffer *dataBuff;
322
323 IF((dataBuff = dsDataBufferAllocate(dsRef, 4096)) != NULL) {
324 tContextData context = NULL;
325 unsigned long nodeCount;
326
327 /*---------------------------------------------------------------
328 * Find and open the search node.
329 *---------------------------------------------------------------*/
330 IF((status = dsFindDirNodes(dsRef, dataBuff, NULL,
331 eDSAuthenticationSearchNodeName, &nodeCount, &context))
332 == eDSNoErr)
333 {
334 tDataListPtr nodeName;
335 if(nodeCount < 1) {
336 status = eDSNodeNotFound;
337 break;
338 }
339 nodeName = NULL;
340 IF((status = dsGetDirNodeName(dsRef, dataBuff, 1, &nodeName)) == eDSNoErr)
341 {
342 tDirNodeReference nodeRef;
343
344 IF((status = dsOpenDirNode(dsRef, nodeName, &nodeRef)) == eDSNoErr) {
345 tDataListPtr pRecType;
346 tDataListPtr pAttrType;
347 tDataListPtr pPattern;
348 unsigned long recCount;
349 tContextData context2 = NULL;
350
351 /*---------------------------------------------------
352 * Now search the search node for the given user name.
353 *---------------------------------------------------*/
354 pRecType = dsBuildListFromStrings(dsRef,
355 kDSStdRecordTypeUsers, NULL);
356 pAttrType = dsBuildListFromStrings(dsRef,
357 kDSNAttrMetaNodeLocation, NULL);
358 pPattern = dsBuildListFromStrings(dsRef, name, NULL);
359 IF((status = dsGetRecordList(nodeRef, dataBuff,
360 pPattern, eDSExact, pRecType, pAttrType, 0, &recCount,
361 &context2)) == eDSNoErr) {
362 tAttributeListRef attrListRef;
363 tRecordEntry *pRecEntry;
364
365 if(recCount < 1) {
366 status = E_NOTFOUND;
367 break;
368 }
369 /*-----------------------------------------------
370 * Get the attributes for the first entry we find
371 *-----------------------------------------------*/
372 IF((status = dsGetRecordEntry(nodeRef,
373 dataBuff, 1, &attrListRef, &pRecEntry)) ==
374 eDSNoErr) {
375 tAttributeValueListRef valueRef;
376 tAttributeEntry *pAttrEntry;
377
378 /*-------------------------------------------
379 * Get the first (only) attribute
380 *-------------------------------------------*/
381 IF((status = dsGetAttributeEntry( nodeRef, dataBuff, attrListRef, 1, &valueRef,
382 &pAttrEntry)) == eDSNoErr)
383 {
384 tAttributeValueEntry *pValueEntry;
385
386 /*---------------------------------------
387 * Put the attribute values into a data
388 * list.
389 *---------------------------------------*/
390
391 status = dsGetAttributeValue(nodeRef, dataBuff, 1, valueRef, &pValueEntry);
392 if ( status == eDSNoErr )
393 {
394 DSPath = (char *) malloc( pValueEntry->fAttributeValueData.fBufferLength + 1 );
395 if ( DSPath != NULL )
396 strlcpy( DSPath, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength + 1 );
397
398 dsDeallocAttributeValueEntry(dsRef, pValueEntry);
399 }
400
401 if(status != eDSNoErr)
402 break;
403
404 if(strcmp(DSPath, LocalNI) == 0)
405 {
406 status = WHERE_LOCALNI;
407 /*---------------------------
408 * Translate to netinfo path
409 *---------------------------*/
410 free((void *)DSPath);
411 DSPath = strdup(".");
412 }
413 else if(strcmp(DSPath, DSFiles) == 0)
414 {
415 status = WHERE_FILES;
416 /*---------------------------
417 * Translate to master.passwd
418 * path
419 *---------------------------*/
420 free((void *)DSPath);
421 DSPath = strdup(MasterPasswd);
422 }
423 else if(strncmp(DSPath, RemoteNI, REMOTEINFOLEN) == 0)
424 {
425 status = WHERE_REMOTENI;
426 /*---------------------------
427 * Translate to netinfo path
428 *---------------------------*/
429 if(strncmp(DSPath, NetinfoRoot,
430 NETINFOROOTLEN) == 0) {
431 if(DSPath[NETINFOROOTLEN]
432 == 0) {
433 free((void *)DSPath);
434 DSPath = strdup("/");
435 } else {
436 char *tmp =
437 strdup(DSPath +
438 NETINFOROOTLEN);
439 free((void *)DSPath);
440 DSPath = tmp;
441 }
442 }
443 }
444 else
445 {
446 status = WHERE_DS;
447 }
448 } CLEANUP {
449 dsCloseAttributeValueList(valueRef);
450 dsDeallocAttributeEntry(dsRef, pAttrEntry);
451 } ELSE {
452 } ENDIF
453 } CLEANUP {
454 dsCloseAttributeList(attrListRef);
455 dsDeallocRecordEntry(dsRef, pRecEntry);
456 } ENDIF
457 } CLEANUP {
458 if(context2)
459 dsReleaseContinueData(dsRef, context2);
460 } ENDIF
461 dsDataListDeallocate(dsRef, pRecType);
462 free(pRecType);
463 dsDataListDeallocate(dsRef, pAttrType);
464 free(pAttrType);
465 dsDataListDeallocate(dsRef, pPattern);
466 free(pPattern);
467 } CLEANUP {
468 dsCloseDirNode(nodeRef);
469 } ENDIF
470 } CLEANUP {
471 dsDataListDeallocate(dsRef, nodeName);
472 } ENDIF
473 } CLEANUP {
474 if(context)
475 dsReleaseContinueData(dsRef, context);
476 } ENDIF
477 } CLEANUP {
478 dsDataBufferDeAllocate(dsRef, dataBuff);
479 } ELSE {
480 status = eMemoryAllocError;
481 } ENDIF
482 } CLEANUP {
483 dsCloseDirService(dsRef);
484 } ENDIF
485 return status;
486}
487
488/*---------------------------------------------------------------------------
489 * whereFF - check the flat file (/etc/master.passwd)
490 *---------------------------------------------------------------------------*/
491static int
492whereFF(const char *name)
493{
494 pid_t pid;
495 int estat;
496 int status;
497
498 IF((pid = fork()) >= 0) {
499 if(pid == 0) {
500 char pat[64];
501
502 sprintf(pat, FFPatFmt, name);
503 Grep[PATINDEX] = pat;
504 /*---------------------------------------------------------------
505 * Become fully root to read /etc/master.passwd
506 *---------------------------------------------------------------*/
507 setuid(geteuid());
508 execv(Grep[0], Grep);
509 _exit(1);
510 }
511 if(waitpid(pid, &estat, 0) < 0) {
512 status = errno;
513 break;
514 }
515 if(!WIFEXITED(estat)) {
516 status = E_CHILDFAILED;
517 break;
518 }
519 status = (WEXITSTATUS(estat) == 0 ? WHERE_FILES : E_NOTFOUND);
520 } CLEANUP {
521 } ELSE {
522 status = errno;
523 } ENDIF
524 return status;
525}
526
527/*---------------------------------------------------------------------------
528 * whereNI - call whereDS to do the work, then the entry is found in directory
529 * service (and not netinfo), mark as not found.
530 *---------------------------------------------------------------------------*/
531static int
532whereNI(const char *name)
533{
534 int status = whereDS(name);
535
536 if(status == WHERE_DS)
537 status = E_NOTFOUND;
538 return status;
539}
540
541/*---------------------------------------------------------------------------
542 * whereNIL - we skip the NILAgent
543 *---------------------------------------------------------------------------*/
544static int
545whereNIL(const char *name)
546{
547 return E_NOTFOUND;
548}
549
550/*---------------------------------------------------------------------------
551 * whereNIS - check NIS passwd.byname
552 *---------------------------------------------------------------------------*/
553static int
554whereNIS(const char *name)
555{
556 pid_t pid;
557 int estat;
558 int status;
559
560 IF((pid = fork()) >= 0) {
561 if(pid == 0) {
562 char cmd[256];
563
564 sprintf(cmd, NISPatFmt, name);
565 Ypcat[YPCATINDEX] = cmd;
566 execv(Ypcat[0], Ypcat);
567 _exit(1);
568 }
569 if(waitpid(pid, &estat, 0) < 0) {
570 status = errno;
571 break;
572 }
573 if(!WIFEXITED(estat)) {
574 status = E_CHILDFAILED;
575 break;
576 }
577 status = (WEXITSTATUS(estat) == 0 ? WHERE_NIS : E_NOTFOUND);
578 } CLEANUP {
579 } ELSE {
580 status = errno;
581 } ENDIF
582 return status;
583}
584
585/*---------------------------------------------------------------------------
586 * PUBLIC wherepwent - Given a const char *, determine lookupd's LookupOrder
587 * and then search for the corresponding record for each agent.
588 *---------------------------------------------------------------------------*/
589int
590wherepwent(const char *name)
591{
592 char user[LINESIZE];
593 char *cp, *str;
594 struct where *w;
595 FILE *fp = NULL;
596 int status = 0;
597 fd_set fdset;
598 struct timeval selectTimeout = { 2, 0 };
599 int result;
600 char order[LINESIZE], line[LINESIZE];
601 char *task_argv[3] = {NULL};
602 int readPipe = -1;
603 int writePipe = -1;
604
605 /*-------------------------------------------------------------------
606 * Save the first LookupOrder as the global setting. We make sure
607 * that the first _config_name is Global Configuration.
608 *-------------------------------------------------------------------*/
609
610 do
611 {
612 task_argv[0] = "/usr/sbin/lookupd";
613 task_argv[1] = "-configuration";
614 task_argv[2] = NULL;
615
616 if ( LaunchTaskWithPipes(task_argv[0], task_argv, &readPipe, &writePipe) != 0 )
617 return E_NOTFOUND;
618
619 // close this pipe now so the forked process quits on completion
620 if ( writePipe != -1 )
621 close( writePipe );
622
623 // wait for data (and skip signals)
624 FD_ZERO( &fdset );
625 FD_SET( readPipe, &fdset );
626 do {
627 result = select( FD_SETSIZE, &fdset, NULL, NULL, &selectTimeout );
628 }
629 while ( result == -1 && errno == EINTR );
630 if ( result == -1 || result == 0 ) {
631 status = E_NOTFOUND;
632 break;
633 }
634
635 // now that the descriptor is ready, parse the configuration
636 fp = fdopen(readPipe, "r");
637 if ( fp == NULL ) {
638 status = E_NOTFOUND;
639 break;
640 }
641 *user = 0;
642 while(fgets(line, LINESIZE, fp))
643 {
644 if(strncasecmp(line, LookupOrder, LOOKUPORDERLEN) == 0) {
645 if((cp = strchr(line, '\n')) != NULL)
646 *cp = 0;
647 strcpy(user, line + LOOKUPORDERLEN);
648 continue;
649 }
650 if(strncasecmp(line, ConfigName, CONFIGNAMELEN) == 0) {
651 if(strncasecmp(line + CONFIGNAMELEN, GlobalConfig, GLOBALCONFIGLEN) != 0) {
652 status = E_NOGLOBALCONFIG;
653 }
654 break;
655 }
656 }
657 if(status < 0)
658 break;
659 /*-------------------------------------------------------------------
660 * Save the each LookupOrder and look for _config_name of User
661 * Configuration. If found, replace the global order with this one.
662 *-------------------------------------------------------------------*/
663 *order = 0;
664 while(fgets(line, LINESIZE, fp))
665 {
666 if(strncasecmp(line, LookupOrder, LOOKUPORDERLEN) == 0) {
667 if((cp = strchr(line, '\n')) != NULL)
668 *cp = 0;
669 strcpy(order, line + LOOKUPORDERLEN);
670 continue;
671 }
672 if(strncasecmp(line, ConfigName, CONFIGNAMELEN) == 0) {
673 if(strncasecmp(line + CONFIGNAMELEN, UserConfig, USERCONFIGLEN) == 0) {
674 if(*order)
675 strcpy(user, order);
676 break;
677 }
678 *order = 0;
679 }
680 }
681 if(*user == 0) {
682 status = E_NOLOOKUPORDER;
683 break;
684 }
685 }
686 while ( 0 );
687
688 if ( fp != NULL )
689 fclose( fp );
690 else if ( readPipe != -1 )
691 close( readPipe );
692
693 if(status < 0)
694 return status;
695
696 /*-----------------------------------------------------------------------
697 * Now for each agent, call the corresponding where function. If the
698 * return value is no E_NOTFOUND, then we either have found it or have
699 * detected an error.
700 *-----------------------------------------------------------------------*/
701 str = user;
702 while((cp = strtok(str, LookupOrderSep)) != NULL) {
703 if((w = bsearch(cp, WhereList, NWHERE, sizeof(struct where),
704 compar)) != NULL) {
705 if((status = w->func(name)) != E_NOTFOUND)
706 return status;
707 } else
708 printf("%s not supported\n", cp);
709 str = NULL;
710 }
711 return E_NOTFOUND;
712}
713#endif /* DIRECTORY_SERVICE */