1 mike 1.1 /*
2 **==============================================================================
3 **
4 ** Open Management Infrastructure (OMI)
5 **
6 ** Copyright (c) Microsoft Corporation
7 **
8 ** Licensed under the Apache License, Version 2.0 (the "License"); you may not
9 ** use this file except in compliance with the License. You may obtain a copy
10 ** of the License at
11 **
12 ** http://www.apache.org/licenses/LICENSE-2.0
13 **
14 ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
16 ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
17 ** MERCHANTABLITY OR NON-INFRINGEMENT.
18 **
19 ** See the Apache 2 License for the specific language governing permissions
20 ** and limitations under the License.
21 **
22 mike 1.1 **==============================================================================
23 */
24
|
25 krisbash 1.3 #include <common.h>
26
27 #include <pal/format.h>
|
28 mike 1.1 #include "user.h"
29 #include "log.h"
30
31 #if defined(CONFIG_POSIX)
32 # include "credcache.h"
33 # include <unistd.h>
34 # include <sys/types.h>
35 # include <pwd.h>
36 # include <grp.h>
37 # if defined(CONFIG_OS_DARWIN) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ <= 1058
38 # include <pam/pam_appl.h>
39 # else
40 # include <security/pam_appl.h>
41 # endif
42 # include <signal.h>
43 # include <sys/wait.h>
44 # include <sys/socket.h>
45 # include <sys/un.h>
46 # include <unistd.h>
47 # include <fcntl.h>
48 # include <sys/stat.h>
49 mike 1.1 #elif defined(CONFIG_OS_WINDOWS)
50 #endif
51
52 #include "paths.h"
53
54 #define GROUPNAME_SIZE 128
55
56 #if defined(CONFIG_POSIX)
|
57 krisbash 1.3 static int s_ignoreAuthCalls = 0;
|
58 mike 1.1 #endif
59
60 #if defined(CONFIG_OS_WINDOWS)
61 /*
62 Validates user name and password;
63 Returns:
64 '0' if user account is valid and authorized to use CIM server
65 '-1' otherwise
66 */
67 int AuthenticateUser(const char* user, const char* password)
68 {
69 MI_UNUSED(user);
70 MI_UNUSED(password);
71 return 0;
72 }
73
74 /*
75 Validates user's account for correct account name, expiration etc.
76 Returns:
77 '0' if user account is valid and authorized to use CIM server
78 '-1' otherwise
79 mike 1.1 */
80 int ValidateUser(const char* user)
81 {
82 MI_UNUSED(user);
83 return 0;
84 }
85
86 int LookupUser(const char* user, uid_t* uid, gid_t* gid)
87 {
88 MI_UNUSED(user);
89 *uid = -1;
90 *gid = -1;
91 return 0;
92 }
93
94 int GetUserGidByUid(uid_t uid, gid_t* gid)
95 {
96 MI_UNUSED(uid);
97 *gid = -1;
98 return 0;
99 }
100 mike 1.1
101
102 int GetUIDByConnection(int fd, uid_t* uid, gid_t* gid)
103 {
104 MI_UNUSED(fd);
105 *uid = -1;
106 *gid = -1;
107 return -1;
108 }
109
110 /*
111 Creates file with random data owned by user and RO by user only
112 Parameters:
113 uid - user ID
114 size - number of bytes to write
115 path - [out] - resulting file name
116
117 Returns:
118 0 if operation was successful; -1 otherwise
119 */
|
120 krisbash 1.3 _Use_decl_annotations_
|
121 mike 1.1 int CreateAuthFile(
122 uid_t uid,
123 char* content,
124 size_t size,
|
125 krisbash 1.3 char path[PAL_MAX_PATH_SIZE])
|
126 mike 1.1 {
127 MI_UNUSED(uid);
128 MI_UNUSED(size);
129 MI_UNUSED(path);
130 MI_UNUSED(content);
131
132 return -1;
133 }
134
135 #elif defined(CONFIG_POSIX)
136
137 static int GetGroupName(
138 gid_t gid,
139 char name[GROUPNAME_SIZE]);
140
141 static int _authCallback(
142 int numMessages,
|
143 krisbash 1.3 #if defined(CONFIG_OS_LINUX) || defined(CONFIG_OS_DARWIN) || defined(CONFIG_OS_BSD)
|
144 mike 1.1 const struct pam_message** messages,
145 #else
146 struct pam_message** messages,
147 #endif
148 struct pam_response** response,
149 void* applicationData)
150 {
151 const char* password = (const char*)applicationData;
152 int i;
153
154 /* If zero (or megative) messages, return now */
155
156 if (numMessages <= 0)
157 {
158 return PAM_CONV_ERR;
159 }
160
161 /* Allocate the responses */
162
|
163 krisbash 1.3 *response = (struct pam_response*)SystemCalloc(
|
164 mike 1.1 numMessages,
|
165 krisbash 1.3 sizeof(struct pam_response));
|
166 mike 1.1
167 if (!*response)
168 {
|
169 krisbash 1.3 return PAM_BUF_ERR;
|
170 mike 1.1 }
171
172 /* Copy the password to the response messages */
173
174 for (i = 0; i < numMessages; i++)
175 {
176 if (PAM_PROMPT_ECHO_OFF == messages[i]->msg_style)
177 {
|
178 krisbash 1.3 response[i]->resp = (char*)SystemMalloc(PAM_MAX_MSG_SIZE);
|
179 mike 1.1 Strlcpy(response[i]->resp, password, PAM_MAX_MSG_SIZE);
180 response[i]->resp_retcode = 0;
181 }
182 else
|
183 krisbash 1.3 {
|
184 mike 1.1 return PAM_CONV_ERR;
|
185 krisbash 1.3 }
|
186 mike 1.1 }
187
188 return PAM_SUCCESS;
189 }
190
191 static int _PamCheckUser(
192 const char* user,
193 const char* password)
194 {
195 struct pam_conv conv;
196 pam_handle_t* t = 0;
197
198 memset(&conv, 0, sizeof(conv));
199
200 conv.conv = _authCallback;
201 conv.appdata_ptr = (void*)password;
202
203 if (PAM_SUCCESS != pam_start("omi", user, &conv, &t))
204 return -1;
205
206 if (PAM_SUCCESS != pam_authenticate(t, 0))
207 mike 1.1 {
208 pam_end(t,0);
209 return -1;
210 }
211
212 if (PAM_SUCCESS != pam_acct_mgmt(t, 0))
213 {
214 pam_end(t,0);
215 return -1;
216 }
217
218 pam_end(t, 0);
219
220 return 0;
221 }
222
223 static int _CreateChildProcess(
224 int* fd,
225 pid_t* child,
226 const char* user,
227 const char* password)
228 mike 1.1 {
229 int s[2];
230 int fdLimit;
231
232 /* create communication pipe */
233 if(0 != socketpair(AF_UNIX, SOCK_STREAM, 0, s))
234 {
|
235 krisbash 1.3 trace_SocketPair_Failed();
|
236 mike 1.1 return -1;
237 }
238
239 *child = fork();
240
241 if (*child < 0)
242 {
243 close(s[0]);
244 close(s[1]);
245 return -1; /* Failed */
246 }
247
248 if (*child > 0)
249 {
250 close(s[1]);
251 *fd = s[0];
252
253 return 0; /* Started */
254 }
255
256 /* We are in child process here */
257 mike 1.1
258 /* Close all open file descriptors except provided socket
259 (Some systems have UNLIMITED of 2^64; limit to something reasonable) */
260
261 fdLimit = getdtablesize();
262 if (fdLimit > 2500 || fdLimit < 0)
263 {
264 fdLimit = 2500;
265 }
266
267 /* ATTN: close first 3 also! Left for debugging only */
268 {
269 int i;
270 for (i = 3; i < fdLimit; ++i)
271 {
272 if (i != s[1])
273 close(i);
274 }
275 }
276
277 /* perform operation in quesiton */
278 mike 1.1 {
279 int r = _PamCheckUser(user, password);
280
281 write(s[1], &r, sizeof(r));
282 close(s[1]);
283 }
284
285 _exit(0);
286 }
287
|
288 krisbash 1.3 /*
289 Tests whether last error is EINTR
290 */
291 MI_INLINE int _TestEINTR()
292 {
293 #if defined(CONFIG_OS_WINDOWS)
294 return 0;
295 #else
296 return errno == EINTR;
297 #endif
298 }
299
300 /*
301 Reads data from file, retry if was interuptted
302 Returns:
303 'size of the data' readed from the file if success
304 '-1' otherwise
305 */
306 int ReadFile(int fd, void* data, size_t size)
307 {
308 int r;
309 krisbash 1.3 while ((r = read(fd, data, size)) == -1)
310 {
311 if (!_TestEINTR()) break;
312 }
313 return r;
314 }
315
|
316 mike 1.1 /*
317 Validates user name and password;
318 Returns:
319 '0' if user account is valid and authorized to use CIM server
320 '-1' otherwise
321 */
322 int AuthenticateUser(const char* user, const char* password)
323 {
324 int fd = 0;
325 pid_t child = 0;
326 int r = -1;
327
328 if (s_ignoreAuthCalls)
329 return 0;
330
331 /* Verify if user is in cache already */
332 if (0 == CredCache_CheckUser(user, password))
333 return 0;
334
335 if (0 != _CreateChildProcess(&fd, &child, user, password))
336 {
337 mike 1.1 return -1;
338 }
339
|
340 krisbash 1.3 if (sizeof(int) != ReadFile(fd, &r, sizeof(int)))
|
341 mike 1.1 {
|
342 krisbash 1.3 trace_UserAuth_FailedToRead( errno );
|
343 mike 1.1 r = -1;
344 goto done;
345 }
346
347 done:
348 close(fd);
349 /* SIGCHILD HANDLER will take care of pid waiting */
350 #if 0
351 {
352 int loc = 0;
353 pid_t p = waitpid(child, &loc, 0 /*WNOHANG*/);
354
355 if (p != child)
356 LOGW_CHAR(("waitpid returned %d, loc %d",(int)p, loc));
357 }
358 #endif
359
360 /* Add user to cache if auth was ok */
361 if (0 == r)
362 CredCache_PutUser(user, password);
363
364 mike 1.1 return r;
365 }
366
367 /*
368 Validates user's account for correct account name, expiration etc.
369 Returns:
370 '0' if user account is valid and authorized to use CIM server
371 '-1' otherwise
372 */
373 int ValidateUser(const char* user)
374 {
375 if (s_ignoreAuthCalls)
376 return 0;
377
378 MI_UNUSED(user);
379 return 0;
380 }
381
382 /*
383 Disables authentication calls so 'AuthUser' always retunrs 'ok';
384 used for unit-test only
385 mike 1.1 */
386 void IgnoreAuthCalls(int flag)
387 {
388 s_ignoreAuthCalls = flag;
389 }
390
|
391 krisbash 1.3 /*
392 Get if authentication calls was ignored or not
393 Return value:
394 1 - ignored; 0 - not
395 */
396 int IsAuthCallsIgnored()
397 {
398 return s_ignoreAuthCalls;
399 }
|
400 mike 1.1
401 /*
402 Looks for user's account and retrieves uid/gid.
403 Parameters:
404 user - user name
405 uid [out] user ID
406 gid [out] group ID
407
408 Returns:
409 0 if operation was successful; -1 otherwise
410 */
411 int LookupUser(const char* user, uid_t* uid, gid_t* gid)
412 {
413 char buf[1024];
414 struct passwd pwd;
415 struct passwd* ppwd = 0;
416
417 if (s_ignoreAuthCalls)
418 return 0;
419
420 if (0 != getpwnam_r(user, &pwd, buf, sizeof(buf), &ppwd) ||
421 mike 1.1 !ppwd)
422 {
|
423 krisbash 1.3 trace_getpwnamr_Failed(errno);
|
424 mike 1.1 return -1;
425 }
426
427 *uid = pwd.pw_uid;
428 *gid = pwd.pw_gid;
429 return 0;
430 }
431
432 int GetUserGidByUid(uid_t uid, gid_t* gid)
433 {
434 /* user name */
435 char name[USERNAME_SIZE];
436
437 if (0 != GetUserName(uid, name))
438 return -1;
439
440 return LookupUser(name, &uid, gid);
441 }
442
443 /*
444 Changes user/group IDs of current process.
445 mike 1.1 Parameters:
446 uid - user ID
447 gid - group ID
448
449 Returns:
450 0 if operation was successful; -1 otherwise
451 */
452 int SetUser(uid_t uid, gid_t gid)
453 {
454 if (s_ignoreAuthCalls)
455 return 0;
456
457 if (setgid(gid) != 0)
458 {
|
459 krisbash 1.3 trace_setgid_Failed(errno);
|
460 mike 1.1 return -1;
461 }
462
|
463 krisbash 1.3 /*
464 * Limit the groups that this user is in to the ones in /etc/groups.
465 * Without this it includes the root group if root called Setuser()
466 */
467 {
468 struct passwd pwbuf;
469 char buf[1024];
470 struct passwd* pw;
471
472 if (getpwuid_r(uid, &pwbuf, buf, sizeof(buf), &pw) != 0 || !pw)
473 {
474 trace_getpwuidr_Failed(errno);
475 return -1;
476 }
477
478 if (initgroups(pw->pw_name, gid) != 0)
479 {
480 trace_initgroups_Failed(errno);
481 return -1;
482 }
483 }
484 krisbash 1.3
|
485 mike 1.1 if (setuid(uid) != 0)
486 {
|
487 krisbash 1.3 trace_setuid_Failed(errno);
|
488 mike 1.1 return -1;
489 }
490
491 return 0;
492 }
493
494 /*
495 Verifies if current process is running as root
496 Returns:
497 0 - current process is root
498 -1 - current process is not root.
499 */
500 int IsRoot()
501 {
502 uid_t uid = geteuid();
503
504 return uid == 0 ? 0 : -1;
505 }
506
507 int GetUIDByConnection(int fd, uid_t* uid, gid_t* gid)
508 {
|
509 krisbash 1.3 #if defined(CONFIG_OS_LINUX) || defined(CONFIG_OS_BSD)
|
510 mike 1.1 struct ucred credentials;
511 socklen_t ucred_size = (socklen_t)sizeof(credentials);
512
513 /*fill in the user data structure */
514 if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_size))
515 return -1;
516
517 /* the process ID of the process on the other side of the socket */
518 // credentials.pid;
519
520 /* the effective UID of the process on the other side of the socket */
521 *uid = credentials.uid;
522
523 /* the effective primary GID of the process on the other side of the socket */
524 *gid = credentials.gid;
525
526 return 0;
527 #else
528
529 MI_UNUSED(fd);
530 *uid = -1;
531 mike 1.1 *gid = -1;
532 return -1;
533 #endif
534 }
535
536 /*
537 Creates file with random data owned by user and RO by user only
538 Parameters:
539 uid - user ID
540 size - number of bytes to write
541 path - [out] - resulting file name
542
543 Returns:
544 0 if operation was successful; -1 otherwise
545 */
|
546 krisbash 1.3 int CreateAuthFile(uid_t uid, char* content, size_t size, char path[PAL_MAX_PATH_SIZE])
|
547 mike 1.1 {
548 static MI_Uint32 counter;
549
550 counter ++;
551
552 /* Format file name as <dir>/<user>.<counter> */
553 {
554 /* user name */
555 char name[USERNAME_SIZE];
556 char numBuf[12];
557 size_t dummy;
558
559 if (0 != GetUserName(uid, name))
560 return -1;
561
|
562 krisbash 1.3 if (Strlcpy(path, OMI_GetPath(ID_AUTHDIR), PAL_MAX_PATH_SIZE) >= PAL_MAX_PATH_SIZE)
|
563 mike 1.1 return -1;
564
|
565 krisbash 1.3 if (Strlcat(path, "/", PAL_MAX_PATH_SIZE) >= PAL_MAX_PATH_SIZE)
|
566 mike 1.1 return -1;
567
|
568 krisbash 1.3 if (Strlcat(path, name, PAL_MAX_PATH_SIZE) >= PAL_MAX_PATH_SIZE)
|
569 mike 1.1 return -1;
570
|
571 krisbash 1.3 if (Strlcat(path, ".", PAL_MAX_PATH_SIZE) >= PAL_MAX_PATH_SIZE)
|
572 mike 1.1 return -1;
573
|
574 krisbash 1.3 if (Strlcat(path, Uint32ToStr(numBuf, counter, &dummy), PAL_MAX_PATH_SIZE) >= PAL_MAX_PATH_SIZE)
|
575 mike 1.1 return -1;
576 }
577
578 /* Generate random */
579 if (0 != CredCache_GenerateRandom(content, size))
580 return -1;
581
582 /* write file */
583 unlink(path);
584
585 {
586 int fd;
587
588 /* Create auth file */
589 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR);
590 if (fd == -1)
591 {
|
592 krisbash 1.3 trace_CreateAuthFile_Failed(scs(path));
|
593 mike 1.1 return -1;
594 }
595 if (write(fd, content, size) != (int)size)
596 {
597 close(fd);
598 unlink(path);
599 return -1;
600 }
601
602 if (0 != fchown(fd, uid, -1))
603 {
|
604 krisbash 1.3 trace_ChownAuthFile_Failed(scs(path));
|
605 mike 1.1 close(fd);
606 unlink(path);
607 return -1;
608 }
609
610 close(fd);
611 }
612
613 return 0;
614 }
615
|
616 krisbash 1.3 /*
617 Gets username by uid
618 */
619 int GetUserName(
|
620 mike 1.1 uid_t uid,
621 char name[USERNAME_SIZE])
622 {
623 struct passwd pwbuf;
624 char buf[1024];
625 struct passwd* pw;
626
627 if (getpwuid_r(uid, &pwbuf, buf, sizeof(buf), &pw) != 0)
628 return -1;
629
630 if (!pw)
631 return -1;
632
633 if (Strlcpy(name, pw->pw_name, USERNAME_SIZE) >= USERNAME_SIZE)
634 return -1;
635
636 return 0;
637 }
638
639 static int GetGroupName(
640 gid_t gid,
641 mike 1.1 char name[GROUPNAME_SIZE])
642 {
643 struct group grbuf;
644 char buf[1024];
645 struct group* gr;
646
647 if (getgrgid_r(gid, &grbuf, buf, sizeof(buf), &gr) != 0)
648 return -1;
649
650 if (!gr)
651 return -1;
652
653 if (Strlcpy(name, gr->gr_name, GROUPNAME_SIZE) >= GROUPNAME_SIZE)
654 return -1;
655
656 return 0;
657 }
658
659 int FormatLogFileName(
660 uid_t uid,
661 gid_t gid,
|
662 krisbash 1.3 char path[PAL_MAX_PATH_SIZE])
|
663 mike 1.1 {
664 char user[USERNAME_SIZE];
665 char group[GROUPNAME_SIZE];
666
|
667 krisbash 1.3 if (Strlcpy(path, OMI_GetPath(ID_LOGDIR), PAL_MAX_PATH_SIZE) >= PAL_MAX_PATH_SIZE)
|
668 mike 1.1 return -1;
669
|
670 krisbash 1.3 Strlcat(path, "/omiagent.", PAL_MAX_PATH_SIZE);
|
671 mike 1.1
672 /* Append user name */
673 if (GetUserName(getuid(), user) == 0)
674 {
|
675 krisbash 1.3 Strlcat(path, user, PAL_MAX_PATH_SIZE);
|
676 mike 1.1 }
677 else
678 {
679 char buf[11];
680 sprintf(buf, "%u", getuid());
|
681 krisbash 1.3 Strlcat(path, buf, PAL_MAX_PATH_SIZE);
|
682 mike 1.1 }
683
684 /* Append group name */
685 if (GetGroupName(getgid(), group) == 0)
686 {
|
687 krisbash 1.3 Strlcat(path, ".", PAL_MAX_PATH_SIZE);
688 Strlcat(path, group, PAL_MAX_PATH_SIZE);
|
689 mike 1.1 }
690 else
691 {
692 char buf[11];
693 sprintf(buf, "%u", getgid());
|
694 krisbash 1.3 Strlcat(path, ".", PAL_MAX_PATH_SIZE);
695 Strlcat(path, buf, PAL_MAX_PATH_SIZE);
|
696 mike 1.1 }
697
|
698 krisbash 1.3 Strlcat(path, ".log", PAL_MAX_PATH_SIZE);
|
699 mike 1.1
700 return 0;
701 }
702
703 #endif
704
|