Merge branch 'master' of ssh://git.code.sf.net/p/cdesktopenv/code
[oweals/cde.git] / cde / programs / dtaction / Main.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: Main.c /main/18 1998/07/23 17:56:36 mgreess $ */
24 /*****************************************************************************
25  *****************************************************************************
26  **
27  **   File:         Main.c
28  **
29  **   Project:      DT
30  **
31  **   Description:  This file contains the main program for dtaction.
32  **               
33  **
34  **(c) Copyright 1993, 1994 Hewlett-Packard Company
35  **(c) Copyright 1993, 1994 International Business Machines Corp.
36  **(c) Copyright 1993, 1994 Sun Microsystems, Inc.
37  **(c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of Novell, Inc.
38  **
39  **
40  ****************************************************************************
41  ************************************<+>*************************************/
42
43 #include <limits.h>
44 #include <stddef.h>
45 #include <unistd.h>
46 #include <nl_types.h>
47 #include <signal.h>
48 #include <locale.h>
49 #include <sys/param.h>          /* for MAXPATHLEN and MAXHOSTNAMELEN */
50
51 #ifdef sun
52 #include <crypt.h>
53 #include <shadow.h>
54 #endif
55
56 #include <errno.h>
57 #include <grp.h>
58 #include <pwd.h>
59
60 #include <X11/Intrinsic.h>
61 #include <Xm/XmP.h>
62 #include <Xm/Text.h>
63 #include <Xm/SelectioB.h>
64 #include <Xm/MessageB.h>
65 #include <Xm/Protocols.h>
66 #include <Xm/MwmUtil.h>
67
68 #include <Dt/Dt.h>
69 #include <Dt/DbUtil.h>
70 #include <Dt/CmdInv.h>
71 #include <Dt/Action.h>
72 #include <Dt/EnvControlP.h>
73
74
75 /******************************************************************************/
76
77 #include <time.h>
78
79
80 /* Command line options */
81 XrmOptionDescRec option_list[] =
82 {
83    {  "-user",     "user",          XrmoptionSepArg,   NULL},
84    {  "-contextDir", "contextDir",  XrmoptionSepArg,   NULL},
85    {  "-execHost", "execHost",      XrmoptionSepArg,   NULL},
86    {  "-termOpts", "termOpts",      XrmoptionSepArg,   NULL},
87 };
88
89 /* Fallback Resources */
90 static char *fallback_resources[] = {
91     "*timeout: 5",
92     (char *) NULL
93 };
94
95 typedef struct {
96    char * user;
97    char * contextDir;
98    char * execHost;
99    char * termOpts;
100 } ApplArgs, *ApplArgsPtr;
101
102 #define LOGIN_STR_LEN 15
103 #define STAR ' '
104
105 Widget dlog;
106 char *password;
107 char *stars;
108 int  passwordLength = 0;
109
110 Boolean finished = False;
111 Boolean rootRequest = False;
112 char * toPword = NULL;
113 char * rootPword = NULL;
114 char * origName = "Unknown";
115 int basegid = -1;
116 int newuid = -1;
117
118 /* Dtaction resources */
119 XtResource resources[] =
120 {
121    {
122     "user", "User", XmRString, sizeof(char *),
123     XtOffsetOf(ApplArgs, user), XmRImmediate, (XtPointer) NULL,
124    },
125    {
126     "contextDir", "ContextDir", XmRString, sizeof(char *),
127     XtOffsetOf(ApplArgs, contextDir), XmRImmediate, (XtPointer) NULL,
128    },
129    {
130     "execHost", "ExecHost", XmRString, sizeof(char *),
131     XtOffsetOf(ApplArgs, execHost), XmRImmediate, (XtPointer) NULL,
132    },
133    {
134     "termOpts", "TermParms", XmRString, sizeof(char *),
135     XtOffsetOf(ApplArgs, termOpts), XmRImmediate, (XtPointer) NULL,
136    },
137 };
138
139  /*
140  * macro to get message catalog strings
141  */
142
143 #ifndef NO_MESSAGE_CATALOG
144 # ifdef __ultrix
145 #  define _CLIENT_CAT_NAME "dtact.cat"
146 # else  /* __ultrix */
147 #  define _CLIENT_CAT_NAME "dtact"
148 # endif /* __ultrix */
149 extern char *_DtGetMessage(char *filename, int set, int n, char *s);
150 # define GETMESSAGE(set, number, string)\
151     (_DtGetMessage(_CLIENT_CAT_NAME, set, number, string))
152 #else
153 # define GETMESSAGE(set, number, string)\
154     string
155 #endif
156
157 Boolean _DtWmStringsAreEqual( 
158                         register char *in_str,
159                         register char *test_str) ;
160 void CheckForDone( 
161                         XtPointer clientData,
162                         XtIntervalId id) ;
163 void CheckUserRequest( void ) ;
164 void CheckPasswd( void ) ;
165 void AddSuLog( 
166                         char *FromName,
167                         char *ToName,
168                         char *ChangeType) ;
169 void CleanPath( 
170                         char *Path) ;
171 void OkCallback( 
172                         Widget w,
173                         XtPointer clientData,
174                         XtPointer callData) ;
175 void ErrOkCallback( 
176                         Widget w,
177                         XtPointer clientData,
178                         XtPointer callData) ;
179 void CancelCallback( 
180                         Widget w,
181                         XtPointer clientData,
182                         XtPointer callData) ;
183 void MapCallback( 
184                         Widget w,
185                         XtPointer clientData,
186                         XtPointer callData) ;
187
188 void actionStatusCallback (
189                         DtActionInvocationID id,
190                         XtPointer   client_data,
191                         DtActionArg *aap,
192                         int         aac,
193                         DtActionStatus status );
194
195 void GetUserPrompt( void ) ;
196 void LogSuccess( void ) ;
197 void LogFailure( void ) ;
198 void MyInsert( 
199                         Widget w,
200                         XEvent *event,
201                         char **params,
202                         Cardinal *num_params) ;
203 void MyCancel( 
204                         Widget w,
205                         XEvent *event,
206                         char **params,
207                         Cardinal *num_params) ;
208 void EditPasswdCB( 
209                         Widget w,
210                         XtPointer client,
211                         XtPointer call) ;
212 void UnknownUser( void ) ;
213 void UnknownUserCallback( 
214                         Widget w,
215                         XtPointer clientData,
216                         XtPointer callData) ;
217
218
219 /********    End Forward Function Declarations    ********/
220
221
222 /***************************************************************************
223  *
224  *  Text widget actions and translations
225  *
226  ***************************************************************************/
227
228 XtActionsRec textActions[] = {
229         {"my-insert", (XtActionProc)MyInsert},
230         {"my-cancel", (XtActionProc)MyCancel},
231 };
232
233 char textEventBindings[] = {
234 "Shift <Key>Tab:                        prev-tab-group() \n\
235   Ctrl <Key>Tab:                        next-tab-group() \n\
236  <Key>Tab:                              next-tab-group() \n\
237  <Key>osfEndLine:                       end-of-line() \n\
238  <Key>osfBeginLine:                     beginning-of-line() \n\
239  ~Shift <Key>osfRight:                  forward-character()\n\
240  ~Shift <Key>osfLeft:                   backward-character()\n\
241   Ctrl <Key>osfDelete:                  delete-to-end-of-line()\n\
242  <Key>osfDelete:                        delete-next-character()\n\
243   <Key>osfBackSpace:                    delete-previous-character()\n\
244  <Key>osfActivate:                      activate()\n\
245   Ctrl <Key>Return:                     activate()\n\
246  <Key>Return:                           activate()\n\
247  <Key>osfCancel:                        my-cancel()\n\
248  <Key>:                                 my-insert()\n\
249  ~Ctrl ~Shift ~Meta ~Alt<Btn1Down>:     grab-focus() \n\
250  <EnterWindow>:                         enter()\n\
251  <LeaveWindow>:                         leave()\n\
252  <FocusIn>:                             focusIn()\n\
253  <FocusOut>:                            focusOut()\n\
254  <Unmap>:                               unmap()"
255 };
256
257
258 /****************************************************************************/
259 /****************************************************************************/
260
261 static Widget toplevel;
262 static ApplArgs appArgs;
263 static XtAppContext appContext;
264 static DtActionInvocationID actionId;
265 static Boolean exitAfterInvoked = False; 
266 static int exitStatus = 0;
267
268 /* ARGSUSED */
269 void
270 CheckForDone(
271    XtPointer clientData,
272    XtIntervalId id)
273 {
274     if ( toplevel->core.num_popups ==  0 ) 
275         exit(exitStatus);
276
277     XtAppAddTimeOut(appContext,
278         10, (XtTimerCallbackProc)CheckForDone,
279       NULL);
280 }
281
282
283 void
284 main( 
285      int argc,
286      char **argv ) 
287 {
288     Display *display;
289     Arg args[20];
290     int n=0;
291     char *actionName;
292     int numArgs = 0;
293     char contextDir[MAXPATHLEN+1];
294     DtActionArg *ap = NULL;
295   
296     XtSetLanguageProc(NULL, NULL, NULL);
297     _DtEnvControl(DT_ENV_SET);
298     (void) signal(SIGCLD, (void (*)())SIG_IGN);
299
300     /*  Initialize the toolkit and open the display  */
301     XtToolkitInitialize() ;
302     appContext = XtCreateApplicationContext() ;
303     if ( !(display = XtOpenDisplay( appContext, NULL, argv[0], "Dtaction", 
304                              option_list, 
305                              sizeof(option_list)/sizeof(XrmOptionDescRec),
306                              &argc, argv)) )
307     {
308         setlocale(LC_ALL, "");
309         fprintf(stderr,GETMESSAGE(1,11,"Can't open display.\n"));
310         exit(-1);
311     }
312   
313     XtSetArg(args[n], XmNallowShellResize, True); n++;
314     XtSetArg(args[n], XmNmappedWhenManaged, False); n++;
315     XtSetArg(args[n], XmNheight, 1); n++;
316     XtSetArg(args[n], XmNwidth, 1); n++;
317     toplevel = XtAppCreateShell( argv[0], "Dtaction", 
318             topLevelShellWidgetClass, display, args, n) ;
319
320     XtRealizeWidget(toplevel);
321
322     display = XtDisplay (toplevel);
323     XtGetApplicationResources(toplevel, &appArgs, 
324         resources, XtNumber(resources), NULL, 0);
325
326     password = XtMalloc(1);
327     password[0] = '\0';
328     stars = XtMalloc(1);
329     stars[0] = '\0';
330
331    /*  Get Dt initialized  */
332    if (DtInitialize (display, toplevel, argv[0], "Dtaction") == False)
333    {
334       /* Fatal Error: could not connect to the messaging system. */
335       /* DtInitialize() has already logged an appropriate error msg */
336       exit(-1);
337    }
338
339    /*
340     * If the request specified that it wanted to run as a different
341     * user, then take care of prompting for a password, and doing any
342     * necessary verification and logging.
343     */
344    CheckUserRequest();
345    
346    /* Load the filetype/action dbs; DtInvokeAction() requires this */
347    DtDbLoad();
348
349    /*
350     * Get the requested action name
351     */
352     if ( (actionName = argv[1]) == NULL)
353     {
354         fprintf(stderr,GETMESSAGE(1,10,"No action name specified.\n"));
355         exit(-1);
356     }
357
358     if ( argc > 2 ) 
359     {
360         /*
361          * create an action arg array for the file objects for
362          * this action.  This number of objects should be one
363          * less than the argument count. The returned vector will
364          * be terminated by a null pointer.
365          */
366         numArgs= argc - 2;
367         ap = (DtActionArg *) XtCalloc(numArgs,sizeof(DtActionArg)); 
368     }
369
370         /*
371          * This client is restricted to FILE arguments.
372          * for the time being.
373          */
374     for ( n = 0; n < numArgs; n++) {
375         ap[n].argClass    = DtACTION_FILE;
376         ap[n].u.file.name = argv[n+2];
377     }
378
379     actionId = DtActionInvoke(toplevel, actionName, ap, numArgs,
380         appArgs.termOpts,
381         appArgs.execHost,
382         appArgs.contextDir,
383         True,                   /* use indicator */
384         (DtActionCallbackProc) actionStatusCallback,
385         NULL);
386
387     /*
388      * Set up a timer if we didn't get a valid procId -- since there will
389      * be no invocation update in that case.
390      * We must invoke XtMainLoop() at least once, to force any prompt or
391      * error dialogs to get posted.
392      */
393     if ( !actionId)
394             XtAppAddTimeOut(appContext,
395                 10, (XtTimerCallbackProc)CheckForDone,
396                            NULL);
397
398     XtAppMainLoop(appContext);
399
400 }
401
402
403 static void
404 SetGidUid ( unsigned short rgid, unsigned short ruid )
405 {
406
407         /* fix process gid */
408 #if defined(SVR4) || defined(_AIX)
409     setgid(rgid);
410 #elif defined(__osf__) || defined(linux)
411     setregid(rgid, rgid);
412 #elif defined(__hpux)
413     setresgid(rgid, rgid, rgid);
414 #else
415     setregid(rgid, rgid, rgid);
416 #endif
417
418         /* fix process uid */
419 #if defined (SVR4) || defined (_AIX)
420     setuid(ruid);
421 #elif defined(__osf__) || defined(linux)
422     setreuid(ruid, ruid);
423 #elif defined(__hpux)
424     setresuid(ruid, ruid, ruid);
425 #else
426     setreuid(ruid, ruid, ruid);
427 #endif
428
429 }
430
431
432 /*
433  * This function checks to see if the user has requested that the action
434  * be invoked under a different user Id.  If a different user Id has been
435  * requested, then the user will be prompted to enter either the password
436  * for that user, or the root password.  Once a valid password has been
437  * entered, this function will return.
438  */
439
440 void 
441 CheckUserRequest( void )
442
443 {
444    unsigned short ruid;
445    unsigned short rgid;
446    struct passwd * passwd;
447 #ifdef sun
448    struct spwd * spwd;
449 #endif
450    Boolean notExist = False;
451    Boolean alreadySetToRoot = False;
452
453    rootRequest = False;
454
455         /* get current group id */
456    rgid = getgid();
457         /* get current user id */
458    ruid = getuid();
459
460       /* See if the user wants to run as himself */
461    if (appArgs.user == NULL)
462    {
463       SetGidUid(rgid,ruid);
464       return;
465    }
466
467    /* Get password for the requested user */
468    passwd = getpwnam(appArgs.user);
469    if (passwd) {
470 #ifdef sun
471       spwd = getspnam(appArgs.user);
472       if (spwd)
473       {
474 #endif
475          if (passwd->pw_uid == ruid)
476          {
477             /* 
478              * We are already running as the
479              *  requested user.  So return now.
480              */
481              SetGidUid(rgid,ruid);
482              return;
483           }
484
485 #ifdef sun
486           if (spwd->sp_pwdp)
487              toPword = XtNewString(spwd->sp_pwdp);
488        }
489 #else
490        if (passwd->pw_passwd)
491           toPword = XtNewString(passwd->pw_passwd);
492 #endif /* sun */
493        basegid = passwd->pw_gid;
494        newuid = passwd->pw_uid;
495     }
496    else
497        notExist = True;
498
499    /* Root requests require some extra work later */
500    if (strcmp(appArgs.user, "root") == 0)
501          rootRequest = True;
502
503    /* Get name for the current user */
504    passwd = getpwuid(ruid);
505    if (passwd && passwd->pw_name)
506        origName = XtNewString(passwd->pw_name);
507
508    /* Get password for the root user */
509    passwd = getpwnam("root");
510    if (passwd && passwd->pw_passwd) 
511    {
512 #ifdef sun
513        spwd = getspnam("root");
514        if (spwd && spwd->sp_pwdp) 
515        { 
516            rootPword = XtNewString(spwd->sp_pwdp);
517        }
518 #else
519       rootPword = XtNewString(passwd->pw_passwd);
520 #endif /* sun */
521
522        if (passwd->pw_uid == ruid)
523           alreadySetToRoot = True;
524    }
525
526    /*
527     * If 'alreadySetToRoot' is set to True, then that means that the
528     * user is  currently running as root.
529     */
530
531    if (notExist)
532    {
533       /* Requested user does not exist; this function will exit() */
534        UnknownUser();
535    }
536    else if ((alreadySetToRoot) ||  /* requested users passwd is null */
537       ((toPword && (toPword[0] == '\0')) || (toPword == NULL)))
538    {
539        /* Already there -- no need to check a passwd */
540           LogSuccess();
541    }
542    else
543          CheckPasswd();
544 }
545
546
547 /**********
548  * void CheckPasswd ()
549  *
550  * get a password from the user and check it against an encrypted passwd
551  *
552  * Returns:
553  *      0 if invalid
554  *      True if valid
555  *
556  * Side Effects:
557  *      none.
558  */
559
560 void 
561 CheckPasswd( void )
562
563 {
564     /*
565      * get this users password
566      */
567     GetUserPrompt();
568 }
569
570
571 /**********
572  * void AddSuLog (FromName, ToName, ChangeType)
573  *
574  * add switch from user "FromName" to user "ToName" to sulog.
575  * ChangeType is "+" for success, "-" for failure.
576  *
577  * Parameters:
578  *      char *FromName -- from name (for logging).
579  *      char *ToName -- to name (for logging).
580  *      char *ChangeType -- +/- (for logging).
581  */
582
583 void 
584 AddSuLog(
585         char *FromName,
586         char *ToName,
587         char *ChangeType )
588
589 {
590     char        *toString;
591     struct tm  *localtime ();
592
593     register    FILE * f;
594     struct stat st;
595     time_t    timenow;
596     struct tm  *now;
597
598 #ifdef hpV4  /* 10.* versions */
599     char * SULog = "/var/adm/sulog";
600 #elif  defined( hpux )  /* 9.* versions */
601     char * SULog = "/usr/adm/sulog";
602 #else
603     char * SULog = "/var/adm/sulog";
604 #endif
605
606     if ((f = fopen (SULog, "a")) == NULL)
607         return;
608
609     (void) time (&timenow);
610     now = localtime (&timenow);
611
612     /* build toString... */
613     if (ToName && *ToName) 
614             toString = ToName;
615     else 
616             toString = FromName;
617
618     fprintf(f, (GETMESSAGE(1,1, 
619            "dtaction %1$.2d/%2$.2d %3$.2d:%4$.2d %5$1.1s %6$s %7$s-%8$s\n")),
620            now -> tm_mon + 1, now -> tm_mday, now -> tm_hour,
621            now -> tm_min, ChangeType, "?", FromName, toString);
622
623     (void) fclose (f);
624
625     /*
626      * take away write access from SULog
627      */
628
629     if (stat (SULog, &st) == 0)
630        chmod (SULog, (int) (st.st_mode & 07777) & ~0222);
631
632     return;
633 }
634
635
636 /**********
637  * void CleanPath (Path);
638  *
639  * remove any directories from the path that are
640  *    - null (leading/trailing colon or double colon)
641  *    - not anchored to root (no leading /)
642  *
643  * the returned path is the original path with any such
644  * directories stripped
645  *
646  * Parameters:
647  *      char *Path -- $PATH to clean
648  *
649  * Returns:
650  *      none.
651  *
652  * Side Effects:
653  *      Unanchored paths will be stripped off of Path.
654  */
655
656 void 
657 CleanPath(
658         char *Path )
659
660 {
661     register char  *StrippedPath;
662     register char  *PathHead;
663
664     StrippedPath = PathHead = Path;
665
666     while (*Path) {
667
668         /*
669          * remove multiple ':'s
670          */
671
672         while (*Path && (*Path == ':')) {
673             (void) Path++;
674         }
675
676         /*
677          * is the first character of this
678          * directory a '/'????
679          */
680
681         if (*Path == '/') {
682
683             /*
684              * copy directory intact;
685              */
686
687             while (*Path && (*Path != ':')) {
688                 *StrippedPath++ = *Path++;
689             }
690
691             if (*Path == ':') {
692                 *StrippedPath++ = *Path++;
693             }
694         }
695         else {
696
697             /*
698              * zip past directory
699              */
700
701             while (*Path && (*Path != ':')) {
702                 (void) Path++;
703             }
704
705             if (*Path == ':') {
706                 (void) Path++;
707             }
708         }
709
710     }
711
712     /*
713      * remove all trailing ':'s
714      */
715
716     while ((StrippedPath > PathHead) && (StrippedPath[-1] == ':')) {
717         StrippedPath--;
718     }
719
720     /*
721      * null terminate the path
722      */
723
724     *StrippedPath = '\0';
725     return;
726 }
727
728
729 /*
730  * This is the Ok callback for the password dialog.  It will attempt to
731  * validate the password.  If invalid, then an error dialog is posted,
732  * and the user is prompted to try again.  If valid, then the process
733  * will change to run as the requested user, and dtaction will continue
734  * on its way, attempting to invoke the requested action.
735  */
736
737 /* ARGSUSED */
738 void 
739 OkCallback(
740         Widget w,
741         XtPointer clientData,
742         XtPointer callData )
743
744 {
745    Boolean valid = False;
746
747    /* Do any verification here */
748
749    /* check for primary passwd... */
750    if (!strcmp (crypt (password, toPword), toPword))
751       valid = True;
752
753    /* check for secondary passwd ... */
754    if (rootPword && *rootPword &&
755        !strcmp (crypt (password, rootPword), rootPword))
756    {
757       valid = True;
758    }
759    else if (((rootPword == NULL) || (*rootPword == '\0')) &&
760             (*password == '\0'))
761    {
762       /* No root password, and the user entered no password */
763       valid = True;
764    }
765
766
767    /* If valid password, then unpost */
768    if (valid)
769    {
770       XtUnmanageChild(dlog);
771       XFlush(XtDisplay(dlog));
772       finished = True;
773       LogSuccess();
774    }
775    else
776    {
777       /* Invalid password */
778       Widget err;
779       int n;
780       Arg args[10];
781       XmString okLabel;
782       XmString message;
783       char * template;
784       char * title;
785       char * master;
786
787       okLabel = XmStringCreateLocalized(GETMESSAGE(1, 2, "OK"));
788       template = (GETMESSAGE(1,3, "The password you entered does not match\nthe password for user %s.\n\nPlease reenter the password, or select the\nCancel button to terminate the operation."));
789       master = XtMalloc(strlen(template) + strlen(appArgs.user) + 10);
790       sprintf(master, template, appArgs.user);
791       message = XmStringCreateLocalized(master);
792       title = (GETMESSAGE(1,4, "Action Invoker - Password Error"));
793
794       /* Post an error dialog */
795       n = 0;
796       XtSetArg(args[n], XmNtitle, title); n++;
797       XtSetArg(args[n], XmNmessageString, message); n++;
798       XtSetArg(args[n], XmNokLabelString, okLabel); n++;
799       err = XmCreateErrorDialog(dlog, "err", args, n);
800       XtUnmanageChild(XmMessageBoxGetChild(err, XmDIALOG_CANCEL_BUTTON));
801       XtUnmanageChild(XmMessageBoxGetChild(err, XmDIALOG_HELP_BUTTON));
802       XtManageChild(err);
803       XtAddCallback(err, XmNokCallback, ErrOkCallback, err);
804
805       XFlush(XtDisplay(dlog));
806       XmUpdateDisplay(dlog);
807       LogFailure();
808       XmStringFree(okLabel);
809       XmStringFree(message);
810       XtFree(master);
811    }
812 }
813
814
815 /*
816  * This is the Cancel callback for the password dialog.  It will unpost
817  * the dialog, and exit.
818  */
819
820 /* ARGSUSED */
821 void 
822 CancelCallback(
823         Widget w,
824         XtPointer clientData,
825         XtPointer callData )
826
827 {
828    XtUnmanageChild(dlog);
829    XFlush(XtDisplay(dlog));
830    exit(-1);
831 }
832
833
834 /*
835  * This is the 'Ok' callback for the invalid password error dialog.
836  * It will simply unpost and destroy the error dialog.
837  */
838
839 /* ARGSUSED */
840 void 
841 ErrOkCallback(
842         Widget w,
843         XtPointer clientData,
844         XtPointer callData )
845
846 {
847    Widget err = (Widget)clientData;
848
849    XtUnmanageChild(err);
850    XFlush(XtDisplay(err));
851    XmUpdateDisplay(err);
852    XtDestroyWidget(err);
853 }
854
855
856 /*
857  * This callback is invoked when the password dialog is posted; it forces
858  * the focus to the text input field.
859  */
860
861 /* ARGSUSED */
862 void 
863 MapCallback(
864         Widget w,
865         XtPointer clientData,
866         XtPointer callData )
867
868 {
869    Widget text;
870    Widget dlog = (Widget)clientData;
871
872    /* Force focus initially to the text field */
873    text = XmSelectionBoxGetChild(dlog, XmDIALOG_TEXT);
874    XmProcessTraversal(text, XmTRAVERSE_CURRENT);
875 }
876
877
878 /*
879  * This function creates the prompt dialog for collecting the password
880  * from the user.  It will not give up control until a valid password
881  * has been entered.  If the user cancels the request, then the cancel
882  * callback will exit.
883  */
884
885 void 
886 GetUserPrompt( void )
887
888 {
889    int n;
890    XmString xmString;
891    XmString xmString2;
892    char prompt[BUFSIZ];
893    Widget help;
894    Widget text;
895    XEvent event;
896    Arg args[10];
897    XtTranslations      textTable;
898    XmString cancelLabel;
899    XmString okLabel;
900
901    snprintf(prompt, sizeof prompt, (GETMESSAGE(1,5, "Enter password for user %s:")), 
902             appArgs.user);
903    xmString = XmStringCreateLocalized(prompt);
904    xmString2 =XmStringCreateLocalized(GETMESSAGE(1,6, "Action Invoker - Password"));
905    cancelLabel = XmStringCreateLocalized(GETMESSAGE(1,7, "Cancel"));
906    okLabel = XmStringCreateLocalized(GETMESSAGE(1,2, "OK"));
907
908    XtAppAddActions(appContext,textActions, 2);
909    textTable = XtParseTranslationTable(textEventBindings);
910
911    /* Create the prompt dialog */
912    n = 0;
913    XtSetArg(args[n], XmNselectionLabelString, xmString);  n++;
914    XtSetArg(args[n], XmNdialogTitle, xmString2);  n++;
915    XtSetArg(args[n], XmNautoUnmanage, False);  n++;
916    XtSetArg(args[n], XmNokLabelString, okLabel);  n++;
917    XtSetArg(args[n], XmNcancelLabelString, cancelLabel);  n++;
918    XtSetArg(args[n], XmNdefaultPosition, False);  n++;
919    dlog = XmCreatePromptDialog(toplevel, "prompt", args, n);
920    XmStringFree(xmString);
921    XmStringFree(xmString2);
922    XmStringFree(okLabel);
923    XmStringFree(cancelLabel);
924    XtAddCallback(dlog, XmNokCallback, OkCallback, NULL);
925    XtAddCallback(dlog, XmNcancelCallback, CancelCallback, NULL);
926
927    text = XmSelectionBoxGetChild(dlog, XmDIALOG_TEXT);
928    n = 0;
929    XtSetArg(args[n], XmNtranslations, textTable); n++;
930    XtSetArg(args[n], XmNverifyBell, False);       n++;
931    XtSetValues(text, args, n);
932    XtAddCallback(text, XmNmodifyVerifyCallback, EditPasswdCB, NULL);
933
934    /* Add callback for forcing traversal to the text field */
935    XtAddCallback (XtParent(dlog), XmNpopupCallback, MapCallback, dlog);
936
937    /* Unmanage the help button */
938    help = XmSelectionBoxGetChild(dlog, XmDIALOG_HELP_BUTTON);
939    XtUnmanageChild(help);
940
941    /* Center the dialog */
942    XtRealizeWidget(dlog);
943    XtSetArg (args[0], XmNx,
944              (Position)(WidthOfScreen(XtScreen(dlog)) -
945                         dlog->core.width) / 2);
946    XtSetArg (args[1], XmNy,
947              (Position)(HeightOfScreen(XtScreen(dlog)) -
948                         dlog->core.height) / 2);
949    XtSetValues (dlog, args, 2);
950
951    /* Set the transient property */
952    XSetTransientForHint (XtDisplay (toplevel),
953                          XtWindow (XtParent (dlog)),
954                          XtWindow (toplevel));
955
956    /*  Adjust the decorations for the dialog shell of the dialog  */
957    n = 0;
958    XtSetArg(args[n], XmNmwmFunctions, 0);          n++;
959    XtSetArg(args[n], XmNmwmDecorations, 
960             MWM_DECOR_BORDER | MWM_DECOR_TITLE);   n++;
961    XtSetValues(XtParent(dlog), args, n);
962
963    /* Post the dialog */
964    XtManageChild(dlog);
965
966    /* Wait for the user to finish with the dialog */
967    while (!finished)
968    {
969       XtAppNextEvent(appContext,&event);
970       XtDispatchEvent(&event);
971    }
972
973    /* Destroy the widget, and return any data back to the appl */
974    XtDestroyWidget(dlog);
975 }
976
977
978 /*
979  * When a user has successfully logged in as another user, we need to set
980  * the uid and gid to the requested user.  In addition, if the user is
981  * changing to 'root', then we need to log this in /usr/adm/sulog, and
982  * we need to do some housekeeping of the $PATH environment variable.
983  */
984
985 void 
986 LogSuccess( void )
987
988 {
989    char * path;
990    char * tmpPath;
991
992    AddSuLog(origName, appArgs.user, "+");
993
994    if (rootRequest)
995    {
996       /* Special stuff for the root user */
997       /* Cleanse the $PATH setting */
998       tmpPath = getenv("PATH");
999       path = XtNewString(tmpPath);
1000       CleanPath (path);
1001       tmpPath = XtMalloc(strlen(path) + 10);
1002       strcpy(tmpPath, "PATH=");
1003       strcat(tmpPath, path);
1004       putenv(tmpPath);
1005    }
1006
1007    /* Set up the user's new id's */
1008    SetGidUid(basegid,newuid);
1009 #ifndef sco
1010    initgroups(appArgs.user, basegid);
1011 #endif
1012
1013 }
1014
1015
1016 /*
1017  * Each time the user enters an invalid password, we need to log this in
1018  * /usr/adm/sulog, if the user is attempting to switch to the 'root' user.
1019  */
1020
1021 void 
1022 LogFailure( void )
1023
1024 {
1025    /* Unable to change to specified user; post error, then exit */
1026    AddSuLog(origName, appArgs.user, "-");
1027 }
1028
1029
1030 /***************************************************************************
1031  *
1032  *  MyInsert
1033  *
1034  *  Local self-insert action for the text widget. The default action
1035  *  discards control characters, which are allowed in password.
1036  ***************************************************************************/
1037
1038 /* ARGSUSED */
1039 void 
1040 MyInsert(
1041         Widget w,
1042         XEvent *event,
1043         char **params,
1044         Cardinal *num_params )
1045 {
1046     char           str[32];
1047     XComposeStatus compstatus;
1048     int            n;
1049
1050     n = XLookupString((XKeyEvent *)event, str, sizeof(str),
1051                       (KeySym *)NULL, &compstatus);
1052
1053     if (n > 0) {
1054        str[n] = '\0';
1055        XmTextFieldInsert(w, XmTextFieldGetInsertionPosition(w), str);
1056     }
1057 }
1058
1059
1060 /***************************************************************************
1061  *
1062  *  MyCancel
1063  *
1064  *  This action catches the 'Escape' key, and following Motif standards,
1065  *  unposts the dialog, as if the 'Cancel' button had been pressed.
1066  ***************************************************************************/
1067
1068 /* ARGSUSED */
1069 void 
1070 MyCancel(
1071         Widget w,
1072         XEvent *event,
1073         char **params,
1074         Cardinal *num_params )
1075 {
1076    CancelCallback(w, NULL, NULL);
1077 }
1078
1079
1080
1081 /***************************************************************************
1082  *
1083  *  EditPasswdCB
1084  *
1085  *  implement no-echo of the password
1086  ***************************************************************************/
1087
1088
1089 /* ARGSUSED */
1090 void 
1091 EditPasswdCB(
1092         Widget w,
1093         XtPointer client,
1094         XtPointer call )
1095 {
1096
1097     static Boolean      allow_flag = False;
1098     int                 replaced_length, i;
1099     char                *src, *dst;
1100     XmTextVerifyPtr     cbData = (XmTextVerifyPtr) call;
1101   
1102     if (!allow_flag)
1103     {
1104       /* 
1105        * we need to keep track of the password ourselves in order to 
1106        * disable echoing of the password...
1107        */
1108       
1109       replaced_length = cbData->endPos - cbData->startPos;
1110       if (replaced_length > cbData->text->length)
1111       {
1112         /* shift the text at and after endPos to the left...  */
1113         for (src = password + cbData->endPos,
1114              dst = src + (cbData->text->length - replaced_length),
1115              i = passwordLength - cbData->endPos;
1116              i > 0;
1117              ++src, ++dst, --i)
1118          {
1119             *dst = *src;
1120          }
1121       }
1122       else if (replaced_length < cbData->text->length)
1123       {
1124         /* Buffer must grow */
1125         password = XtRealloc(password,
1126               passwordLength + cbData->text->length - replaced_length + 5);
1127
1128         /* shift the text at and after endPos to the right...  */
1129         for (src = password + passwordLength - 1,
1130              dst = src + (cbData->text->length - replaced_length),
1131              i = passwordLength - cbData->endPos;
1132              i > 0;
1133              --src, --dst, --i)
1134          {
1135             *dst = *src;
1136          }
1137       }
1138       
1139       /*
1140        * update the password...
1141        */
1142
1143       for (src = cbData->text->ptr,
1144            dst = password + cbData->startPos,
1145            i = cbData->text->length;
1146            i > 0;
1147            ++src, ++dst, --i)
1148       {
1149         *dst = *src;
1150       }
1151
1152       passwordLength += cbData->text->length - replaced_length;
1153       password[passwordLength] = '\0';
1154       stars = XtRealloc(stars, cbData->text->length + 10);
1155       for (i = 0; i < cbData->text->length; i++)
1156          stars[i] = ' ';
1157       stars[cbData->text->length] = '\0';
1158       
1159       /*
1160        * put the appropriate number of stars in the passwd Widget..
1161        */
1162
1163       allow_flag = True;
1164       XmTextFieldReplace(w, cbData->startPos, cbData->endPos, stars);
1165       allow_flag = False;
1166     }
1167   
1168     cbData->doit = allow_flag;
1169 }
1170
1171
1172 /*
1173  * This function posts an error dialog informing the user that they have
1174  * specified an invalid user name.  No further processing will be done; we
1175  * will simply wait for the user to acknowledge the error, and then exit.
1176  */
1177
1178 void 
1179 UnknownUser( void )
1180
1181 {
1182    Widget err;
1183    int n;
1184    Arg args[10];
1185    XmString okLabel;
1186    XmString message;
1187    char * template;
1188    char * title;
1189    char * master;
1190
1191    okLabel = XmStringCreateLocalized(GETMESSAGE(1,2, "OK"));
1192    template = (GETMESSAGE(1,8, "The user '%s' is an unknown user name.\n\nThe requested action will not be executed."));
1193    master = XtMalloc(strlen(template) + strlen(appArgs.user) + 10);
1194    sprintf(master, template, appArgs.user);
1195    message = XmStringCreateLocalized(master);
1196    title = (GETMESSAGE(1,9, "Action Invoker - Unknown User"));
1197
1198    /* Post an error dialog */
1199    n = 0;
1200    XtSetArg(args[n], XmNtitle, title); n++;
1201    XtSetArg(args[n], XmNmessageString, message); n++;
1202    XtSetArg(args[n], XmNokLabelString, okLabel); n++;
1203    XtSetArg(args[n], XmNdefaultPosition, False);  n++;
1204    err = XmCreateErrorDialog(toplevel, "err", args, n);
1205    XtUnmanageChild(XmMessageBoxGetChild(err, XmDIALOG_CANCEL_BUTTON));
1206    XtUnmanageChild(XmMessageBoxGetChild(err, XmDIALOG_HELP_BUTTON));
1207
1208    /* Center the dialog */
1209    XtRealizeWidget(err);
1210    XtSetArg (args[0], XmNx,
1211              (Position)(WidthOfScreen(XtScreen(err)) -
1212                         err->core.width) / 2);
1213    XtSetArg (args[1], XmNy,
1214              (Position)(HeightOfScreen(XtScreen(err)) -
1215                         err->core.height) / 2);
1216    XtSetValues (err, args, 2);
1217
1218    XtManageChild(err);
1219    XtAddCallback(err, XmNokCallback, UnknownUserCallback, err);
1220    XtAddCallback(err, XmNcancelCallback, UnknownUserCallback, err);
1221
1222    XFlush(XtDisplay(toplevel));
1223    XmUpdateDisplay(toplevel);
1224    LogFailure();
1225    XmStringFree(okLabel);
1226    XmStringFree(message);
1227    XtFree(master);
1228    XtAppMainLoop(appContext);
1229 }
1230
1231
1232 /*
1233  * This is the 'Cancel' callback for the 'Invalid User' error dialog.
1234  * It removes the dialog, and then exits.
1235  */
1236
1237 /* ARGSUSED */
1238 void 
1239 UnknownUserCallback(
1240         Widget w,
1241         XtPointer clientData,
1242         XtPointer callData )
1243
1244 {
1245    Widget err = (Widget)clientData;
1246
1247    XtUnmanageChild(err);
1248    XFlush(XtDisplay(err));
1249    XmUpdateDisplay(err);
1250    exit(-1);
1251 }
1252
1253 void actionStatusCallback(
1254                         DtActionInvocationID id,
1255                         XtPointer   client_data,
1256                         DtActionArg *aap,
1257                         int         aac,
1258                         DtActionStatus status )
1259 {
1260
1261     switch( status ) {
1262         case DtACTION_INVOKED:
1263             /*
1264              * There may still be error dialogs to post so we must return
1265              * to mainLoop before exiting.
1266              */
1267             if ( exitAfterInvoked )
1268                     XtAppAddTimeOut(appContext,
1269                         10 , (XtTimerCallbackProc)CheckForDone,
1270                       NULL);
1271             break;
1272
1273         case DtACTION_DONE:
1274             XtAppAddTimeOut(appContext,
1275                 10 , (XtTimerCallbackProc)CheckForDone,
1276               NULL);
1277             break;
1278
1279         case DtACTION_FAILED:
1280         case DtACTION_CANCELED:
1281             exitStatus = 1;
1282             XtAppAddTimeOut(appContext,
1283                 10 , (XtTimerCallbackProc)CheckForDone,
1284               NULL);
1285             break;
1286
1287         default:
1288             break;
1289     }
1290
1291 }