1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2013 Gary Mills
  23  *
  24  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  25  * Use is subject to license terms.
  26  */
  27 
  28 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  29 /*        All Rights Reserved   */
  30 
  31 /*
  32  * Copyright (c) 2013 RackTop Systems.
  33  */
  34 
  35 #include <sys/types.h>
  36 #include <sys/stat.h>
  37 #include <sys/param.h>
  38 #include <stdio.h>
  39 #include <stdlib.h>
  40 #include <ctype.h>
  41 #include <limits.h>
  42 #include <string.h>
  43 #include <userdefs.h>
  44 #include <user_attr.h>
  45 #include <nss_dbdefs.h>
  46 #include <errno.h>
  47 #include <project.h>
  48 #include "users.h"
  49 #include "messages.h"
  50 #include "funcs.h"
  51 
  52 /*
  53  *  usermod [-u uid [-o] | -g group | -G group [[,group]...] | -d dir [-m]
  54  *              | -s shell | -c comment | -l new_logname]
  55  *              | -f inactive | -e expire ]
  56  *              [ -A authorization [, authorization ...]]
  57  *              [ -P profile [, profile ...]]
  58  *              [ -R role [, role ...]]
  59  *              [ -K key=value ]
  60  *              [ -p project [, project]] login
  61  *
  62  *      This command adds new user logins to the system.  Arguments are:
  63  *
  64  *      uid - an integer less than MAXUID
  65  *      group - an existing group's integer ID or char string name
  66  *      dir - a directory
  67  *      shell - a program to be used as a shell
  68  *      comment - any text string
  69  *      skel_dir - a directory
  70  *      base_dir - a directory
  71  *      rid - an integer less than 2**16 (USHORT)
  72  *      login - a string of printable chars except colon (:)
  73  *      inactive - number of days a login maybe inactive before it is locked
  74  *      expire - date when a login is no longer valid
  75  *      authorization - One or more comma separated authorizations defined
  76  *                      in auth_attr(4).
  77  *      profile - One or more comma separated execution profiles defined
  78  *                in prof_attr(4)
  79  *      role - One or more comma-separated role names defined in user_attr(4)
  80  *      key=value - One or more -K options each specifying a valid user_attr(4)
  81  *              attribute.
  82  *
  83  */
  84 
  85 extern int **valid_lgroup(), isbusy();
  86 extern int valid_uid(), check_perm(), create_home(), move_dir();
  87 extern int valid_expire(), edit_group(), call_passmgmt();
  88 extern projid_t **valid_lproject();
  89 
  90 static uid_t uid;               /* new uid */
  91 static gid_t gid;                       /* gid of new login */
  92 static char *new_logname = NULL;        /* new login name with -l option */
  93 static char *uidstr = NULL;             /* uid from command line */
  94 static char *group = NULL;              /* group from command line */
  95 static char *grps = NULL;               /* multi groups from command line */
  96 static char *dir = NULL;                /* home dir from command line */
  97 static char *shell = NULL;              /* shell from command line */
  98 static char *comment = NULL;            /* comment from command line */
  99 static char *logname = NULL;            /* login name to add */
 100 static char *inactstr = NULL;           /* inactive from command line */
 101 static char *expire = NULL;             /* expiration date from command line */
 102 static char *projects = NULL;           /* project ids from command line */
 103 static char *usertype;
 104 
 105 char *cmdname;
 106 static char gidstring[32], uidstring[32];
 107 char inactstring[10];
 108 
 109 char *
 110 strcpmalloc(str)
 111 char *str;
 112 {
 113         if (str == NULL)
 114                 return (NULL);
 115 
 116         return (strdup(str));
 117 }
 118 struct passwd *
 119 passwd_cpmalloc(opw)
 120 struct passwd *opw;
 121 {
 122         struct passwd *npw;
 123 
 124         if (opw == NULL)
 125                 return (NULL);
 126 
 127 
 128         npw = malloc(sizeof (struct passwd));
 129 
 130         npw->pw_name = strcpmalloc(opw->pw_name);
 131         npw->pw_passwd = strcpmalloc(opw->pw_passwd);
 132         npw->pw_uid = opw->pw_uid;
 133         npw->pw_gid = opw->pw_gid;
 134         npw->pw_age = strcpmalloc(opw->pw_age);
 135         npw->pw_comment = strcpmalloc(opw->pw_comment);
 136         npw->pw_gecos  = strcpmalloc(opw->pw_gecos);
 137         npw->pw_dir = strcpmalloc(opw->pw_dir);
 138         npw->pw_shell = strcpmalloc(opw->pw_shell);
 139 
 140         return (npw);
 141 }
 142 
 143 int
 144 main(argc, argv)
 145 int argc;
 146 char **argv;
 147 {
 148         int ch, ret = EX_SUCCESS, call_pass = 0, oflag = 0;
 149         int tries, mflag = 0, inact, **gidlist, flag = 0;
 150         boolean_t fail_if_busy = B_FALSE;
 151         char *ptr;
 152         struct passwd *pstruct;         /* password struct for login */
 153         struct passwd *pw;
 154         struct group *g_ptr;    /* validated group from -g */
 155         struct stat statbuf;            /* status buffer for stat */
 156 #ifndef att
 157         FILE *pwf;              /* fille ptr for opened passwd file */
 158 #endif
 159         int warning;
 160         projid_t **projlist;
 161         char **nargv;                   /* arguments for execvp of passmgmt */
 162         int argindex;                   /* argument index into nargv */
 163         userattr_t *ua;
 164         char *val;
 165         int isrole;                     /* current account is role */
 166 
 167         cmdname = argv[0];
 168 
 169         if (geteuid() != 0) {
 170                 errmsg(M_PERM_DENIED);
 171                 exit(EX_NO_PERM);
 172         }
 173 
 174         opterr = 0;                     /* no print errors from getopt */
 175         /* get user type based on the program name */
 176         usertype = getusertype(argv[0]);
 177 
 178         while ((ch = getopt(argc, argv,
 179                                 "c:d:e:f:G:g:l:mop:s:u:A:P:R:K:")) != EOF)
 180                 switch (ch) {
 181                 case 'c':
 182                         comment = optarg;
 183                         flag++;
 184                         break;
 185                 case 'd':
 186                         dir = optarg;
 187                         fail_if_busy = B_TRUE;
 188                         flag++;
 189                         break;
 190                 case 'e':
 191                         expire = optarg;
 192                         flag++;
 193                         break;
 194                 case 'f':
 195                         inactstr = optarg;
 196                         flag++;
 197                         break;
 198                 case 'G':
 199                         grps = optarg;
 200                         flag++;
 201                         break;
 202                 case 'g':
 203                         group = optarg;
 204                         fail_if_busy = B_TRUE;
 205                         flag++;
 206                         break;
 207                 case 'l':
 208                         new_logname = optarg;
 209                         fail_if_busy = B_TRUE;
 210                         flag++;
 211                         break;
 212                 case 'm':
 213                         mflag++;
 214                         flag++;
 215                         fail_if_busy = B_TRUE;
 216                         break;
 217                 case 'o':
 218                         oflag++;
 219                         flag++;
 220                         fail_if_busy = B_TRUE;
 221                         break;
 222                 case 'p':
 223                         projects = optarg;
 224                         flag++;
 225                         break;
 226                 case 's':
 227                         shell = optarg;
 228                         flag++;
 229                         break;
 230                 case 'u':
 231                         uidstr = optarg;
 232                         flag++;
 233                         fail_if_busy = B_TRUE;
 234                         break;
 235                 case 'A':
 236                         change_key(USERATTR_AUTHS_KW, optarg);
 237                         flag++;
 238                         break;
 239                 case 'P':
 240                         change_key(USERATTR_PROFILES_KW, optarg);
 241                         flag++;
 242                         break;
 243                 case 'R':
 244                         change_key(USERATTR_ROLES_KW, optarg);
 245                         flag++;
 246                         break;
 247                 case 'K':
 248                         change_key(NULL, optarg);
 249                         flag++;
 250                         break;
 251                 default:
 252                 case '?':
 253                         if (is_role(usertype))
 254                                 errmsg(M_MRUSAGE);
 255                         else
 256                                 errmsg(M_MUSAGE);
 257                         exit(EX_SYNTAX);
 258                 }
 259 
 260         if (optind != argc - 1 || flag == 0) {
 261                 if (is_role(usertype))
 262                         errmsg(M_MRUSAGE);
 263                 else
 264                         errmsg(M_MUSAGE);
 265                 exit(EX_SYNTAX);
 266         }
 267 
 268         if ((!uidstr && oflag) || (mflag && !dir)) {
 269                 if (is_role(usertype))
 270                         errmsg(M_MRUSAGE);
 271                 else
 272                         errmsg(M_MUSAGE);
 273                 exit(EX_SYNTAX);
 274         }
 275 
 276         logname = argv[optind];
 277 
 278         /* Determine whether the account is a role or not */
 279         if ((ua = getusernam(logname)) == NULL ||
 280             (val = kva_match(ua->attr, USERATTR_TYPE_KW)) == NULL ||
 281             strcmp(val, USERATTR_TYPE_NONADMIN_KW) != 0)
 282                 isrole = 0;
 283         else
 284                 isrole = 1;
 285 
 286         /* Verify that rolemod is used for roles and usermod for users */
 287         if (isrole != is_role(usertype)) {
 288                 if (isrole)
 289                         errmsg(M_ISROLE);
 290                 else
 291                         errmsg(M_ISUSER);
 292                 exit(EX_SYNTAX);
 293         }
 294 
 295         /* Set the usertype key; defaults to the commandline  */
 296         usertype = getsetdefval(USERATTR_TYPE_KW, usertype);
 297 
 298         if (is_role(usertype)) {
 299                 /* Roles can't have roles */
 300                 if (getsetdefval(USERATTR_ROLES_KW, NULL) != NULL) {
 301                         errmsg(M_MRUSAGE);
 302                         exit(EX_SYNTAX);
 303                 }
 304                 /* If it was an ordinary user, delete its roles */
 305                 if (!isrole)
 306                         change_key(USERATTR_ROLES_KW, "");
 307         }
 308 
 309 #ifdef att
 310         pw = getpwnam(logname);
 311 #else
 312         /*
 313          * Do this with fgetpwent to make sure we are only looking on local
 314          * system (since passmgmt only works on local system).
 315          */
 316         if ((pwf = fopen("/etc/passwd", "r")) == NULL) {
 317                 errmsg(M_OOPS, "open", "/etc/passwd");
 318                 exit(EX_FAILURE);
 319         }
 320         while ((pw = fgetpwent(pwf)) != NULL)
 321                 if (strcmp(pw->pw_name, logname) == 0)
 322                         break;
 323 
 324         fclose(pwf);
 325 #endif
 326 
 327         if (pw == NULL) {
 328                 char            pwdb[NSS_BUFLEN_PASSWD];
 329                 struct passwd   pwd;
 330 
 331                 if (getpwnam_r(logname, &pwd, pwdb, sizeof (pwdb)) == NULL) {
 332                         /* This user does not exist. */
 333                         errmsg(M_EXIST, logname);
 334                         exit(EX_NAME_NOT_EXIST);
 335                 } else {
 336                         /* This user exists in non-local name service. */
 337                         errmsg(M_NONLOCAL, logname);
 338                         exit(EX_NOT_LOCAL);
 339                 }
 340         }
 341 
 342         pstruct = passwd_cpmalloc(pw);
 343 
 344         /*
 345          * We can't modify a logged in user if any of the following
 346          * are being changed:
 347          * uid (-u & -o), group (-g), home dir (-m), loginname (-l).
 348          * If none of those are specified it is okay to go ahead
 349          * some types of changes only take effect on next login, some
 350          * like authorisations and profiles take effect instantly.
 351          * One might think that -K type=role should require that the
 352          * user not be logged in, however this would make it very
 353          * difficult to make the root account a role using this command.
 354          */
 355         if (isbusy(logname)) {
 356                 if (fail_if_busy) {
 357                         errmsg(M_BUSY, logname, "change");
 358                         exit(EX_BUSY);
 359                 }
 360                 warningmsg(WARN_LOGGED_IN, logname);
 361         }
 362 
 363         if (new_logname && strcmp(new_logname, logname)) {
 364                 switch (valid_login(new_logname, (struct passwd **)NULL,
 365                         &warning)) {
 366                 case INVALID:
 367                         errmsg(M_INVALID, new_logname, "login name");
 368                         exit(EX_BADARG);
 369                         /*NOTREACHED*/
 370 
 371                 case NOTUNIQUE:
 372                         errmsg(M_USED, new_logname);
 373                         exit(EX_NAME_EXISTS);
 374                         /*NOTREACHED*/
 375 
 376                 case LONGNAME:
 377                         errmsg(M_TOO_LONG, new_logname);
 378                         exit(EX_BADARG);
 379                         /*NOTREACHED*/
 380 
 381                 default:
 382                         call_pass = 1;
 383                         break;
 384                 }
 385                 if (warning)
 386                         warningmsg(warning, logname);
 387         }
 388 
 389         if (uidstr) {
 390                 /* convert uidstr to integer */
 391                 errno = 0;
 392                 uid = (uid_t)strtol(uidstr, &ptr, (int)10);
 393                 if (*ptr || errno == ERANGE) {
 394                         errmsg(M_INVALID, uidstr, "user id");
 395                         exit(EX_BADARG);
 396                 }
 397 
 398                 if (uid != pstruct->pw_uid) {
 399                         switch (valid_uid(uid, NULL)) {
 400                         case NOTUNIQUE:
 401                                 if (!oflag) {
 402                                         /* override not specified */
 403                                         errmsg(M_UID_USED, uid);
 404                                         exit(EX_ID_EXISTS);
 405                                 }
 406                                 break;
 407                         case RESERVED:
 408                                 errmsg(M_RESERVED, uid);
 409                                 break;
 410                         case TOOBIG:
 411                                 errmsg(M_TOOBIG, "uid", uid);
 412                                 exit(EX_BADARG);
 413                                 break;
 414                         }
 415 
 416                         call_pass = 1;
 417 
 418                 } else {
 419                         /* uid's the same, so don't change anything */
 420                         uidstr = NULL;
 421                         oflag = 0;
 422                 }
 423 
 424         } else uid = pstruct->pw_uid;
 425 
 426         if (group) {
 427                 switch (valid_group(group, &g_ptr, &warning)) {
 428                 case INVALID:
 429                         errmsg(M_INVALID, group, "group id");
 430                         exit(EX_BADARG);
 431                         /*NOTREACHED*/
 432                 case TOOBIG:
 433                         errmsg(M_TOOBIG, "gid", group);
 434                         exit(EX_BADARG);
 435                         /*NOTREACHED*/
 436                 case UNIQUE:
 437                         errmsg(M_GRP_NOTUSED, group);
 438                         exit(EX_NAME_NOT_EXIST);
 439                         /*NOTREACHED*/
 440                 case RESERVED:
 441                         gid = (gid_t)strtol(group, &ptr, (int)10);
 442                         errmsg(M_RESERVED_GID, gid);
 443                         break;
 444                 }
 445                 if (warning)
 446                         warningmsg(warning, group);
 447 
 448                 if (g_ptr != NULL)
 449                         gid = g_ptr->gr_gid;
 450                 else
 451                         gid = pstruct->pw_gid;
 452 
 453                 /* call passmgmt if gid is different, else ignore group */
 454                 if (gid != pstruct->pw_gid)
 455                         call_pass = 1;
 456                 else group = NULL;
 457 
 458         } else gid = pstruct->pw_gid;
 459 
 460         if (grps && *grps) {
 461                 if (!(gidlist = valid_lgroup(grps, gid)))
 462                         exit(EX_BADARG);
 463         } else
 464                 gidlist = (int **)0;
 465 
 466         if (projects && *projects) {
 467                 if (! (projlist = valid_lproject(projects)))
 468                         exit(EX_BADARG);
 469         } else
 470                 projlist = (projid_t **)0;
 471 
 472         if (dir) {
 473                 if (REL_PATH(dir)) {
 474                         errmsg(M_RELPATH, dir);
 475                         exit(EX_BADARG);
 476                 }
 477                 if (strcmp(pstruct->pw_dir, dir) == 0) {
 478                         /* home directory is the same so ignore dflag & mflag */
 479                         dir = NULL;
 480                         mflag = 0;
 481                 } else call_pass = 1;
 482         }
 483 
 484         if (mflag) {
 485                 if (stat(dir, &statbuf) == 0) {
 486                         /* Home directory exists */
 487                         if (check_perm(statbuf, pstruct->pw_uid,
 488                             pstruct->pw_gid, S_IWOTH|S_IXOTH) != 0) {
 489                                 errmsg(M_NO_PERM, logname, dir);
 490                                 exit(EX_NO_PERM);
 491                         }
 492 
 493                 } else ret = create_home(dir, NULL, uid, gid);
 494 
 495                 if (ret == EX_SUCCESS)
 496                         ret = move_dir(pstruct->pw_dir, dir, logname);
 497 
 498                 if (ret != EX_SUCCESS)
 499                         exit(ret);
 500         }
 501 
 502         if (shell) {
 503                 if (REL_PATH(shell)) {
 504                         errmsg(M_RELPATH, shell);
 505                         exit(EX_BADARG);
 506                 }
 507                 if (strcmp(pstruct->pw_shell, shell) == 0) {
 508                         /* ignore s option if shell is not different */
 509                         shell = NULL;
 510                 } else {
 511                         if (stat(shell, &statbuf) < 0 ||
 512                             (statbuf.st_mode & S_IFMT) != S_IFREG ||
 513                             (statbuf.st_mode & 0555) != 0555) {
 514 
 515                                 errmsg(M_INVALID, shell, "shell");
 516                                 exit(EX_BADARG);
 517                         }
 518 
 519                         call_pass = 1;
 520                 }
 521         }
 522 
 523         if (comment) {
 524                 /* ignore comment if comment is not changed */
 525                 if (strcmp(pstruct->pw_comment, comment))
 526                         call_pass = 1;
 527                 else
 528                         comment = NULL;
 529         }
 530 
 531         /* inactive string is a positive integer */
 532         if (inactstr) {
 533                 /* convert inactstr to integer */
 534                 inact = (int)strtol(inactstr, &ptr, 10);
 535                 if (*ptr || inact < 0) {
 536                         errmsg(M_INVALID, inactstr, "inactivity period");
 537                         exit(EX_BADARG);
 538                 }
 539                 call_pass = 1;
 540         }
 541 
 542         /* expiration string is a date, newer than today */
 543         if (expire) {
 544                 if (*expire &&
 545                     valid_expire(expire, (time_t *)0) == INVALID) {
 546                         errmsg(M_INVALID, expire, "expiration date");
 547                         exit(EX_BADARG);
 548                 }
 549                 call_pass = 1;
 550         }
 551 
 552         if (nkeys > 0)
 553                 call_pass = 1;
 554 
 555         /* that's it for validations - now do the work */
 556 
 557         if (grps) {
 558                 /* redefine login's supplentary group memberships */
 559                 ret = edit_group(logname, new_logname, gidlist, 1);
 560                 if (ret != EX_SUCCESS) {
 561                         errmsg(M_UPDATE, "modified");
 562                         exit(ret);
 563                 }
 564         }
 565         if (projects) {
 566                 ret = edit_project(logname, (char *)NULL, projlist, 0);
 567                 if (ret != EX_SUCCESS) {
 568                         errmsg(M_UPDATE, "modified");
 569                         exit(ret);
 570                 }
 571         }
 572 
 573 
 574         if (!call_pass) exit(ret);
 575 
 576         /* only get to here if need to call passmgmt */
 577         /* set up arguments to  passmgmt in nargv array */
 578         nargv = malloc((30 + nkeys * 2) * sizeof (char *));
 579 
 580         argindex = 0;
 581         nargv[argindex++] = PASSMGMT;
 582         nargv[argindex++] = "-m";       /* modify */
 583 
 584         if (comment) {  /* comment */
 585                 nargv[argindex++] = "-c";
 586                 nargv[argindex++] = comment;
 587         }
 588 
 589         if (dir) {
 590                 /* flags for home directory */
 591                 nargv[argindex++] = "-h";
 592                 nargv[argindex++] = dir;
 593         }
 594 
 595         if (group) {
 596                 /* set gid flag */
 597                 nargv[argindex++] = "-g";
 598                 (void) sprintf(gidstring, "%u", gid);
 599                 nargv[argindex++] = gidstring;
 600         }
 601 
 602         if (shell) {    /* shell */
 603                 nargv[argindex++] = "-s";
 604                 nargv[argindex++] = shell;
 605         }
 606 
 607         if (inactstr) {
 608                 nargv[argindex++] = "-f";
 609                 nargv[argindex++] = inactstr;
 610         }
 611 
 612         if (expire) {
 613                 nargv[argindex++] = "-e";
 614                 nargv[argindex++] = expire;
 615         }
 616 
 617         if (uidstr) {   /* set uid flag */
 618                 nargv[argindex++] = "-u";
 619                 (void) sprintf(uidstring, "%u", uid);
 620                 nargv[argindex++] = uidstring;
 621         }
 622 
 623         if (oflag) nargv[argindex++] = "-o";
 624 
 625         if (new_logname) {      /* redefine login name */
 626                 nargv[argindex++] = "-l";
 627                 nargv[argindex++] = new_logname;
 628         }
 629 
 630         if (nkeys > 0)
 631                 addkey_args(nargv, &argindex);
 632 
 633         /* finally - login name */
 634         nargv[argindex++] = logname;
 635 
 636         /* set the last to null */
 637         nargv[argindex++] = NULL;
 638 
 639         /* now call passmgmt */
 640         ret = PEX_FAILED;
 641         for (tries = 3; ret != PEX_SUCCESS && tries--; ) {
 642                 switch (ret = call_passmgmt(nargv)) {
 643                 case PEX_SUCCESS:
 644                 case PEX_BUSY:
 645                         break;
 646 
 647                 case PEX_HOSED_FILES:
 648                         errmsg(M_HOSED_FILES);
 649                         exit(EX_INCONSISTENT);
 650                         break;
 651 
 652                 case PEX_SYNTAX:
 653                 case PEX_BADARG:
 654                         /* should NEVER occur that passmgmt usage is wrong */
 655                         if (is_role(usertype))
 656                                 errmsg(M_MRUSAGE);
 657                         else
 658                                 errmsg(M_MUSAGE);
 659                         exit(EX_SYNTAX);
 660                         break;
 661 
 662                 case PEX_BADUID:
 663                         /* uid in use - shouldn't happen print message anyway */
 664                         errmsg(M_UID_USED, uid);
 665                         exit(EX_ID_EXISTS);
 666                         break;
 667 
 668                 case PEX_BADNAME:
 669                         /* invalid loname */
 670                         errmsg(M_USED, logname);
 671                         exit(EX_NAME_EXISTS);
 672                         break;
 673 
 674                 default:
 675                         errmsg(M_UPDATE, "modified");
 676                         exit(ret);
 677                         break;
 678                 }
 679         }
 680         if (tries == 0) {
 681                 errmsg(M_UPDATE, "modified");
 682         }
 683 
 684         exit(ret);
 685         /*NOTREACHED*/
 686 }