1/* 2 * tclXunixId.c -- 3 * 4 * Tcl commands to access getuid, setuid, getgid, setgid and friends on Unix. 5 *----------------------------------------------------------------------------- 6 * Copyright 1991-1999 Karl Lehenbauer and Mark Diekhans. 7 * 8 * Permission to use, copy, modify, and distribute this software and its 9 * documentation for any purpose and without fee is hereby granted, provided 10 * that the above copyright notice appear in all copies. Karl Lehenbauer and 11 * Mark Diekhans make no representations about the suitability of this 12 * software for any purpose. It is provided "as is" without express or 13 * implied warranty. 14 *----------------------------------------------------------------------------- 15 * $Id: tclXunixId.c,v 8.1 2001/10/24 23:31:50 hobbs Exp $ 16 *----------------------------------------------------------------------------- 17 */ 18 19#include "tclExtdInt.h" 20 21/* 22 * Actually configured number of groups (from sysconf if we have it). 23 */ 24#ifndef NO_SYSCONF 25static int confNGroups = -1; 26#else 27#ifndef NGROUPS 28# ifdef NGROUPS_MAX 29# define NGROUPS NGROUPS_MAX 30# else 31# define NGROUPS 32 32# endif 33#endif 34static int confNGroups = NGROUPS; 35#endif 36 37/* 38 * Prototypes of internal functions. 39 */ 40static int 41UseridToUsernameResult _ANSI_ARGS_((Tcl_Interp *interp, 42 int userId)); 43 44static int 45UsernameToUseridResult _ANSI_ARGS_((Tcl_Interp *interp, 46 char *userName)); 47 48static int 49GroupidToGroupnameResult _ANSI_ARGS_((Tcl_Interp *interp, 50 int groupId)); 51 52static int 53GroupnameToGroupidResult _ANSI_ARGS_((Tcl_Interp *interp, 54 char *groupName)); 55 56static int 57IdConvert _ANSI_ARGS_((Tcl_Interp *interp, 58 int objc, 59 Tcl_Obj *CONST objv[])); 60 61static int 62IdEffective _ANSI_ARGS_((Tcl_Interp *interp, 63 int objc, 64 Tcl_Obj *CONST objv[])); 65 66static int 67IdProcess _ANSI_ARGS_((Tcl_Interp *interp, 68 int objc, 69 Tcl_Obj *CONST objv[])); 70 71static int 72IdGroupids _ANSI_ARGS_((Tcl_Interp *interp, 73 int objc, 74 Tcl_Obj *CONST objv[], 75 int symbolic)); 76 77static int 78IdHost _ANSI_ARGS_((Tcl_Interp *interp, 79 int objc, 80 Tcl_Obj *CONST objv[])); 81 82static int 83GetSetWrongArgs _ANSI_ARGS_((Tcl_Interp *interp, 84 Tcl_Obj *CONST objv[])); 85 86static int 87IdUser _ANSI_ARGS_((Tcl_Interp *interp, 88 int objc, 89 Tcl_Obj *CONST objv[])); 90 91static int 92IdUserId _ANSI_ARGS_((Tcl_Interp *interp, 93 int objc, 94 Tcl_Obj *CONST objv[])); 95 96static int 97IdGroup _ANSI_ARGS_((Tcl_Interp *interp, 98 int objc, 99 Tcl_Obj *CONST objv[])); 100 101static int 102IdGroupId _ANSI_ARGS_((Tcl_Interp *interp, 103 int objc, 104 Tcl_Obj *CONST objv[])); 105 106static int 107TclX_IdObjCmd _ANSI_ARGS_((ClientData clientData, 108 Tcl_Interp *interp, 109 int objc, 110 Tcl_Obj *CONST objv[])); 111 112/*----------------------------------------------------------------------------- 113 * TclX_IdObjCmd -- 114 * Implements the TclX id command on Unix. 115 * 116 * id user ?name? 117 * id convert user <name> 118 * 119 * id userid ?uid? 120 * id convert userid <uid> 121 * 122 * id group ?name? 123 * id convert group <name> 124 * 125 * id groupid ?gid? 126 * id convert groupid <gid> 127 * 128 * id groupids 129 * 130 * id host 131 * 132 * id process 133 * id process parent 134 * id process group 135 * id process group set 136 * 137 * id effective user 138 * id effective userid 139 * 140 * id effective group 141 * id effective groupid 142 * 143 * Results: 144 * Standard TCL results, may return the UNIX system error message. 145 * 146 *----------------------------------------------------------------------------- 147 */ 148 149static int 150UseridToUsernameResult (interp, userId) 151 Tcl_Interp *interp; 152 int userId; 153{ 154 uid_t uid = (uid_t) userId; 155 struct passwd *pw = getpwuid (userId); 156 Tcl_Obj *resultObj = Tcl_GetObjResult (interp); 157 char userIdString[16]; 158 159 if ((pw == NULL) || ((int) uid != userId)) { 160 sprintf (userIdString, "%d", uid); 161 Tcl_AppendStringsToObj (resultObj, 162 "unknown user id: ", 163 userIdString, 164 NULL); 165 endpwent (); 166 return TCL_ERROR; 167 } 168 Tcl_AppendToObj (resultObj, pw->pw_name, -1); 169 endpwent (); 170 return TCL_OK; 171} 172 173static int 174UsernameToUseridResult (interp, userName) 175 Tcl_Interp *interp; 176 char *userName; 177{ 178 struct passwd *pw = getpwnam (userName); 179 Tcl_Obj *resultObj = Tcl_GetObjResult (interp); 180 181 if (pw == NULL) { 182 Tcl_AppendStringsToObj (resultObj, 183 "unknown user id: ", 184 userName, 185 (char *) NULL); 186 endpwent (); 187 return TCL_ERROR; 188 } 189 Tcl_SetObjResult (interp, Tcl_NewIntObj (pw->pw_uid)); 190 endpwent (); 191 return TCL_OK; 192} 193 194static int 195GroupidToGroupnameResult (interp, groupId) 196 Tcl_Interp *interp; 197 int groupId; 198{ 199 gid_t gid = (gid_t) groupId; 200 struct group *grp = getgrgid (groupId); 201 Tcl_Obj *resultObj = Tcl_GetObjResult (interp); 202 char groupIdString[16]; 203 204 sprintf (groupIdString, "%d", gid); 205 206 if ((grp == NULL) || ((int) gid != groupId)) { 207 Tcl_AppendStringsToObj (resultObj, 208 "unknown group id: ", 209 groupIdString, 210 (char *)NULL); 211 endgrent (); 212 return TCL_ERROR; 213 } 214 Tcl_AppendToObj (resultObj, grp->gr_name, -1); 215 endgrent (); 216 return TCL_OK; 217} 218 219static int 220GroupnameToGroupidResult (interp, groupName) 221 Tcl_Interp *interp; 222 char *groupName; 223{ 224 struct group *grp = getgrnam (groupName); 225 Tcl_Obj *resultObj = Tcl_GetObjResult (interp); 226 if (grp == NULL) { 227 Tcl_AppendStringsToObj (resultObj, 228 "unknown group id: ", 229 groupName, 230 (char *) NULL); 231 return TCL_ERROR; 232 } 233 Tcl_SetIntObj (resultObj, grp->gr_gid); 234 return TCL_OK; 235} 236 237/* 238 * id convert type value 239 */ 240static int 241IdConvert (interp, objc, objv) 242 Tcl_Interp *interp; 243 int objc; 244 Tcl_Obj *CONST objv[]; 245{ 246 long uid; 247 long gid; 248 char *subCommand; 249 char *valueString; 250 251 if (objc != 4) 252 return TclX_WrongArgs (interp, objv [0], "convert type value"); 253 254 subCommand = Tcl_GetStringFromObj (objv[2], NULL); 255 valueString = Tcl_GetStringFromObj (objv[3], NULL); 256 257 if (STREQU (subCommand, "user")) 258 return UsernameToUseridResult (interp, valueString); 259 260 if (STREQU (subCommand, "userid")) { 261 if (Tcl_GetLongFromObj (interp, objv[3], &uid) != TCL_OK) 262 return TCL_ERROR; 263 return UseridToUsernameResult (interp, uid); 264 } 265 266 if (STREQU (subCommand, "group")) 267 return GroupnameToGroupidResult (interp, valueString); 268 269 if (STREQU (subCommand, "groupid")) { 270 if (Tcl_GetLongFromObj (interp, objv[3], &gid) != TCL_OK) 271 return TCL_ERROR; 272 return GroupidToGroupnameResult (interp, gid); 273 274 } 275 TclX_AppendObjResult (interp, "third arg must be \"user\", \"userid\", ", 276 "\"group\" or \"groupid\", got \"", subCommand, "\"", 277 (char *) NULL); 278 return TCL_ERROR; 279} 280 281/* 282 * id effective type 283 */ 284static int 285IdEffective (interp, objc, objv) 286 Tcl_Interp *interp; 287 int objc; 288 Tcl_Obj *CONST objv[]; 289{ 290 char *subCommand; 291 292 if (objc != 3) 293 return TclX_WrongArgs (interp, objv [0], "effective type"); 294 295 subCommand = Tcl_GetStringFromObj (objv[2], NULL); 296 297 if (STREQU (subCommand, "user")) 298 return UseridToUsernameResult (interp, geteuid ()); 299 300 if (STREQU (subCommand, "userid")) { 301 Tcl_SetObjResult (interp, Tcl_NewIntObj (geteuid ())); 302 return TCL_OK; 303 } 304 305 if (STREQU (subCommand, "group")) 306 return GroupidToGroupnameResult (interp, getegid ()); 307 308 if (STREQU (subCommand, "groupid")) { 309 Tcl_SetObjResult (interp, Tcl_NewIntObj (getegid ())); 310 return TCL_OK; 311 } 312 313 TclX_AppendObjResult (interp, "third arg must be \"user\", \"userid\", ", 314 "\"group\" or \"groupid\", got \"", 315 subCommand, "\"", (char *) NULL); 316 return TCL_ERROR; 317} 318 319/* 320 * id process ?parent|group? ?set? 321 */ 322static int 323IdProcess (interp, objc, objv) 324 Tcl_Interp *interp; 325 int objc; 326 Tcl_Obj *CONST objv[]; 327{ 328 pid_t pid; 329 char *subCommand; 330 char *trailerCommand; 331 332 if (objc > 4) 333 return TclX_WrongArgs (interp, 334 objv [0], 335 "process ?parent|group? ?set?"); 336 337 if (objc == 2) { 338 Tcl_SetObjResult (interp, Tcl_NewIntObj (getpid ())); 339 return TCL_OK; 340 } 341 342 subCommand = Tcl_GetStringFromObj (objv[2], NULL); 343 344 if (STREQU (subCommand, "parent")) { 345 if (objc != 3) 346 return TclX_WrongArgs (interp, objv [0], 347 " process parent"); 348 349 Tcl_SetObjResult (interp, Tcl_NewIntObj (getppid ())); 350 return TCL_OK; 351 } 352 if (STREQU (subCommand, "group")) { 353 if (objc == 3) { 354 Tcl_SetObjResult (interp, Tcl_NewIntObj (getpgrp ())); 355 return TCL_OK; 356 } 357 trailerCommand = Tcl_GetStringFromObj (objv[3], NULL); 358 if ((objc != 4) || !STREQU (trailerCommand, "set")) 359 return TclX_WrongArgs (interp, objv [0], 360 " process group ?set?"); 361 362 if (Tcl_IsSafe (interp)) { 363 TclX_AppendObjResult (interp, "can't set process group from a ", 364 "safe interpeter", (char *) NULL); 365 return TCL_ERROR; 366 } 367 368#ifndef NO_SETPGID 369 pid = getpid (); 370 setpgid (pid, pid); 371#else 372 setpgrp (); 373#endif 374 return TCL_OK; 375 } 376 377 TclX_AppendObjResult (interp, "expected one of \"parent\" or \"group\" ", 378 "got\"", subCommand, "\"", (char *) NULL); 379 return TCL_ERROR; 380} 381 382/* 383 * id groupids 384 * id groups 385 */ 386static int 387IdGroupids (interp, objc, objv, symbolic) 388 Tcl_Interp *interp; 389 int objc; 390 Tcl_Obj *CONST objv[]; 391 int symbolic; 392{ 393#ifndef NO_GETGROUPS 394 gid_t *groups; 395 int nGroups, groupIndex; 396 struct group *grp; 397 Tcl_Obj *resultObj = Tcl_GetObjResult (interp); 398 Tcl_Obj *newObj; 399 400 if (objc != 2) 401 return TclX_WrongArgs (interp, objv [0], "arg"); 402 403#ifndef NO_SYSCONF 404 if (confNGroups < 0) 405 confNGroups = sysconf (_SC_NGROUPS_MAX); 406#endif 407 groups = (gid_t *) ckalloc (confNGroups * sizeof (gid_t)); 408 409 410 nGroups = getgroups (confNGroups, groups); 411 if (nGroups < 0) { 412 Tcl_AppendStringsToObj (Tcl_GetObjResult (interp), 413 Tcl_PosixError (interp), (char *) NULL); 414 ckfree ((char *) groups); 415 return TCL_ERROR; 416 } 417 418 for (groupIndex = 0; groupIndex < nGroups; groupIndex++) { 419 if (symbolic) { 420 int groupId = groups [groupIndex]; 421 grp = getgrgid (groupId); 422 if (grp == NULL) { 423 char groupIdString[16]; 424 425 sprintf (groupIdString, "%d", groupId); 426 Tcl_AppendStringsToObj (resultObj, 427 "unknown group id: ", 428 groupIdString, 429 (char *)NULL); 430 endgrent (); 431 return TCL_ERROR; 432 } 433 newObj = Tcl_NewStringObj (grp->gr_name, -1); 434 Tcl_ListObjAppendElement (interp, 435 resultObj, 436 newObj); 437 } else { 438 newObj = Tcl_NewIntObj(groups[groupIndex]); 439 Tcl_ListObjAppendElement (interp, 440 resultObj, 441 newObj); 442 } 443 } 444 if (symbolic) 445 endgrent (); 446 ckfree ((char *) groups); 447 return TCL_OK; 448#else 449 TclX_AppendObjResult (interp, "group id lists unavailable on this system ", 450 "(no getgroups function)", (char *) NULL); 451 return TCL_ERROR; 452#endif 453} 454 455/* 456 * id host 457 */ 458static int 459IdHost (interp, objc, objv) 460 Tcl_Interp *interp; 461 int objc; 462 Tcl_Obj *CONST objv[]; 463{ 464#ifndef NO_GETHOSTNAME 465#ifndef MAXHOSTNAMELEN 466# define MAXHOSTNAMELEN 256 467#endif 468 char hostNameBuf[MAXHOSTNAMELEN]; 469 470 if (objc != 2) 471 return TclX_WrongArgs (interp, objv [0], "host"); 472 473 if (gethostname (hostNameBuf, MAXHOSTNAMELEN) < 0) { 474 TclX_AppendObjResult (interp, Tcl_PosixError (interp), 475 (char *) NULL); 476 return TCL_ERROR; 477 } 478 hostNameBuf[MAXHOSTNAMELEN-1] = '\0'; 479 Tcl_SetObjResult (interp, Tcl_NewStringObj (hostNameBuf, -1)); 480 return TCL_OK; 481#else 482 TclX_AppendObjResult (interp, "host name unavailable on this system ", 483 "(no gethostname function)", (char *) NULL); 484 return TCL_ERROR; 485#endif 486} 487 488/* 489 * Return error when a get set function has too many args (2 or 3 expected). 490 */ 491static int 492GetSetWrongArgs (interp, objv) 493 Tcl_Interp *interp; 494 Tcl_Obj *CONST objv[]; 495{ 496 return TclX_WrongArgs (interp, objv [0], "arg ?value?"); 497} 498 499/* 500 * id user 501 */ 502static int 503IdUser (interp, objc, objv) 504 Tcl_Interp *interp; 505 int objc; 506 Tcl_Obj *CONST objv[]; 507{ 508 struct passwd *pw; 509 char *user; 510 511 if (objc > 3) 512 return GetSetWrongArgs (interp, objv); 513 514 if (objc == 2) { 515 return UseridToUsernameResult (interp, getuid ()); 516 } 517 518 user = Tcl_GetStringFromObj (objv[2], NULL); 519 520 pw = getpwnam (user); 521 if (pw == NULL) { 522 TclX_AppendObjResult (interp, "user \"",user, "\" does not exist", 523 (char *) NULL); 524 goto errorExit; 525 } 526 if (setuid (pw->pw_uid) < 0) { 527 TclX_AppendObjResult (interp, Tcl_PosixError (interp), (char *) NULL); 528 goto errorExit; 529 } 530 endpwent (); 531 return TCL_OK; 532 533 errorExit: 534 endpwent (); 535 return TCL_ERROR; 536} 537 538/* 539 * id userid 540 */ 541static int 542IdUserId (interp, objc, objv) 543 Tcl_Interp *interp; 544 int objc; 545 Tcl_Obj *CONST objv[]; 546{ 547 int uid; 548 549 if (objc > 3) 550 return GetSetWrongArgs (interp, objv); 551 552 if (objc == 2) { 553 Tcl_SetObjResult (interp, Tcl_NewIntObj (getuid())); 554 return TCL_OK; 555 } 556 557 if (Tcl_GetIntFromObj (interp, objv[2], &uid) != TCL_OK) 558 return TCL_ERROR; 559 560 if (setuid ((uid_t) uid) < 0) { 561 TclX_AppendObjResult (interp, Tcl_PosixError (interp), (char *) NULL); 562 return TCL_ERROR; 563 } 564 565 return TCL_OK; 566} 567 568/* 569 * id group 570 */ 571static int 572IdGroup (interp, objc, objv) 573 Tcl_Interp *interp; 574 int objc; 575 Tcl_Obj *CONST objv[]; 576{ 577 struct group *grp; 578 char *groupName; 579 580 if (objc > 3) 581 return GetSetWrongArgs (interp, objv); 582 583 if (objc == 2) { 584 return GroupidToGroupnameResult (interp, getgid ()); 585 } 586 587 groupName = Tcl_GetStringFromObj (objv[2], NULL); 588 589 grp = getgrnam (groupName); 590 if (grp == NULL) { 591 TclX_AppendObjResult (interp, "group \"", groupName, 592 "\" does not exist", (char *) NULL); 593 goto errorExit; 594 } 595 if (setgid (grp->gr_gid) < 0) { 596 TclX_AppendObjResult (interp, Tcl_PosixError (interp), (char *) NULL); 597 goto errorExit; 598 } 599 endgrent (); 600 return TCL_OK; 601 602 errorExit: 603 endgrent (); 604 return TCL_ERROR; 605} 606 607/* 608 * id groupid 609 */ 610static int 611IdGroupId (interp, objc, objv) 612 Tcl_Interp *interp; 613 int objc; 614 Tcl_Obj *CONST objv[]; 615{ 616 int gid; 617 618 if (objc > 3) 619 return GetSetWrongArgs (interp, objv); 620 621 if (objc == 2) { 622 Tcl_SetIntObj (Tcl_GetObjResult (interp), getgid()); 623 return TCL_OK; 624 } 625 626 if (Tcl_GetIntFromObj (interp, objv[2], &gid) != TCL_OK) 627 return TCL_ERROR; 628 629 if (setgid ((gid_t) gid) < 0) { 630 TclX_AppendObjResult (interp, Tcl_PosixError (interp), (char *) NULL); 631 return TCL_ERROR; 632 } 633 634 return TCL_OK; 635} 636 637static int 638TclX_IdObjCmd (clientData, interp, objc, objv) 639 ClientData clientData; 640 Tcl_Interp *interp; 641 int objc; 642 Tcl_Obj *CONST objv[]; 643{ 644 char *subCommand; 645 646 if (objc < 2) 647 return TclX_WrongArgs (interp, objv [0], "arg ?arg...?"); 648 649 subCommand = Tcl_GetStringFromObj (objv [1], NULL); 650 /* 651 * If the first argument is "convert", handle the conversion. 652 */ 653 if (STREQU (subCommand, "convert")) { 654 return IdConvert (interp, objc, objv); 655 } 656 657 /* 658 * If the first argument is "effective", return the effective user ID, 659 * name, group ID or name. 660 */ 661 if (STREQU (subCommand, "effective")) { 662 return IdEffective (interp, objc, objv); 663 } 664 665 /* 666 * If the first argument is "process", return the process ID, parent's 667 * process ID, process group or set the process group depending on args. 668 */ 669 if (STREQU (subCommand, "process")) { 670 return IdProcess (interp, objc, objv); 671 } 672 673 /* 674 * Handle getting list of groups the user is a member of. 675 */ 676 if (STREQU (subCommand, "groups")) { 677 return IdGroupids (interp, objc, objv, TRUE); 678 } 679 680 if (STREQU (subCommand, "groupids")) { 681 return IdGroupids (interp, objc, objv, FALSE); 682 } 683 684 /* 685 * Handle returning the host name if its available. 686 */ 687 if (STREQU (subCommand, "host")) { 688 return IdHost (interp, objc, objv); 689 } 690 691 /* 692 * Handle setting or returning the user ID or group ID (by name or number). 693 */ 694 if (STREQU (subCommand, "user")) { 695 return IdUser (interp, objc, objv); 696 } 697 698 if (STREQU (subCommand, "userid")) { 699 return IdUserId (interp, objc, objv); 700 } 701 702 if (STREQU (subCommand, "group")) { 703 return IdGroup (interp, objc, objv); 704 } 705 706 if (STREQU (subCommand, "groupid")) { 707 return IdGroupId (interp, objc, objv); 708 } 709 710 TclX_AppendObjResult (interp, "second arg must be one of \"convert\", ", 711 "\"effective\", \"process\", ", 712 "\"user\", \"userid\", \"group\", \"groupid\", ", 713 "\"groups\", \"groupids\", ", 714 "or \"host\"", (char *) NULL); 715 return TCL_ERROR; 716} 717 718 719/*----------------------------------------------------------------------------- 720 * TclX_IdInit -- 721 * Initialize the id command. 722 *----------------------------------------------------------------------------- 723 */ 724void 725TclX_IdInit (interp) 726 Tcl_Interp *interp; 727{ 728 Tcl_CreateObjCommand (interp, 729 "id", 730 TclX_IdObjCmd, 731 (ClientData) NULL, 732 (Tcl_CmdDeleteProc*) NULL); 733} 734